Build a CMS API With GraphQL and Apollo Server

We will be building an API for a Blog CMS. The Blog will comprise of three concepts: Users, Posts and Tags. The CMS will handle creating and authenticating users (using JWT). Tags will be used as a taxonomy to group posts, think of it like categories in WordPress. A post can belong to many tags and a tag can have many posts. Authenticated users will be able to perform CRUD tasks like creating posts and tags.

This tutorial assumes you already have some basic understanding of GraphQL. You might want to go over GraphQL docs as a refresher.

With that said, let’s get started!

Create a New Project

We’ll start by creating a new Node.js project, we’ll call it graphql-blog-cms-api:

```bash

mkdir graphql-blog-cms-api && cd graphql-blog-cms-api
npm init -y

```

Once the app is created, we need to install project’s dependencies:

```bash

npm install graphql apollo-server-express express body-parser graphql-tools dotenv mysql2 sequelize bcrypt jsonwebtoken express-jwt slugify

```

We’ll go over each of package as we get to them. With the dependencies installed, let’s start fleshing out the app by creating a GraphQL server.

Create GraphQL Server

Create a new server.js file and paste the code below into it:

```js

// server.js

'use strict';
const express = require('express');

const bodyParser = require('body-parser');

const { graphqlExpress, graphiqlExpress } = require('apollo-server-express');

const schema = require('./data/schema');
const PORT = 3000;
// Create our express app
const app = express();

// Graphql endpoint
app.use('/api', bodyParser.json(), graphqlExpress({ schema }));

// Graphiql for testing the API out

app.use('/graphiql', graphiqlExpress({ endpointURL: 'api' }));

app.listen(PORT, () => {
   console.log(`GraphiQL is running on http://localhost:${PORT}/graphiql`);
});

```

We import our dependencies, express is the Node.js framework of choice for this tutorial. body-parser is used to parse incoming request body. `graphqlExpress` is the express implementation of Apollo server which will be used to power our GraphQL server. With graphiqlExpress, we will be able to use GraphiQL which is an in-browser IDE for exploring GraphQL (we’ll use this to test out the GraphQL API). Lastly, we import our GraphQL schema which we’ll created shortly.

We define a port that the server will listen on. We then create an express app.

We define the route for our GraphQL API. We add body-parser middleware to the route. We also addgraphqlExpress passing along the GraphQL schema.

Then we define the route for GraphiQL passing to it the GraphQL endpoint we created above.

Finally, we start the server and listen on the port defined above.

Define GraphQL Schema

Let’s move on to defining our GraphQL schema. Schemas describe how data are shaped and what data on the server can be queried. GraphQL schemas are strongly typed, hence all the object defined in a schema must have types. Schemas can be of two types: Query and Mutation.

Create a folder name data and within this folder, create a new `schema.js` file, then paste the code below into it:

```js

// data/schema.js
'use strict';

const { makeExecutableSchema } = require('graphql-tools');
const resolvers = require('./resolvers');

// Define our schema using the GraphQL schema language
const typeDefs = `
   scalar DateTime

   type User {
       id: Int!
       firstName: String!
       lastName: String
       email: String!
       posts: [Post]
       createdAt: DateTime! # will be generated
       updatedAt: DateTime! # will be generated
   }
   type Post {
       id: Int!
       title: String!
       slug: String!
       content: String!
       status: Boolean!
       user: User!
       tags: [Tag!]!
       createdAt: DateTime! # will be generated
       updatedAt: DateTime! # will be generated
   }

   type Tag {
       id: Int!
       name: String!
       slug: String!
       description: String
       posts: [Post]
       createdAt: DateTime! # will be generated
       updatedAt: DateTime! # will be generated
   }

   type Query {
       allUsers: [User]
       fetchUser(id: Int!): User
       allPosts: [Post]
       fetchPost(id: Int!): Post
       allTags: [Tag]
       fetchTag(id: Int!): Tag
   }

   type Mutation {
       login (
           email: String!,
           password: String!
       ): String

       createUser (
           firstName: String!,
           lastName: String,
           email: String!,
           password: String!
       ): User

       updateUser (
           id: Int!,
           firstName: String!,
           lastName: String,
           email: String!,
           password: String!
       ): User

       addPost (
           title: String!,
           content: String!,
           status: Boolean
           tags: [Int!]!
       ): Post

       updatePost (
           id: Int!,
           title: String!,
           content: String!,
           status: Boolean,
           tags: [Int!]!
       ): Post

       deletePost (id: Int!): Boolean

       addTag (
           name: String!,
           description: String

       ): Tag

       updateTag (
           id: Int!,
           name: String!,
           description: String
       ): Tag

       deleteTag (id: Int!): Boolean
   }
`;

