Skip to content

docs: add page on authorization strategies #4396

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: 16.x.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions website/pages/docs/_meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const meta = {
'object-types': '',
'mutations-and-input-types': '',
'authentication-and-express-middleware': '',
'authorization-strategies': '',
'-- 2': {
type: 'separator',
title: 'Advanced Guides',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Authentication and Express Middleware
sidebarTitle: Authentication & Middleware
title: Using Express Middleware with GraphQL.js
sidebarTitle: Using Express Middleware
---

import { Tabs } from 'nextra/components';
Expand Down Expand Up @@ -100,3 +100,5 @@ In a REST API, authentication is often handled with a header, that contains an a
If you aren't familiar with any of these authentication mechanisms, we recommend using `express-jwt` because it's simple without sacrificing any future flexibility.

If you've read through the docs linearly to get to this point, congratulations! You now know everything you need to build a practical GraphQL API server.

Want to control access to specific operations or fields? See [Authorization Strategies](\pages\docs\authorization-strategies.mdx).
178 changes: 178 additions & 0 deletions website/pages/docs/authorization-strategies.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
---
title: Authorization Strategies
---

GraphQL gives you complete control over how to define and enforce access control.
That flexibility means it's up to you to decide where authorization rules live and
how they're enforced.

This guide covers common strategies for implementing authorization in GraphQL
servers using GraphQL.js. It assumes you're authenticating requests and passing a user or
session object into the `context`.

## What is authorization?

Authorization determines what a user is allowed to do. It's different from
authentication, which verifies who a user is.

In GraphQL, authorization typically involves restricting:

- Access to certain queries or mutations
- Visibility of specific fields
- Ability to perform mutations based on roles or ownership

## Resolver-based authorization

> **Note:**
> All examples assume you're using Node.js 20 or later with [ES module (ESM) support](https://nodejs.org/api/esm.html) enabled.

The simplest approach is to enforce access rules directly inside resolvers
using the `context.user` value:

```js
export const resolvers = {
Query: {
secretData: (parent, args, context) => {
if (!context.user || context.user.role !== 'admin') {
throw new Error('Not authorized');
}
return getSecretData();
},
},
};
```

This works well for smaller schemas or one-off checks.

## Centralizing access control logic

As your schema grows, repeating logic like `context.user.role !=='admin'`
becomes error-prone. Instead, extract shared logic into utility functions:

```js
export function requireUser(user) {
if (!user) {
throw new Error('Not authenticated');
}
}

export function requireRole(user, role) {
requireUser(user);
if (user.role !== role) {
throw new Error(`Must be a ${role}`);
}
}
```

You can use these helpers in resolvers:

```js
import { requireRole } from './auth.js';

export const resolvers = {
Mutation: {
deleteUser: (parent, args, context) => {
requireRole(context.user, 'admin');
return deleteUser(args.id);
},
},
};
```

This pattern makes your access rules easier to read, test, and update.

## Field-level access control

You can also conditionally return or hide data at the field level. This
is useful when, for example, users should only see their own private data:

```js
export const resolvers = {
User: {
email: (parent, args, context) => {
if (context.user.id !== parent.id && context.user.role !== 'admin') {
return null;
}
return parent.email;
},
},
};
```

Returning `null` is a common pattern when fields should be hidden from
unauthorized users without triggering an error.

## Declarative authorization with directives

If you prefer a schema-first or declarative style, you can define custom
schema directives like `@auth(role: "admin")` directly in your SDL:

```graphql
type Query {
users: [User] @auth(role: "admin")
}
```

To enforce this directive during execution, you need to inspect it in your resolvers
using `getDirectiveValues`:

```js
import { getDirectiveValues } from 'graphql';

function withAuthCheck(resolverFn, schema, fieldNode, variableValues, context) {
const directive = getDirectiveValues(
schema.getDirective('auth'),
fieldNode,
variableValues
);

if (directive?.role && context.user?.role !== directive.role) {
throw new Error('Unauthorized');
}

return resolverFn();
}
```

You can wrap individual resolvers with this logic, or apply it more broadly using a
schema visitor or transformation.

GraphQL.js doesn't interpret directives by default, they're just annotations.
You must implement their behavior manually, usually by:

- Wrapping resolvers in custom logic
- Using a schema transformation library to inject authorization checks

Directive-based authorization can add complexity, so many teams start with
resolver-based checks and adopt directives later if needed.

## Best practices

- Keep authorization logic close to business logic. Resolvers are often the
right place to keep authorization logic.
- Use shared helper functions to reduce duplication and improve clarity.
- Avoid tightly coupling authorization logic to your schema. Make it
reusable where possible.
- Consider using `null` to hide fields from unauthorized users, rather than
throwing errors.
- Be mindful of tools like introspection or GraphQL Playground that can
expose your schema. Use caution when deploying introspection in production
environments.

## Additional resources

- [Anatomy of a Resolver](./resolver-anatomy): Shows how resolvers work and how the `context`
object is passed in. Helpful if you're new to writing custom resolvers or
want to understand where authorization logic fits.
- [GraphQL Specification, Execution section](https://spec.graphql.org/October2021/#sec-Execution): Defines how fields are
resolved, including field-level error propagation and execution order. Useful
background when building advanced authorization patterns that rely on the
structure of GraphQL execution.
- [`graphql-shield`](https://github.com/dimatill/graphql-shield): A community library for adding rule-based
authorization as middleware to resolvers.
- [`graphql-auth-directives`](https://github.com/the-guild-org/graphql-auth-directives): Adds support for custom directives like
`@auth(role: "admin")`, letting you declare access control rules in SDL.
Helpful if you're building a schema-first API and prefer declarative access
control.


21 changes: 11 additions & 10 deletions website/pages/docs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@ title: Overview
sidebarTitle: Overview
---

GraphQL.JS is the reference implementation to the [GraphQL Specification](https://spec.graphql.org/draft/), it's designed to be simple to use and easy to understand
while closely following the Specification.
GraphQL.js is the official JavaScript implementation of the
[GraphQL Specification](https://spec.graphql.org/draft/). It provides the core building blocks
for constructing GraphQL servers, clients, tools, and utilities in JavaScript and TypeScript.

You can build GraphQL servers, clients, and tools with this library, it's designed so you can choose which parts you use, for example, you can build your own parser
and use the execution/validation from the library. There also a lot of useful utilities for schema-diffing, working with arguments and many more.
This documentation site is for developers who want to:

In the following chapters you'll find out more about the three critical pieces of this library
- Understand how GraphQL works
- Build a GraphQL API using GraphQL.js
- Extend, customize, or introspect GraphQL systems
- Learn best practices for using GraphQL.js in production

- The GraphQL language
- Document validation
- GraphQL Execution

You can also code along on [a tutorial](/docs/getting-started).
Whether you're writing your own server, building a GraphQL clients, or creating tools
that work with GraphQL, this site guides you through core concepts, APIs, and
advanced use cases of GraphQL.js.
Loading