We can create a simple GraphQL server with Express. To do this, we need the express-graphql
and graphql
packages.
In this article, we’ll look at how to create mutations and input types with Express and GraphQL.
Mutations and Input Types
To create mutations, we create a schema that has the Mutation
type rather than a Query
.
Then it’s a simple as making the API endpoint part of the top-level Mutation
type instead of a Query
type.
Both mutations and queries can be handled by root resolvers.
We can then create a GraphQL server that takes both queries and mutations as follows:
const express = require('express');
const graphqlHTTP = require('express-graphql');
const { buildSchema } = require('graphql');
const crypto = require('crypto');
const schema = buildSchema(`
input TodoInput {
text: String
}
type Todo {
id: ID!
text: String
}
type Query {
getTodo(id: ID!): Todo
}
type Mutation {
createTodo(input: TodoInput): Todo
updateTodo(id: ID!, input: TodoInput): Todo
}
`);
class Todo {
constructor(id, { text }) {
this.id = id;
this.text = text;
}
}
let todos = {};
const root = {
getTodo: ({ id }) => {
if (!todos[id]) {
throw new Error('Todo not found.');
}
return new Todo(id, todos[id]);
},
createTodo: ({ input }) => {
const id = crypto.randomBytes(10).toString('hex');
todos[id] = input;
return new Todo(id, input);
},
updateTodo: ({ id, input }) => {
if (!todos[id]) {
throw new Error('Todo not found');
}
todos[id] = input;
return new Todo(id, input);
},
};
const app = express();
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true,
}));
app.listen(3000, () => console.log('server started'));
In the code above, we defined our types by writing:
const schema = buildSchema(`
input TodoInput {
text: String
}
type Todo {
id: ID!
text: String
}
type Query {
getTodo(id: ID!): Todo
}
type Mutation {
createTodo(input: TodoInput): Todo
updateTodo(id: ID!, input: TodoInput): Todo
}
`);
We created the input type TodoInput
and the Todo
type. Then we created the Query
type with the getTodo
member so that we can get our todo items.
Then in our Mutation
, we added the createTodo
and updateTodo
members so that we can add and update todos.
Then we create our Todo
class so that we can store the todo data:
class Todo {
constructor(id, { text }) {
this.id = id;
this.text = text;
}
}
Next, we have our root resolver:
const root = {
getTodo: ({ id }) => {
if (!todos[id]) {
throw new Error('Todo not found.');
}
return new Todo(id, todos[id]);
},
createTodo: ({ input }) => {
const id = crypto.randomBytes(10).toString('hex');
todos[id] = input;
return new Todo(id, input);
},
updateTodo: ({ id, input }) => {
if (!todos[id]) {
throw new Error('Todo not found');
}
todos[id] = input;
return new Todo(id, input);
},
};
It adds the functions with the same as we specified in our schema so that we can do something when we make some queries.
In this example, getTodo
, we’ll return the todo with the given id
.The todo that’s found will be returned. Otherwise, we throw an error.
In createTodo
, we get the input
from the query and then add the todo entry to our todos
object, which is our fake database to store the todos. The todo that’s saved will be returned.
Then we have the updateTodo
function to update the todo by id
. Whatever has the given id
will be replaced with the content of input
. The todo that’s saved will be returned. We throw an error if a todo with the given id
isn’t found.
Then when we go to the /graphql
page, we can type in the following to the GraphiQL window:
mutation {
createTodo(input: {text: "eat"}) {
id
text
}
}
Then we get something like:
{
"data": {
"createTodo": {
"id": "c141d1fda69e8d9084bd",
"text": "eat"
}
}
}
as the response.
If we make a query for updating todo as follows:
mutation {
updateTodo(id: "e99ce10750c93793a23d", input: {text: "eat"}) {
id
text
}
}
We get something like:
{
"data": {
"updateTodo": {
"id": "e99ce10750c93793a23d",
"text": "eat"
}
}
}
back as the response.
If a todo isn’t found, then we get:
{
"errors": [
{
"message": "Todo not found",
"locations": [
{
"line": 9,
"column": 3
}
],
"path": [
"updateTodo"
]
}
],
"data": {
"updateTodo": null
}
}
as the response.
We can make the getTodo
query as follows:
query {
getTodo(id: "e99ce10750c93793a23d"){
id
text
}
}
Then we get:
{
"data": {
"getTodo": {
"id": "e99ce10750c93793a23d",
"text": "eat"
}
}
}
as the response.
Conclusion
We can create mutations as we do with queries.
To accept mutation operations in our GraphQL server, we make our types for storing our data, then we create our mutations by populating the Mutation
type with our members.
We can then use the buildSchema
function to build the schema we just specified.
Then in our root reducer, we make the functions that with the names that we specified in the type definitions.
Finally, we can make queries to our server to run the mutations.