module.exports = makeExecutableSchema({ typeDefs, resolvers });

```

We start off by pulling in `graphql-tools`, a package by the Apollo team. This package allows us to define our schema using the GraphQL schema language. We also import our resolvers which we’ll create shortly. We then begin to define the schema. We start by defining a custom scalar type called DateTime because Date is part of the types GraphQL support out of the box. So we need to define it ourselves. The DateTime will be used for the createdAt and updatedAt fields respectively. The createdAt and updatedAt fields will be auto generated at the point of creating our defined types.

We define the User type. Its fields are pretty straightforward. Notice the posts field as it will be an array of all the posts a user has created. User and Post have a one-to-many relationship, that is, a user can have many posts and on the other hand, a post can only belong to one user.

We then define the Post type. Its fields are pretty straightforward The user is a required field and represent the user that created a post. The tags field is an array of tags a post belongs to. `[Tag!]!` signifies that the array can not be empty. This means a post must belong to at least one tag. `Post` and `Tag` have a belongs-to-many relationship, that is, a post can belong to many tags and on the other hand, a tag can have many posts.

Then we define the `Tag` type. Again, its fields are pretty straightforward. The posts field is an array of posts a tag has.

Having defined our types, we move on to define the queries that can be performed on these types. `allUsers` will fetch all the users created and return them in an array. `fetchUser(id: Int!)` will fetch a user with a specified ID. We do the same for Post and Tag respectively.

Next, we define some mutations. While queries are used for fetching data from the server, mutations are used to add/modify data on the server. We define a login mutation which takes email address and password as inputs. It is use to authenticate users. We also define mutations to create and update User, Post and Tag respectively. The update mutations in addition to the data, also accept the `ID` of the type (`User`, `Post`, `Tag`) we want to update. Lastly, we define mutations for deleting a `Post` and a `Tag` respectively.

Finally, we use `makeExecutableSchema` to build the schema, passing to it our schema and the resolvers.

Setting Up Database

As earlier mentioned, we’ll be using MySQL for the purpose of this tutorial. Also, we’ll be using Sequelize as our ORM. We have installed the necessary dependencies for both of these. Now, we need to install Sequelize CLI on our computer. We’ll install it globally:

```bash

npm install –g sequelize-cli

```

Once it’s installed, we can then initialize Sequelize in our project. With the project’s root directory, run the command below:

```

sequelize init

```

This will create following folders:

  • config: contains config file, which tells CLI how to connect with database
  • models: contains all models for your project, also contains an `index.js` file which integrates all the models together.
  • migrations: contains all migration files
  • seeders: contains all seed files

Ignore the seeders folder as we won’t be creating any seeders in this tutorial. The config folder contain a JSON file `config.json`. We’ll rename this file to `config.js`. Now, open `config/config.js` and paste the snippet below into it:

```js

// config/config.js

'use strict';
require('dotenv').config();

module.exports = {
   "development": {

       "username": process.env.DB_USERNAME,

       "password": process.env.DB_PASSWORD,

       "database": process.env.DB_NAME,

       "host": process.env.DB_HOST,

       "dialect": "mysql"

   },

   "production": {

       "username": process.env.DB_USERNAME,

       "password": process.env.DB_PASSWORD,

       "database": process.env.DB_NAME,

       "host": process.env.DB_HOST,

       "dialect": "mysql"

   }

};

