JWT Verification in GraphQL: Securing Resolvers (Complete Guide)

What is JWT Verification in GraphQL in Simple Terms?

JWT (JSON Web Token) is a secure token used to identify users.

After login:

  • Server generates a token
  • Client sends token with every request
  • Server verifies token before executing resolver

Example token format:

xxxxx.yyyyy.zzzzzCode language: CSS (css)

Why JWT After Merging Resolvers?

This guide is a continuation of our previous post on building scalable resolvers:
How to Merge Multiple Resolvers in GraphQL
In the previous blog, you learned how to organize and merge resolvers efficiently. But here’s the real question:

👉 What if anyone can access those resolvers?

Without authentication:

  • Anyone can query sensitive data
  • APIs become vulnerable
  • No control over user access

So now, we secure our resolvers using JWT verification.

How JWT Fits into GraphQL Flow

Now connect this with your resolver architecture:

Step-by-step flow:

Now your full flow looks like this:

  1. Login → Generate JWT
  2. Client stores token
  3. Client sends token in header
  4. Context verifies token
  5. Resolver checks context.user

Once your GraphQL API structure is ready, the next critical step is securing those resolvers. That’s where JWT (JSON Web Token) verification comes into play.


Add JWT Verification to Your Existing GraphQL Setup

You already have merged resolvers. Now we enhance them.


Step 1: Install JWT Package

npm install jsonwebtoken

Step 2: Generate JWT on Login

You need a login mutation in your GraphQL API. In VS code inside src folder make Login resolvers. Inside this folder make to file 1-resolvers and schema.graphql and copy and paste the below code.

Login Resolver

import jwt from 'jsonwebtoken';

const SECRET_KEY = process.env.JWT_SECRET;

const resolvers = {
  Mutation: {
    login: async (_, { email, password }) => {
      // 1. Validate user (DB check)
      const user = await findUserByEmail(email);

      if (!user || user.password !== password) {
        throw new Error('Invalid credentials');
      }

      // 2. Generate JWT
      const token = jwt.sign(
        { id: user.id, email: user.email, role: user.role },
        SECRET_KEY,
        { expiresIn: '1h' }
      );

      // 3. Send token to client
      return {
        token,
        user,
      };
    },
  },
};Code language: JavaScript (javascript)

Basic Login Schema

type User {
  id: ID!
  email: String!
  role: String
}

type AuthPayload {
  token: String!
  user: User!
}

type Mutation {
  login(email: String!, password: String!): AuthPayload!
}Code language: JavaScript (javascript)

Step 3: Create JWT Verification Utility

import jwt from 'jsonwebtoken';

const SECRET_KEY = process.env.JWT_SECRET;

export const verifyToken = (token) => {
  try {
    return jwt.verify(token, SECRET_KEY);
  } catch (error) {
    return null;
  }
};Code language: JavaScript (javascript)

Step 4: Inject User into GraphQL Context

This is the most important step.

import { verifyToken } from './utils/jwt';

const server = new ApolloServer({
  typeDefs,
  resolvers,
  context: ({ req }) => {
    const authHeader = req.headers.authorization || '';
    const token = authHeader.replace('Bearer ', '');

    const user = verifyToken(token);

    return { user };
  },
});Code language: JavaScript (javascript)

👉 Now every resolver has access to context.user


Securing Your Existing Resolvers

In your previous blog, your message resolvers is like this:

const resolvers = {
  Query: {
    appMessage() {
      return "This response is coming from the message module.";
    },
  },
};
export default resolvers;Code language: JavaScript (javascript)

Now update it with JWT protection:

const resolvers = {
  Query: {
    appMessage(_,_,context) {
         if (!context.user) {
    throw new Error('Unauthorized');
  }
      return "This response is coming from the message module.";
    },
  },
};
export default resolvers;Code language: JavaScript (javascript)

Update Package.json file also

{
  "name": "jwt_varification",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node src/index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "type": "module",
  "dependencies": {
    "@apollo/server": "^5.5.0",
    "@apollo/subgraph": "^2.14.0",
    "@as-integrations/express5": "^1.1.2",
    "cors": "^2.8.6",
    "express": "^5.2.1",
    "graphql": "^16.13.2",
    "graphql-tag": "^2.12.6",
    "jsonwebtoken": "^9.0.3"
  }
}
Code language: JSON / JSON with Comments (json)

Final Index.js code

import express from "express";
import cors from "cors";
import gql from "graphql-tag";
import { ApolloServer } from "@apollo/server";
import { buildSubgraphSchema } from "@apollo/subgraph";
import { expressMiddleware } from "@as-integrations/express5";
import recordResolvers from "./records/resolvers.js";
import messageResolvers from "./message/resolvers.js";
import loginResolvers from "./Login/resolvers.js";
import { verifyToken } from "./util/verifyToken.js";

import { readFileSync } from "fs";
import { createServer } from "http";

const PORT = 5000;
const app = express();
const httpServer = createServer(app);

const typeDefs = gql(`
  ${readFileSync("./src/records/schema.graphql")}
  ${readFileSync("./src/message/schema.graphql")}
  ${readFileSync("./src/Login/schema.graphql")}
`);

const resolvers = {
  Query: {
    ...recordResolvers.Query,
    ...messageResolvers.Query,
  },
  Mutation: {
    ...loginResolvers.Mutation,
  },
};

const server = new ApolloServer({
  schema: buildSubgraphSchema({ typeDefs, resolvers }),
});

const context = async ({ req }) => {
  const authHeader = req.headers.authorization || "";
  const token = authHeader.replace("Bearer ", "");

  const user = verifyToken(token);

  return { user };
};

await server.start();

app.use(
  "/graphql",
  cors(),
  express.json(),
  expressMiddleware(server, { context })
);

httpServer.listen(PORT, () => {
  console.log(`Server running at http://localhost:${PORT}/graphql`);
});

httpServer.on("error", (error) => {
  console.error("Failed to start server:", error);
});
Code language: JavaScript (javascript)

Folder structure

folder struture

Common Mistakes Developers Make

Avoid these issues:

  • ❌ Not checking token in every resolver
  • ❌ Trusting token without verification
  • ❌ No token expiration
  • ❌ Storing JWT in localStorage (security risk)

Step 5: How Client Sends JWT

After login, the client must include the token in every request.

Method 1: Using Authorization Header

Authorization: Bearer <your_token>Code language: HTML, XML (xml)

Example (Frontend using Fetch)

fetch('/graphql', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${token}`,
  },
  body: JSON.stringify({
    query: `Query {
  appMessage
}`,
  }),
});Code language: JavaScript (javascript)

Step 6: How to Test JWT in GraphQL

You can test this easily using tools like:

  • Postman
  • GraphQL Playground

🔹 Test Flow in Postman

  1. Call login mutation
{
  "query": "mutation { login(email: \"test@mail.com\", password: \"1234\") { token } }"
}Code language: JSON / JSON with Comments (json)
  1. Copy the token from response
  2. Call protected query with header:
Authorization: Bearer <token>Code language: HTML, XML (xml)

🔹 Test Flow in GraphQL Playground

get JWT token

JWT Verification in GraphQL

use it in message resolver

resolver protection
{
  "Authorization": "Bearer your_token_here"
}Code language: JSON / JSON with Comments (json)

Then run:

query {
  getUserProfile {
    id
    email
  }
}

Conclusion

In the previous blog, you structured your GraphQL resolvers.
In this guide, you secured them using JWT.

👉 Together, these two steps create:

  • Scalable APIs
  • Secure data access
  • Production-ready GraphQL systems

Leave a Comment

Your email address will not be published. Required fields are marked *