Creating a GraphQL server with Javascript

Rotimi Babalola
Backticks & Tildes
Published in
11 min readSep 11, 2017
Spinning up a GraphQL server — Get it?

Hello! Welcome to the second article in my series on GraphQL. If you haven’t read the first part, here’s the link. In the first part of this article, I gave a high level intro to GraphQL, its features and query syntax.

In this article, I will show you how to spin up a simple GraphQL server for a document management application. The server will allow you to do the following:

  1. Create a user.
  2. Create a document for a user.
  3. Retrieve users and the documents associated with them.

When we’re through, you should be able to do this from scratch in about 10 minutes 😱. I will also show you how to create queries, mutations and resolvers for the server. Ready? Let’s get started! 😎

I’ve created a repo for this tutorial. You can go ahead and clone the repo and install dependencies.

git clone https://github.com/andela-rbabalola/dms-graphql.git
cd dms-graphql
git checkout start
npm install

First, we will create a server with Express, use express-graphql as the route handler for the /graphql HTTP endpoint and connect to a mongoDB database. Let’s see how to do that. Create a file called server.js in the root directory and paste the following code

server.js

I have already defined the User and Document collections so we can focus on setting up the GraphQL server.

On lines 16–19 we are mounting express-graphql as the route handler for the /graphql HTTP endpoint and we set schema to dmsSchema which is a GraphQL schema we will create in a moment. We are also setting graphiql to true; this allows us make GraphQL queries via a graphical interactive in-browser IDE called GraphiQL.

Now let’s create the GraphQL schema for this tutorial.

schema/index.js

This is a lot! Let me explain what we’ve just done. First we imported some GraphQL data types from the graphql package. Next we defined the root query for the GraphQL API. The root query is the entry point we will use for making all our queries. The root query has 2 fields — User and Document.

The User shows us all the information we can access for any user. Its type is UserType which is a custom GraphQL type we will create in a moment. Note that it is wrapped in GraphQLList which means we’re expecting an array (or list) of users from the server. We can define UserType inside the Root Query, but for clarity it’s always good to define custom GraphQL types in a different file, then import them. The User field also has an optional argument called id of type GraphQLString. We will use this id when we want to get details for a particular user.

Something you may have noticed by now is that GraphQL is strongly typed, this means that any field or variable you add to the schema must have a type e.g. string, integer, boolean or a custom GraphQL type.

schema/types/user.js

Now we can see all the fields available to query for User ; note that each field has a description that tells us more about that field. One of the cool things about GraphQL is that it allows to document our API (with the description field) as we code which is very convenient. 😎

Also note that we have wrapped some fields (firstName, lastName, email and isAdmin) in GraphQLNonNull , this means that these fields cannot be null in the response if we include them in our query.

Now let’s import the UserType we created in the GraphQL schema. At the top of index.js add the following

// schema/index.jsimport UserType from './types/user';

Resolvers

The next thing we need to explain is the resolve in the User field. It is commonly referred to as a resolver . The resolver is a function that gets called when you make queries. For example, when we query for the emails of all users, we are actually executing a function (the resolver) that retrieves users’ emails from the database.

The resolver takes 3 arguments but in this tutorial I will only explain the first two. They are:

  1. obj — This object refers to the parent object we are representing. For the User field it will be aUser object; for the Document field it will be aDocument object.
  2. args — This object contains the arguments to the resolver function.

Let’s add functionality to the resolver on the User field. The resolver is going to get data for all users if no ID is passed to it as an argument or it will get data for a user by the ID passed to it. Note that this { id } means we are using Javascript destructuring to destructure the args object so we can directly access the id parameter for a specific user if an ID is passed.

Create a folder called resolvers in the root directory and in it create a file user-resolver.js and paste the following code.

resolvers/user-resolver.js

The user resolver has 2 functions one to get a particular user by the id supplied and another to get all users. These functions return a Promise because loading data from a database is asynchronous. When the Promise resolves we get all the users (or a user) from the database.

Next import the file in schema/index.js

// schema/index.jsimport UserResolver from '../resolvers/user-resolver';

Let’s update the resolve function of the User field with the following:

// schema/index.js
import UserResolver from ‘../resolvers/user-resolver’;