```

Notice we are using the `dotenv` package to read our database details from an `.env` file. Let’s create a `.env` file and paste the snippet below into it:

```env

//.env

NODE_ENV=development
DB_HOST=localhost
DB_USERNAME=root
DB_PASSWORD=
DB_NAME=graphql_blog_cms

```

Update accordingly with your own database details.

Because we have changed the config file from JSON to JavaScript file, we need to make the Sequelize CLI aware of this. We can do that by creating a `.sequelizerc` file and paste the snippet below in it

```js

// .sequelizerc

const path = require('path');

module.exports = {
 'config': path.resolve('config', 'config.js')
}

```

Now the CLI will be aware of our changes.

One last thing we need to do is update `models/index.js` to also reference `config/config.js`. Replace the line where the config file is imported with the line below:

```js

// models/index.js

var config    = require(__dirname + '/../config/config.js')[env];

```

Creating Models and Migrations

With our database setup, now create our models and their corresponding migrations. For consistency, we want our models to mirror our GraphQL schema. So we are going to create 3 models (`User`, `Post` and `Tag`) with the corresponding fields we defined on our schema. We’ll be using the Sequelize CLI for this.

We’ll start with User, run the command below:

```bash

sequelize model:generate --name User --attributes \ firstName:string,lastName:string,email:string

```

This will do following:

  • Create a model file `user.js` in models folder
  • Create a migration file with name like `XXXXXXXXXXXXXX-create-user.js` in migrations folder

Open `migrations/XXXXXXXXXXXXXX-create-user.js` and replace it content with:

```js

// migrations/XXXXXXXXXXXXXX-create-user.js


'use strict';

module.exports = {

   up: (queryInterface, Sequelize) => {

       return queryInterface.createTable('users', {

           id: {

               type: Sequelize.INTEGER,

               autoIncrement: true,

               primaryKey: true,

               allowNull: false

           },

           firstName: {

               type: Sequelize.STRING,

               allowNull: false

           },

           lastName: {

               type: Sequelize.STRING

           },

           email: {

               type: Sequelize.STRING,

               unique: true,

               allowNull: false

           },

           password: {

               type: Sequelize.STRING,

               allowNull: false

           },

           createdAt: {

               type: Sequelize.DATE,

               allowNull: false

           },

           updatedAt: {

               type: Sequelize.DATE,

               allowNull: false

           }

       });

   },

   down: (queryInterface, Sequelize) => {

       return queryInterface.dropTable('Users');

   }

};

```

Also, replace the content of `models/user.js` with:

```js

// models/user.js


'use strict';

module.exports = (sequelize, DataTypes) => {

   const User = sequelize.define('User', {

       id: {

           type: DataTypes.INTEGER,

           primaryKey: true,

           autoIncrement: true,

           allowNull: false

       },

       firstName: {

           type: DataTypes.STRING,

           allowNull: false

       },

       lastName: DataTypes.STRING,

       email: {

           type: DataTypes.STRING,

           unique: true,

           allowNull: false

       },

       password: {

           type: DataTypes.STRING,

           allowNull: false

       }

   });

   User.associate = function(models) {

       // A user can have many post

       User.hasMany(models.Post);

   };

   return User;

};

```

Notice we define the relationship (one-to-many) between User and Post.

We do the same for Post:

```bash

sequelize model:generate --name Post --attributes title:string,content:string

```

Open `migrations/XXXXXXXXXXXXXX-create-post.js` and replace it content with:

```js

// migrations/XXXXXXXXXXXXXX-create-post.js


'use strict';

