“Human security depends on a system where each rational individual calculates that it is more profitable not to rebel.”
― Mark Gough
Introduction
In the previous posts, we have learned about implementing GraphQL and creating queries and mutations. Should you get to this post first, I recommend you to read the previous posts here first:
This is the fourth part of “How to Implement GraphQL in Microservices using Node.js”, and in this part, we will learn about implementing authentication through GraphQL. Because, without this a secured server will deny requests sent from GraphQL server, even though the requests can be accepted without going through the GraphQL server.
I will provide the updated mock server with JWT Authentication to ease you in setting up for learning. And, at the end of this post, I will provide a working source code.
Let’s begin.
Requirements
Here are the requirements in this tutorial:
- The previous GQL project
It’s recommended to clone this project we created from the previous tutorial, and name it ascli-nodejs-gql-4
. If you want to add the code directly, it’s fine too. - This mock service source code
If you prefer to build another REST API on your own, you are free to do so. But, why not save time? If you do, after cloning the project, runnpm i
and explore the endpoints defined inindex.js
to make sure it works well.
Part 4 – Authentication
I assume that you already know about authentications. And the one I use here is by authenticating a JWT token obtained from logging in.
The approach I use is by passing the authentication from the GraphQL server to the mock service. Therefore, the mock service will be the one that authenticate the payloads sent from the front while GraphQL server do nothing but passing only.
By doing this, we make sure that the GraphQL server has only one job: to resolve query and mutation requests from the front.
Login Mutation
We will start by creating a mutation for admin login. The logic for logging in an admin is already in the mock service, so we can focus on creating the GraphQL resolver.
graphql/admin/…
First, we create the graphql/admin/...
directories just like users
, transactions
, and banks
before:

The content in admin/index.js
is the same as banks/index.js
. And admin/queries/index.js
is also the same with banks/queries/index.js
.
Let’s create the schemas for admin in admin/queries/schemas.gql
:
module.exports = `
type AdminLogin {
status: String!
message: String
token: String!
}
`;
We call it AdminLogin
, and it has three properties: status
, message
, token
. Only status
and token
are required to be returned from the mock service. message
enables us to understand better what status
means.
Next, we create the AdminLogin
resolver in admin/queries/resolvers
:
const AdminLogin = {};
module.exports = { AdminLogin };
Yeah, it does nothing, because it does not need to get data from others.
_root/mutation/resolvers.js and _root/mutation/schemas.gql
Logging in requires a username
and password
, so we have to create a new mutation in _root/mutation/schemas.gql
and _root/mutation/resolvers.js
.
Here is the snippet on adding a login mutation:
module.exports = `
type Mutation {
# Banks
...
# Users
...
# Transactions
...
# Login
login(username: String!, password: String!): AdminLogin
}
`;
We just need to add another line: login
. It requires two parameters: username
and password
. The response from this request will be AdminLogin
, which we have written on admin/queries/schemas.gql
.
Next, we write the mutation resolver in _root/mutations/resolvers.js
:
// _root/mutations/resolvers.js
...
const banks = {
...
};
const users = {
...
};
const transactions = {
...
}
const admin = {
login: async (_, data) => {
return post(`${baseUrl}/admin/login`, data);
}
}
const Mutation = {
...,
...admin
};
module.exports = { Mutation };
Pretty much the same with the other mutations, we just need to pass data
to POST <baseUrl>/admin/login
. Then register the admin
resolver to Mutation
object.
Test the code
Run npm start
on both GraphQL server (cil-nodejs-gql-4) and the mock service. Open GraphQL Playground from http://localhost:4000/graphql
.
In the query field, write this mutation:
mutation Login ($username:String!, $password:String!){
login(username:$username, password: $password){
status,
message,
token
}
}
Then in query variables field, put both username
and password
as an object:
{
"username": "admin",
"password": "admin123"
}
Send the request, and you should get the response similar to this:

The string token
will be used later as authentication for all requests we created before.
Nice.
Use token
as Authentication in Headers
We got the token. Now, we need to use them for our requests. In the mock service I updated all mutations and queries, except login, to require authentication before processing the request.
You will see this kind of message if you try to send a query request to get banks without authentication:

