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.