const RootQueryType = new GraphQLObjectType({
...,
resolve: (obj, { id }) => {
let userData;
if (id) {
// get user by id
userData = UserResolver.getById(id);
} else {
// get all users
userData = UserResolver.get();
}
...
});

And we are done with the User field! Next we need to do the same thing for the Document field. In the types folder create a file called document.js and paste the following code:

schema/types/document.js

DocumentType depends on another GraphQL custom type called AccessType . In the same types folder, create a file access.js and paste the code below:

schema/types/access.js

Next, import it in schema/types/document.js

// schema/types/document.jsimport AccessType from './access';

Now that you’ve created DocumentType , import it in schema/index.js

// schema/index.jsimport DocumentType from './types/document';

Finally, we need to create resolvers for the DocumentType . In the resolvers folder, create a file document-resolver.js and paste the following code

resolvers/document-resolver.js

Right now, the document resolver contains pretty much the same thing as the user resolver; later on in this tutorial, we will add more functions to it.

Next, import the file and update the resolve function of the Document field with the following:

// schema/index.js
import DocumentResolver from '../resolvers/document-resolver';

const RootQueryType = new GraphQLObjectType({
...,
resolve: (obj, { id }) => {
let docData;
if (id) {
// get document by id
docData = DocumentResolver.getById(id);
} else {
// get all documents
docData = DocumentResolver.get();
}
...
});

And we are done! Let us test what we have done so far

npm start

If all went well, you should see the following

GraphQL Server listening on PORT 3000
dms db opened

Navigate to this url in your browser localhost:3000/graphql and you should see the graphiql interface. It looks like this

The graphiql interface

Let write a test query that will get the first names of all users in the database. This query will return an empty array because we currently don’t have any users in the database. We are doing this just to make sure everything works fine.

query {
User {
firstName
}
}

As expected this is the result

Writing mutations

We need to figure out a way to allow the GraphQL server we just created to support not only read operations but also write operations. This is where mutations come in 🎉. Let’s see how to write a mutation for our GraphQL server.

First, we need to define a RootMutationType just like we had a RootQueryType for queries. The RootMutationType will have 2 fields — AddUser to create a user and AddDocument to create a new document. Let’s go ahead and do that. Paste this code in index.js , below RootQueryType .

// schema/index.jsconst RootMutationType = new GraphQLObjectType({
name: 'RootMutationType',
fields: () => ({
AddUser: AddUserMutation,
AddDocument: AddDocumentMutation,
}),
});

We also need to add the RootMutationType to the GraphQL schema

// schema/index.jsconst dmsSchema = new GraphQLSchema({
query: RootQueryType,
mutation: RootMutationType,
});

Next, we need to define AddUserMutation . In the schema folder, create a folder called mutations and in it a file called add-user.js , paste the code below:

schema/mutations/add-user.js

Let me explain what we’ve just done. First, we imported some GraphQL data-types. Next we created UserInputType which is an instance of GraphQLInputObjectType . The fields in UserInputType are what we are going to supply when we are making this mutation. Again, some fields are wrapped in GraphQLNonNull meaning that when we are making this mutation, these fields must be supplied for the new user we want to create.

Another thing I want you to notice is the object we are exporting in this file. The first field, type , refers to what type the server will return when the mutation is done. In GraphQL, mutations always return something. In this case we want the AddUserMutation to return a UserType when it is done. Note that we can return anything here — a simple User created! message would suffice. I’m choosing to return UserType because we can query the server for the new user we just created to be sure everything worked fine.

args is an object containing the arguments that the mutation accepts. Note that it can’t be null. When we want to add a new user we are going to pass the new user object as an argument to AddUserMutation .

Finally, we have resolve , the function that executes when we perform this mutation. We need to define it in user-resolver and then import it. Let’s do that.

// resolvers/user-resolver.js
import User from '../models/user.model';
const UserResolver = {
getById: ...,
get: ...,
create: (user) => {
const newUser = new User(user);
return new Promise((resolve, reject) => {
newUser.save((error, createdUser) => {
if (error) reject(error);
else resolve(createdUser);
});
});
},
}

The function basically creates a new entry in the User collection and returns a Promise that resolves to the newly created user.

Great! We now have the resolver for AddUserMutation defined. Let’s import it in add-user.js. Paste the following in add-user.js

// schema/mutations/add-user.jsimport UserResolver from '../../resolvers/user-resolver';

Update the resolve with this:

// schema/mutations/add-user.jsexport default {
type: ...,
args: ...,
resolve(obj, { input }) {
// create user
return UserResolver.create(input);
},

};

Finally, import AddUserMutation in schema/index.js and we are done with AddUserMutation!

// schema/index.jsimport AddUserMutation from './mutations/add-user';