module.exports = {

   up: (queryInterface, Sequelize) => {

       return queryInterface.createTable('posts', {

           id: {

               type: Sequelize.INTEGER,

               autoIncrement: true,

               primaryKey: true,

               allowNull: false

           },

           userId: {

               type: Sequelize.INTEGER.UNSIGNED,

               allowNull: false

           },

           title: {

               type: Sequelize.STRING,

               allowNull: false

           },

           slug: {

               type: Sequelize.STRING,

               unique: true,

               allowNull: false

           },

           content: {

               type: Sequelize.STRING,

               allowNull: false

           },

           status: {

               type: Sequelize.BOOLEAN,

               allowNull: false,

               defaultValue: false

           },

           createdAt: {

               type: Sequelize.DATE,

               allowNull: false

           },

           updatedAt: {

               type: Sequelize.DATE,

               allowNull: false

           }

       });

   },

   down: (queryInterface, Sequelize) => {

       return queryInterface.dropTable('posts');

   }

};

```

Also, replace the content of `models/post.js` with:

```js

// models/post.js


'use strict';

module.exports = (sequelize, DataTypes) => {

   const Post = sequelize.define('Post', {

       id: {

           type: DataTypes.INTEGER,

           primaryKey: true,

           autoIncrement: true,

           allowNull: false

       },

       userId: {

           type: DataTypes.INTEGER.UNSIGNED,

           allowNull: false

       },

       title: {

           type: DataTypes.STRING,

           allowNull: false

       },

       slug: {

           type: DataTypes.STRING,

           allowNull: false,

           unique: true

       },

       content: {

           type: DataTypes.STRING,

           allowNull: false

       },

       status: {

           type: DataTypes.BOOLEAN,

           allowNull: false,

           defaultValue: false

       }

   });

   Post.associate = function(models) {

       // A post belongs to a user

       Post.belongsTo(models.User);

       // A post can belong to many tags

       Post.belongsToMany(models.Tag, { through: 'post_tag' });

   };

   return Post;

};

```

We define the inverse relationship between `Post` and `User`. Also, we define the relationship (belongs-to-many) between `Post` and `Tag`.

We do the same for `Tag`:

```bash

sequelize model:generate --name Tag --attributes \ name:string,description:string

```

Open `migrations/XXXXXXXXXXXXXX-create-tag.js` and replace it content with:

```js

// migrations/XXXXXXXXXXXXXX-create-tag.js


'use strict';

module.exports = {

   up: (queryInterface, Sequelize) => {

       return queryInterface.createTable('tags', {

           id: {

               type: Sequelize.INTEGER,

               autoIncrement: true,

               primaryKey: true,

               allowNull: false

           },

           name: {

               type: Sequelize.STRING,

               unique: true,

               allowNull: false

           },

           slug: {  

               type: Sequelize.STRING,

               unique: true,

               allowNull: false

           },

           description: {

               type: Sequelize.STRING,

           },

           createdAt: {

               type: Sequelize.DATE,

               allowNull: false

           },

           updatedAt: {

               type: Sequelize.DATE,

               allowNull: false

           }

       });

   },

   down: (queryInterface, Sequelize) => {

       return queryInterface.dropTable('tags');

   }

};

```

Also, replace the content of `models/tag.js` with:

```js

// models/tag.js


'use strict';

module.exports = (sequelize, DataTypes) => {

   const Tag = sequelize.define('Tag', {

       id: {

           type: DataTypes.INTEGER,

           primaryKey: true,

           autoIncrement: true,

           allowNull: false

       },

       name: {

           type: DataTypes.STRING,

           unique: true,

           allowNull: false

       },

       slug: {

           type: DataTypes.STRING,

           allowNull: false,

           unique: true

       },

       description: DataTypes.STRING

   });

   Tag.associate = function(models) {

       // A tag can have to many posts

       Tag.belongsToMany(models.Post, { through: 'post_tag' });

   };

   return Tag;

};

```

Also, we define the relationship (belongs-to-many)between `Tag` and `Post`.

We need to define one more model/migration for the pivot table for the belongs-to-many relationship between `Tag` and `Post`.

```bash

sequelize model:generate --name PostTag --attributes postId:integer

```

Open `migrations/XXXXXXXXXXXXXX-create-post-tag.js` and replace it content with:

```js

// migrations/XXXXXXXXXXXXXX-create-post-tag.js


