Chimezie Enyinnaya

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:

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:

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:

// 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:

// 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:

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:

// 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

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:

// .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:

// 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:

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:

// 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:

// 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:

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

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

// 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:

// 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:

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

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

// 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:

// 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.

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

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

// 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:

// 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:

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:

// 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:

// 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:

// 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:

// 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:

// 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

JWT_SECRET=somereallylongsecretkey

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

// 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:

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 userCreate new user

We can now login:

Login a userLogin 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 headerAdd JWT to header

Now, try adding a new post:

Add a new postAdd a new post

Fetch a particular postFetch 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.

Stratus Background
StratusUpdate

Sign up for the Stratus Update newsletter

With our monthly newsletter, we’ll keep you up to date with a curated selection of the latest cloud services, projects and best practices.
Click here to read the latest issue.