In code-first GraphQL servers like Apollo, authorization is often ad-hoc and scattered.

To centralize and harden your access control logic, graphql-shield provides a declarative, composable way to define RBAC at the schema level.

This post outlines how to build a secure and maintainable RBAC system using Apollo Server + graphql-shield.


1. Install Dependencies

npm install graphql graphql-shield apollo-server

2. Define Roles and Permissions

Example roles: ADMIN, USER, GUEST

We'll use a context object to propagate identity:

interface Context {
  user?: {
    id: string;
    role: 'ADMIN' | 'USER' | 'GUEST';
  };
}

3. Define Your Schema

type Query {
  me: User
  adminStats: Stats
}

type Mutation {
  updateUser(id: ID!, input: UserInput): User
}

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

type Stats {
  userCount: Int
  revenue: Float
}

4. Create Permission Rules with graphql-shield

import { rule, shield, and } from 'graphql-shield';

// Base rules
const isAuthenticated = rule()(async (parent, args, ctx: Context) => {
  return !!ctx.user;
});

const isAdmin = rule()(async (parent, args, ctx: Context) => {
  return ctx.user?.role === 'ADMIN';
});

const isSelf = rule()(async (parent, { id }, ctx: Context) => {
  return ctx.user?.id === id;
});

5. Map Rules to Schema with Permissions Object

const permissions = shield({
  Query: {
    me: isAuthenticated,
    adminStats: isAdmin,
  },
  Mutation: {
    updateUser: and(isAuthenticated, isSelf),
  },
});

This enforces:

  • Only authenticated users can access me
  • Only admins can see adminStats
  • Users can only update their own profile

6. Integrate with Apollo Server

import { ApolloServer } from 'apollo-server';
import { applyMiddleware } from 'graphql-middleware';

const server = new ApolloServer({
  schema: applyMiddleware(schema, permissions),
  context: ({ req }) => {
    const token = req.headers.authorization || '';
    const user = decodeJWT(token); // your JWT decoder
    return { user };
  },
});

7. Tips for Secure Scaling

  • Use role constants or enums to avoid typos
  • Chain rules with and, or, not for complex logic
  • Log access decisions for auditability
  • Version control permission definitions alongside schema

Final Thoughts

RBAC in Apollo isn't optional — it's your last line of defense.

With graphql-shield, you gain clarity, modularity, and enforceability across your GraphQL API.

Next: add field-level restrictions, dynamic tenant scoping, and automated tests for auth rules.

Control the graph. Declare the rules. Trust only what you verify.