'use strict';

module.exports = {

   up: (queryInterface, Sequelize) => {

       return queryInterface.createTable('post_tag', {

           id: {

               type: Sequelize.INTEGER,

               autoIncrement: true,

               primaryKey: true,

               allowNull: false

           },

           postId: {

               type: Sequelize.INTEGER,

               allowNull: false

           },

           tagId: {

               type: Sequelize.INTEGER,

               allowNull: false

           },

           createdAt: {

               allowNull: false,

               type: Sequelize.DATE

           },

           updatedAt: {

               allowNull: false,

               type: Sequelize.DATE

           }

       });

   },

   down: (queryInterface, Sequelize) => {

       return queryInterface.dropTable('post_tag');

   }

};

```

Also, replace the content of `models/posttag.js` with:

```js

// models/posttag.js


'use strict';
module.exports = (sequelize, DataTypes) => {

   const PostTag = sequelize.define('PostTag', {

       id: {

           type: DataTypes.INTEGER,

           primaryKey: true,

           autoIncrement: true,

           allowNull: false

       },

       postId:{

           type: DataTypes.INTEGER.UNSIGNED,

           allowNull: false

       },

       tagId:{

           type: DataTypes.INTEGER.UNSIGNED,

           allowNull: false

       }

   });
   return PostTag;

};

```

Now, let’s run our migrations:

```bash

sequelize db:migrate

```

Writing Resolvers

Our schema is nothing without resolvers. A resolver is a function that defines how a field in a schema is executed. Now, let’s we define our resolvers. Within the data folder, create a new `resolvers.js` file and paste following code into it:

```js

// data/resolvers.js

'use strict';


const { GraphQLScalarType } = require('graphql');

const { Kind } = require('graphql/language');

const { User, Post, Tag } = require('../models');

const bcrypt = require('bcrypt');

const jwt = require('jsonwebtoken');

const slugify = require('slugify');

require('dotenv').config();

```

We start off by importing the necessary packages as well as our models. Because we’ll be defining a custom scalar `DateTime` type, we import `GraphQLScalarType` and kind. `bcrypt` will be used for hashing users password, `jsonwebtoken` will be used to generate a JSON Web Token (JWT) which will be used to authenticate users. `slugify` will be used to create slugs. We also import our models. Finally, import `dotenv` so we can read from our `.env` file.

Now let’s start defining our resolver functions. We’ll start by defining resolver functions for our queries. Add the code below inside `resolvers.js`:

```js

// data/resolvers.js


// Define resolvers

const resolvers = {

   Query: {

       // Fetch all users

       async allUsers() {

           return await User.all();

       },


       // Get a user by it ID

       async fetchUser(_, { id }) {

           return await User.findById(id);

       },

       // Fetch all posts

       async allPosts() {

           return await Post.all();

       },

       // Get a post by it ID

       async fetchPost(_, { id }) {

           return await Post.findById(id);

       },

       // Fetch all tags

       async allTags(_, args, { user }) {
           return await Tag.all();

       },

       // Get a tag by it ID

       async fetchTag(_, { id }) {

           return await Tag.findById(id);

       },

   },
}

module.exports = resolvers;

```

Our resolver functions makes use of JavaScript new features like object destructuring and async/await. The resolvers for queries are pretty straightforward as they simply retrieve data from the database.

Now, let’s define resolver functions for our mutations. Add the code below inside `resolvers.js` just after the Query object:

```js

// data/resolvers.js


