Hands-On Full-Stack Web Development with GraphQL and React
上QQ阅读APP看书,第一时间看更新

Writing your first GraphQL mutation

One thing our client already offered was to add new posts to the fake data temporarily. We can realize this in the back end by using GraphQL mutations.

Starting with the schema, we need to add the mutation as well as the input types as follows:

input PostInput {
text: String!
}

input UserInput {
username: String!
avatar: String!
}

type RootMutation {
addPost (
post: PostInput!
user: UserInput!
): Post
}

GraphQL inputs are not more than types. Mutations can use them as parameters inside requests. They may look weird, because our current output types look almost the same. However, it would be wrong to have an id property on PostInput, for example, since the back end chooses the id and the client cannot give it. Consequently, it does make sense to have separate objects for input and output types.

The addPost function receiving our two new required input types—PostInput and UserInput, is a new feature here. Those functions are called mutations, since they mutate the current state of the application. The response to this mutation is an ordinary Post object. When creating a new post with the addPost mutation, we will directly get the created post from the back end in response.

The exclamation mark in the schema tells GraphQL that the field is a required parameter.

The RootMutation type corresponds to the RootQuery type and is an object that holds all of our GraphQL mutations.

The last step is to enable the mutations in our schema for the Apollo Server:

schema {
query: RootQuery
mutation: RootMutation
}
Usually, the client does not send the user with the mutation. This is because the user is authenticated first, before adding a post, and through that, we already know which user initiated the Apollo request. However, we can ignore this for the moment and implement authentication later in Chapter 6, Authentication with Apollo and React.

The addPost resolver function needs to be implemented now in the resolvers.js file.

Add the following RootMutation object to the RootQuery in resolvers.js:

RootMutation: {
addPost(root, { post, user }, context) {
const postObject = {
...post,
user,
id: posts.length + 1,
};
posts.push(postObject);
return postObject;
},
},

This resolver extracts the post and user objects from the mutation's parameters, which are passed in the second argument of the function. Then, we build the postObject variable. We want to add our posts array as property by destructuring the post input and adding the user object. The id field is just the length of the posts array plus one.

The postObject variable looks like a post from the posts array now. Our implementation does the same as the front end is already doing. The return value of our addPost function is the postObject. To get this working, you need to change the initialization of the posts array from const to let. Otherwise, the array will be static and unchangeable.

You can run this mutation via your preferred HTTP client like this:

{
"operationName": null,
"query": "mutation addPost($post : PostInput!, $user: UserInput!) {
addPost(post : $post, user: $user) {
id
text
user {
username
avatar
}
}
}",
"variables": {
"post": {
"text": "You just added a post."
},
"user": {
"avatar": "/uploads/avatar3.png",
"username": "Fake User"
}
}
}

Here, we are using the variables property to send the data we want to insert in our back end. We need to pass them as parameters within the query string. We define both parameters with a dollar sign and the awaited data type inside the operation string. Those variables marked with a dollar sign can then be mapped into the actual action we want to trigger on the back end. Again, we need to send a selection of fields our response should have.

The result will have a data object including an addPost field. The addPost field holds the post, which we send with our request.

Query the posts again, and you will see that there are now three posts. Great, it worked!

As with our client, this is only temporary until we restart the server. We'll cover how to persist data in a SQL database in Chapter 3, Connecting to the Database.

Next, we'll cover the various ways to debug your back end properly.