I am new to GrapQL. I am trying to use it with spring boot. I can make query successfully, it is returning the data that I need, but i want now to use mutation. I need to add a use to database when he registers.
This is my schema.graphqls file:
type Token {
token: String
}
type Register {
message: String
}
type User {
username: String!
firstName: String!
lastName: String!
password: String!
role: String!
}
type Query {
login(username: String, password: String): Token
}
type Mutation {
register(input: RegisterUserInput!): Register
}
input RegisterUserInput {
username: String!
firstName: String!
lastName: String!
password: String!
role: String!
}
schema {
query: Query
mutation: Mutation
}
So as you can see register is in Mutation type, which is added in schema as is Query. But for some reason it looks like it is not going into Mutation, it is only trying to find the types in Query.
This is my controller:
@Autowired
private UserService userService;
/**
* Login the user and return generated token
* @param query
* @return String token
*/
@PostMapping("/login")
public ResponseEntity<Object> login(@RequestBody String query){
ExecutionResult executionResult = userService.getGraphQL().execute(query);
// Check if there are errors
if(!executionResult.getErrors().isEmpty()){
return new ResponseEntity<>(executionResult.getErrors().get(0).getMessage(), HttpStatus.UNAUTHORIZED);
}
return new ResponseEntity<>(executionResult, HttpStatus.OK);
}
/**
* Create new user and save him to database
* @param mutation
* @return String message
*/
@PostMapping("/register")
public ResponseEntity<Object> register(@RequestBody String mutation){
ExecutionResult executionResult = userService.getGraphQL().execute(mutation);
// Check if there are errors
if(!executionResult.getErrors().isEmpty()){
return new ResponseEntity<>(executionResult.getErrors().get(0).getMessage(), HttpStatus.UNAUTHORIZED);
}
return new ResponseEntity<>(executionResult, HttpStatus.OK);
}
As I said, login is working fine, but register is return the error I mentioned in the title.
My service class:
@Value("classpath:graphql-schema/schema.graphqls")
Resource resource;
private GraphQL graphQL;
@Autowired
private LoginDataFetcher loginDataFetcher;
@Autowired
private RegisterDataFetcher registerDataFetcher;
@PostConstruct
public void loadSchema() throws IOException{
// Get the schema
File schemaFile = resource.getFile();
// Parse schema
TypeDefinitionRegistry typeDefinitionRegistry = new SchemaParser().parse(schemaFile);
RuntimeWiring runtimeWiring = buildRuntimeWiring();
GraphQLSchema graphQLSchema = new SchemaGenerator().makeExecutableSchema(typeDefinitionRegistry, runtimeWiring);
graphQL = GraphQL.newGraphQL(graphQLSchema).build();
}
private RuntimeWiring buildRuntimeWiring() {
return RuntimeWiring.newRuntimeWiring()
.type("Query", typeWiring ->
typeWiring
.dataFetcher("login", loginDataFetcher))
.type("Mutation", typeWiring ->
typeWiring
.dataFetcher("register", registerDataFetcher))
.build();
}
public GraphQL getGraphQL() {
return graphQL;
}
My LoginDataFetcher:
@Autowired
private AppUserRepository appUserRepository;
private JwtGenerator jwtGenerator;
public LoginDataFetcher(JwtGenerator jwtGenerator) {
this.jwtGenerator = jwtGenerator;
}
@Override
public TokenDAO get(DataFetchingEnvironment dataFetchingEnvironment) {
String username = dataFetchingEnvironment.getArgument("username");
String password = dataFetchingEnvironment.getArgument("password");
AppUser appUser = appUserRepository.findByUsername(username);
// If user is not foung
if(appUser == null){
throw new RuntimeException("Username does not exist");
}
// If the user is fount check passwords
if(!appUser.getPassword().equals(password)){
throw new RuntimeException("Incorrect password");
}
// Generate the token
String token = jwtGenerator.generate(appUser);
return new TokenDAO(token);
}
The RegisterDataFetcher:
@Autowired
private AppUserRepository appUserRepository;
@Override
public RegisterDAO get(DataFetchingEnvironment dataFetchingEnvironment) {
String username = dataFetchingEnvironment.getArgument("username");
String firstName = dataFetchingEnvironment.getArgument("firstName");
String lastName = dataFetchingEnvironment.getArgument("lastName");
String password = dataFetchingEnvironment.getArgument("password");
String role = dataFetchingEnvironment.getArgument("role");
AppUser appUser = appUserRepository.findByUsername(username);
// Check if username exists
if(appUser != null){
throw new RuntimeException("Username already taken");
}
AppUser newAppUser = new AppUser(username, password, role, firstName, lastName);
// Save new user
appUserRepository.save(newAppUser);
return new RegisterDAO("You have successfully registered");
}
The error that I am getting in the console:
graphql.GraphQL : Query failed to validate : '{
register(username: "user", firstName: "Bla", lastName: "Blabla", password: "password", role: "DEVELOPER") {
message
}
}'
Thank you for your help.
I changed my schema file like this, based on the answer I got:
query UserQuery{
login(username: String, password: String){
token
}
}
mutation UserMutation{
register(input: RegisterUserInput) {
message
}
}
input RegisterUserInput {
username: String!
firstName: String!
lastName: String!
password: String!
role: String!
}
schema {
query: UserQuery
mutation: UserMutation
}
But now I am getting this error:
The operation type 'UserQuery' is not present when resolving type 'query' The operation type 'UserMutation' is not present when resolving type 'mutation'
So what is now the problem? How can I make this work?
You are telling GraphQL that you are requesting a Query, while in fact register
is a Mutation. When composing a GraphQL request, the syntax typically follows this format for Queries:
query someOperationName {
login {
# other fields
}
}
When writing a mutation, you simply specify that instead:
mutation someOperationName {
register {
# other fields
}
}
You can leave the operation name out, although it's good practice to include it. You may see examples in this format:
{
someQuery {
# other fields
}
}
In this case both the operation name and the operation type (query vs mutation) were left off. This is still a valid request, since GraphQL simply assumes you meant query
when you leave off the operation type. From the spec:
If a document contains only one operation, that operation may be unnamed or represented in the shorthand form, which omits both the query keyword and operation name.
So in your request, GraphQL is assuming register
is a query, while in fact it's a mutation, and it's returning an error as a result.
Again, when writing requests, it's good practice to always include both the operation name and query/mutation keyword.