Mutation: {

   // Handles user login

   async login(_, { email, password }) {

       const user = await User.findOne({ where: { email } });

       if (!user) {

           throw new Error('No user with that email');

       }

       const valid = await bcrypt.compare(password, user.password);

       if (!valid) {

           throw new Error('Incorrect password');

       }

       // Return json web token

       return jwt.sign({

           id: user.id,

           email: user.email

       }, process.env.JWT_SECRET, { expiresIn: '1y' });

   },

   // Create new user

   async createUser(_, { firstName, lastName, email, password }) {

       return await User.create({

           firstName,

           lastName,

           email,

           password: await bcrypt.hash(password, 10)

       });

   },

   // Update a particular user

   async updateUser(_, { id, firstName, lastName, email, password }, { authUser }) {

       // Make sure user is logged in

       if (!authUser) {

           throw new Error('You must log in to continue!')

       }

       // fetch the user by it ID

       const user = await User.findById(id);

       // Update the user

       await user.update({

           firstName,

           lastName,

           email,

           password: await bcrypt.hash(password, 10)

       });

       return user;

   },

   // Add a new post

   async addPost(_, { title, content, status, tags }, { authUser }) {

       // Make sure user is logged in

       if (!authUser) {

           throw new Error('You must log in to continue!')

       }

       const user = await User.findOne({ where: { id: authUser.id } });
       const post = await Post.create({

           userId: user.id,

           title,

           slug: slugify(title, { lower: true }),

           content,

           status

       });

       // Assign tags to post

       await post.setTags(tags);

       return post;

   },

   // Update a particular post

   async updatePost(_, { id, title, content, status, tags }, { authUser }) {

       // Make sure user is logged in

       if (!authUser) {

           throw new Error('You must log in to continue!')

       }

       // fetch the post by it ID

       const post = await Post.findById(id);

       // Update the post

      await post.update({

          title,

          slug: slugify(title, { lower: true }),

          content,

          status

      });

      // Assign tags to post

      await post.setTags(tags);

          return post;

   },

   // Delete a specified post

   async deletePost(_, { id }, { authUser }) {

       // Make sure user is logged in

       if (!authUser) {

           throw new Error('You must log in to continue!')

       }

       // fetch the post by it ID

       const post = await Post.findById(id);

       return await post.destroy();

   },

   // Add a new tag

   async addTag(_, { name, description }, { authUser }) {

       // Make sure user is logged in

       if (!authUser) {

           throw new Error('You must log in to continue!')

       }

       return await Tag.create({

           name,

           slug: slugify(name, { lower: true }),

           description

       });

   },

   // Update a particular tag

   async updateTag(_, { id, name, description }, { authUser }) {

       // Make sure user is logged in

       if (!authUser) {

           throw new Error('You must log in to continue!')

       }

       // fetch the tag by it ID

       const tag = await Tag.findById(id);

       // Update the tag

       await tag.update({

           name,

           slug: slugify(name, { lower: true }),

           description

       });

       return tag;

   },

   // Delete a specified tag

   async deleteTag(_, { id }, { authUser }) {

       // Make sure user is logged in

       if (!authUser) {

           throw new Error('You must log in to continue!')

       }

       // fetch the tag by it ID

       const tag = await Tag.findById(id);

       return await tag.destroy();

   }

},

```

Let’s go over the mutations. login checks if a user with the email and password supplied exists in the database. We use `bcrypt` to compare the password supplied with the password hash generated while creating the user. If the user exist, we generate a JWT. `createUser` simply adds a new user to the database with the data passed to it. As you can see we hash the user password with `bcrypt`. For the other mutations, we first check to make sure the user is actually logged in before allowing to go on and carry out the intended tasks. `addPost` and `updatePost` after adding/updating a post to the database uses `setTags()` to assign tags to the post. setTags() is available on the model due to the belongs-to-many relationship between `Post` and `Tag`. We also define resolvers to add, update and delete a tag respectively.

Next, we define resolvers to retrieve the fields on our `User`, `Post` and `Tag` type respectively. Add the code below inside `resolvers.js` just after the Mutation object:

```js

// data/resolvers.js

User: {

   // Fetch all posts created by a user

   async posts(user) {

       return await user.getPosts();

   }

},

Post: {

   // Fetch the author of a particular post

   async user(post) {

       return await post.getUser();

   },

   // Fetch alls tags that a post belongs to

   async tags(post) {

       return await post.getTags();

   }

},

Tag: {

   // Fetch all posts belonging to a tag

   async posts(tag) {

       return await tag.getPosts();

   }

},

