In the last chapters you’ve learnt how to use GraphQL to read data. Time to add some.
When you want to add data, you use almost the same syntax. How does the server know when you want to write data instead of reading? You have to use the mutation
keyword instead of query
. That’s all. Actually not all, but you will learn about the differences in this chapter.
Let’s start with the mutation which adds a new user to the database, like in the howtographql.com common schema
mutation {
createUser(name: String!, authProvider: AuthProviderSignupData!): User
}
input AuthProviderSignupData {
email: AUTH_PROVIDER_EMAIL
}
input AUTH_PROVIDER_EMAIL {
email: String!
password: String!
}
It isn’t hard to imagine what this mutation does. The name suggests it matches our interest - it creates an user,
takes two parameters of type String
and AuthProviderSignupData
and returns an User
in response.
But wait… until now we’ve been using type
not input
. So what is this? input
is a type that can be used as a parameter.
You will frequently see it among mutation
s.
Let’s try to implement mutation in the following order:
InputObjectType
is to input
what ObjectType
is to the type
keyword.
It tells Sangria how to understand data. In fact you can define ObjectType
and InputObjectType
for the same case class, or even more than one. A good example is a User
entity which consists of many fields. But if you need different data when you register a new user and during sign in, you can create different InputObjectType
’s.
To avoid circular dependencies of types, like we’ve experiences in the last chapter ther a suggestion to use lazy
keyword for every type.
But in case above, AuthProviderEmail
is nested object in AuthProviderSignupData
which is built by macro. Thats why we had to add implicit
we have to have this nested object type in the scope in the time of macro executing.
It will be similar to the process you already know.
As you can see, we’re missing one function in DAO
All mutations are optional so you have to wrap it in Some
.
If you will try to run a server, you will get errors about unimplemented FromInput
‘s.
It’s an additional step we have to do to able run those mutations.
Sangria needs to read a part of JSON-like structure and convert it to case classes.
That’s the reason why we need such FromInput
type classes.
There are two ways to do it, you can write your own mapper, but you can also use any JSON library to help with this process.
In the first step we’ve added a dependency to the sangria-spray-json
library,
but if you want you can use any other library.
Sangria uses this to convert it into proper FromInput
type.
All we need to do is to define a proper JSONReader for that case class and import some converting functions.
Everything should work as expected now.
Perform a query in graphQL console:
mutation addMe {
createUser(
name: "Mario",
authProvider:{
email:{
email:"mario@example.com",
password:"p4ssw0rd"
}
}){
id
name
}
}
Of course you can use different data :)
If everything works, we can move forward and implement two more mutations.
Implement a mutation to able run a following code:
createLink(description: String!, url: String!, postedById: ID): Link
First try on your own, next compare to my solution.
Hint! You can skip creating case classes phase because we don’t need any of them.
In this case parameters uses only String
and Int
which are simple scalars available out-of-the-box.
Also add a mutation’s definition inside the Mutation.fields
sequence.
We’re missing arguments definitions
That’s all, now you should be able to run following query:
mutation addLink {
createLink(
url: "howtographql.com",
description: "Great tutorial page",
postedById: 1
){
url
description
postedBy{
name
}
}
}
Let’s implement the last mutation for voting:
Add arguments needed by the next mutation and this mutation itself.
Add mutation definition.
We are done! You can test all those mutations in Graphiql console.
The current state of files changed in this chapter:
models/package.scala
DAO.scala
GraphQLSchema.scala
Now you know how to send data to the server. You will use this knowledge when we implement authentication and authorization logic in the next chapter.