Next need to do the same thing for AddDocumentMutation. First, we define it in a file add-document.js in schema/mutations.

schema/mutations/add-document.js

Next, we write the resolver that will create a new document in document-resolver.js

// resolvers/document-resolver.js
import Document from '../models/document.model';
const DocumentResolver = {
getById: ...,
get: ...,
create: (document) => {
const newDocument = new Document(document);
return new Promise((resolve, reject) => {
newDocument.save((error, createdDocument) => {
if (error) reject(error);
else resolve(createdDocument);
});
});
},
};

Import it in add-document.js

// schema/mutations/add-document.jsimport DocumentResolver from '../../resolvers/document-resolver';

Update the resolve with this:

// schema/mutations/add-document.jsexport default {
type: ...,
args: ...,
resolve(obj, { input }) {
// create document
return DocumentResolver.create(input);
},
};

Finally, import AddDocumentMutation in schema/index.js and we are done!

// schema/index.jsimport AddDocumentMutation from './mutations/add-document';

Now we can test the mutation 🕺

npm start

Navigate to localhost:3000/graphql in your browser and run the following mutation

mutation {
AddUser(input: {
firstName: "John",
lastName: "Doe",
password: "johndoe123",
email: "john@email.com",
}) {
firstName
lastName
}
}

We are writing a mutation to create a new user and querying the server for the firstName and lastName of the new user when the mutation is done. Here is the result

Running the AddUser mutation

Remember the first query we wrote in this article? Let’s run it now

query {
User {
firstName
}
}

Here is the result

Running a query

We also need to test AddDocumentMutation. Run this mutation to create a new document.

mutation {
AddDocument(input: {
title: "some title",
text: "some text",
owner: "599566738248d99115e6d8bb"
}){
title
}
}

If you are wondering how I got the string for owner you can get it by adding id to the first query. The string should be different for you because MongoDB generates a random id for every document in a collection.

Here is the result

Running the AddDocument mutation

Voila! Our GraphQL server can support queries and mutations! Let’s celebrate!

One last resolver

For us to see the awesomeness of GraphQL, we need to add one more resolver to the UserType. This resolver will get the documents owned by a user. Let’s do this!

First, we need to define the resolver function in document-resolver.js. Paste the code below in the file

// resolvers/document-resolver.js
import Document from '../models/document.model';
const DocumentResolver = {
getById: ...,
get: ...,
create: ...,
getDocumentsForUser: userId => new Promise((resolve, reject) => {
Document.find({ owner: userId }, (error, foundDocuments) => {
if (error) reject(error);
else resolve(foundDocuments);
});
}),
};

Next, import it in schema/types/user.js

// schema/types/user.jsimport DocumentResolver from '../../resolvers/document-resolver';

We also need to import DocumentType in user.js because we are now returning documents in UserType.

// schema/types/user.jsimport DocumentType from './document';

Finally, add a new field called documents to UserType

// schema/types/user.jsexport default new GraphQLObjectType({
name: 'UserType'
fields: () => ({
...,
documents: {
type: new GraphQLList(DocumentType),
description: 'Documents owned by user',
resolve: obj => DocumentResolver.getDocumentsForUser(obj._id),
},
}),
});

Note that we are using the obj argument in the resolve function. This is because obj is a reference to the parent object — User which will contain the user’s id. Also we wrapped DocumentType in GraphQLList because we are expecting an array (or list) of documents for each user.

Let’s test! Run the query below in the graphiql interface

query {
User {
firstName
lastName
documents {
title
text
}
}
}

We are querying the server for all users and the title and text of the documents associated with them. Here is the result of the query:

Querying for users and their documents

If we want to get details for just one user, pass the id as an argument to the query like this:

query {
User(id: "599566738248d99115e6d8bb") {
firstName
lastName
documents {
title
text
}
}
}

Here is the result

Querying for a particular user

Awesome! I think this deserves another celebration

As an exercise for you to try out, can you add a field to UserType that will return the count of the documents owned by a user? I know you can do it 😎 You can check my solution on the article-2 branch of this project’s GitHub repo.

Summary

We’ve done a lot in this article! 💪 Thanks for sticking with me to the end and I hope you’ve learnt something from this article.

If you have questions, comments or feedback feel free to write them in the comments section. Also if you encountered any errors while setting up let me know in the comments section. You can also hit me up with ideas for the next article in this series.

Thank you very much for reading this, click the 👏 below if you enjoyed reading it so others can read it too.

Gracias!

--

--