```

These uses the methods (`getPosts()`, `getUser()`, `getTags()`, `getPosts()`) made available on the models due to the relationships we defined.

Let’s define our custom scalar type. Add the code below inside `resolvers.js` just after the `Tag` object:

```js

// data/resolvers.js


DateTime: new GraphQLScalarType({

   name: 'DateTime',

   description: 'DateTime type',

   parseValue(value) {

       // value from the client

       return new Date(value);

   },

   serialize(value) {

       const date = new Date(value);

       // value sent to the client

       return date.toISOString();

   },

   parseLiteral(ast) {

       if (ast.kind === Kind.INT) {

           // ast value is always in string format

           return parseInt(ast.value, 10);

       }

       return null;

   }

})

```

We define our custom scalar `DateTime` type. `parseValue()` accepts a value from the client and convert it to a `Date` object which will be inserted into the database. `serialize()` also accepts a value, but this time value is coming from the database. The value converted to a `Date` object and a date in ISO format is returned to the client.

That’s all for our resolvers. Noticed we use `JWT_SECRET` from the environment variable which we are yet to define. Add the line below to `.env`:

```env

// .env

JWT_SECRET=somereallylongsecretkey

```

One last thing to do before we test out the API is to update `server.js` as below:

```js

// server.js


'use strict';

const express = require('express');

const bodyParser = require('body-parser');

const { graphqlExpress, graphiqlExpress } = require('apollo-server-express');

const schema = require('./data/schema');

const jwt = require('express-jwt');

require('dotenv').config();

const PORT = 3000;

// Create our express app

const app = express();

// Graphql endpoint

app.use('/api', bodyParser.json(), jwt({

       secret: process.env.JWT_SECRET,

       credentialsRequired: false,

   }), graphqlExpress( req => ({

       schema,

       context: {

           authUser: req.user

       }

})
));

// Graphiql for testing the API out

app.use('/graphiql', graphiqlExpress({ endpointURL: 'api' }));

app.listen(PORT, () => {

   console.log(`GraphiQL is running on http://localhost:${PORT}/graphiql`);

});

```

We simply add the `express-jwt` middleware to the API route. This makes the route secured as it will check to see if there is an Authorization header with a JWT on the request before granting access to the route. We set `credentialsRequired` to false because we users to be able to at least login and register first. `express-jwt` adds the details of the authenticated user to the request body which we turn pass as context to `graphqlExpress`.

Testing It Out

Now, we can test out the API. We’ll use GraphiQL to testing out the API. First, we need to start the server with:

```bash

node server.js

```

and we can access it on http://localhost:3000/graphiql. Try creating a new user with createUser mutation. You should get a response a s in the image below:

Create new user
Create new user

We can now login:

Login a user
Login a user

You can see the JWT returned on successful login.

For the purpose of testing out the other secured aspects of the API, we need to find a way to add the JWT generated above to the request headers. To do that, we’ll use a Chrome extension called ModHeader to modify the request headers and define the Authorization header. Once the Authorization header contains a JWT, this signifies that the user making the request is authenticated, hence will be able to carry out authenticated users only activities.

Enter the Authorization as the name of the header and Bearer `YOUR_JSON_WEB_TOKEN` as its value:

Add JWT to header
Add JWT to header

Now, try adding a new post:

Add a new post
Add a new post

Fetch a particular post
Fetch a particular post

Conclusion

We’ve established an API to power our own blog with GraphQL interface and can make authenticated calls to create and retrieve data, now what can you do?

The complete code for our API is available on GitHub and can be used as a starting point.

Check out Deploying Apollo GQL API to Zeit which shows how to take your local implementation of an API and making it accessible on the web using Zeit Now.

Then read about pairing graphql fragments with UI components in GraphQL Fragments are the Best Match for UI Components and start building the interface of your blog.

Recent posts

Related posts

No items found.
New
Create and manage pricing plans with the new Plan Builder. Read our blog post
Explore Plan Builder