And here is the text version from the response:
{
"errors": [
{
"message": "Error: Request failed with status code 401",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"banks"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"stacktrace": [
"Error: Error: Request failed with status code 401",
" at get (/Users/19057992/Documents/training/cil-nodejs-gql-4/connectors/httpConnector.js:9:15)",
" at processTicksAndRejections (internal/process/task_queues.js:97:5)"
]
}
}
}
],
"data": {
"banks": null
}
}
The message shows that it returns a request failed status with code 401. Code 401 is “Unauthorized”, which means it needs some sort of authentication to authorise the access. And here is where our token plays its part.
We will update a resolver for getting a list of Banks
as an example.
graphql/baseResolver.js
First, we create a baseResolver.js
, which will be used as the general endpoint to get the auth
header value, and then import httpConnector.js
to send the request to the mock service:
// graphql/baseResolver.js
const { get } = require('../connectors/httpConnector');
const baseUrl = 'http://localhost:4001';
const createGetListUrl = (url, emptyValue = []) => {
return async (_, params, context) => {
const { auth = '' } = context.headers;
return await get(`${baseUrl}/${url}`, { headers: { auth } }) || emptyValue;
}
};
const createGetUrl = (url, emptyValue = {}) => {
return async (_, params, context) => {
const { auth } = context.headers;
return await get(`${baseUrl}/${url}/${params.id}`, { headers: { auth } }) || emptyValue;
}
};
module.exports = {
baseUrl,
createGetListUrl,
createGetUrl
}
We import get()
from httpConnector.js
, then set baseUrl
. Then we create a new function createGetListUrl()
, which will return a generic asynchronous function as a resolver which requests get()
from httpConnector.js
.
The returned function will get auth
from context
and send it to get()
as a property of { headers }
.
The same goes with createGetUrl()
, which gets params.id
from the returned asynchronous function.
connectors/httpConnector.js
So, we need to modify httpConnector.js
also:
const axios = require('axios');
const get = async (url, headers = {}) => {
try {
const response = await axios.get(url, headers);
return response.data;
} catch (error) {
...
}
return null;
}
...
Add a headers
parameter on get()
and axios.get()
.
_root/queries/resolvers.js
Now, we will refactor _root/queries/resolvers.js
to use baseResolver
.
const {
createGetUrl,
createGetListUrl
} = require('../../baseResolver');
const Query = {
banks: createGetListUrl(`banks`),
bank: createGetUrl(`banks`),
...
};
module.exports = { Query };
We refactor this module to use baseResolver
we created before. The functions we need are createGetUrl()
and createGetListUrl()
.
Next, we change the get()
function in banks
and bank
to call createGetListUrl()
and createGetUrl()
respectively. And we put “banks” as the argument for both functions.
Test the code
OK, let’s run both the mock service and GraphQL server. Open the GraphQL Playground and do these steps:
- Copy the token from the
Login
mutation, you have to send the username and password first, of course. - Reopen the
Banks
query. - In the HTTP Headers field, put this:
{
"auth": "<the_token_you copied_before>"
}
Lastly, run the query. The result should look like the screenshot below:

Now, you can do the same for getting a bank.
Practice Time!
We have created a sample of adding authentication through GraphQL to the mock service for getting a list of banks and a bank detail. How about users
and transactions
? You can try to add them and see if it works.
Users and Transactions Resolvers
If you tried to add these both resolvers, you might find that it did not work just like banks. Most likely you will encounter this error:
{
"errors": [
{
"message": "Error: Request failed with status code 401",
"locations": [
{
"line": 5,
"column": 5
}
],
"path": [
"users",
1,
"bank"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"stacktrace": [
"Error: Error: Request failed with status code 401",
" at get (/Users/19057992/Documents/training/cil-nodejs-gql-4/connectors/httpConnector.js:9:15)",
" at processTicksAndRejections (internal/process/task_queues.js:97:5)"
]
}
}
}
],
"data": {
"users": null
}
}
Look at the path
properties. It shows users
, 1
, and bank
. This basically means the error happen when getting the bank
details of the first user, or in array form: user[0]
. What is going on here? It worked before right?
Let’s check user/queries/resolvers.js
to see what happens. And here is a snippet of the file:
const { get } = require('../../../connectors/httpConnector');
const baseUrl = 'http://localhost:4001';
const User = {
bank: async (user) => await get(`${baseUrl}/banks/${user.bankId}`) || {}
};
module.exports = { User };
The bank
endpoint is still without header authentication. So, we just need to refactor the code just like what we did on _root
by importing createGetUrl()
from baseResolvers
. See this snippet:
const { createGetUrl } = require('../../baseResolver');
const User = {
bank: async (user, _, context) => {
const getBank = createGetUrl('banks');
return getBank(_, { id: user.bankId }, context);
}
};
module.exports = { User };
We still need to get the bankId
from user
, so we get user
the parameter on the bank
resolver function. Then, we create a getBank()
function just like when we query banks
before.
Lastly, we call the getBank()
function with the correct parameters. We can currently ignore the first parameter as we won’t be using it. The second parameter is params
, therefore we can assign user.bankId
as the value of id
in an object. The third and final parameter is the same context
passed from the function. This is important because we need the auth
header passed through context
.
Test the Code
Run npm start
on both the mock service and the GraphQL Server. Then, run GraphQL Playground and query Users
with authentication like query Banks
before. It should work this time.
Practice Time!
Now, you should be able to refactor the transactions
query with the same steps just like on users
. You can compare your code with my source code at the end of the post.
What about mutations?
Same approach. You just need to add createPostUrl()
, createPatchUrl()
and createDeleteUrl()
on baseResolver.js
.
Then refactor _root/mutations/resolvers.js
to import from baseResolver
and the asynchronous functions for every mutations.
Lastly, of course, test the code as usual 🙂
Conclusion
Finally we reached the end of the post. You should have learned about:
- Creating a login mutation
- Refactor queries to support
auth
. - Passing headers through context to detail resolvers.
- Refactor mutations to support
auth
.
And here is the source code for this post. Let me know if there’s something I miss or can be improved. Happy coding!
“We live in a world that has walls and those walls need to be guarded by men with guns.”
― Aaron Sorkin, A Few Good Men