Skip to content

Commit 2d4ab56

Browse files
sarahxsandersJoviDeCroock
authored andcommitted
docs: add page on authorization strategies (#4396)
Adds page on authorization strategies, right after authentication/middleware page Also adds a "Before you start" section about using ESM syntax JS code snippets --------- Co-authored-by: Jovi De Croock <[email protected]>
1 parent 4154239 commit 2d4ab56

File tree

4 files changed

+194
-12
lines changed

4 files changed

+194
-12
lines changed

website/pages/docs/_meta.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const meta = {
1212
'object-types': '',
1313
'mutations-and-input-types': '',
1414
'authentication-and-express-middleware': '',
15+
'authorization-strategies': '',
1516
'-- 2': {
1617
type: 'separator',
1718
title: 'Advanced Guides',

website/pages/docs/authentication-and-express-middleware.mdx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
2-
title: Authentication and Express Middleware
3-
sidebarTitle: Authentication & Middleware
2+
title: Using Express Middleware with GraphQL.js
3+
sidebarTitle: Using Express Middleware
44
---
55

66
import { Tabs } from 'nextra/components';
@@ -100,3 +100,5 @@ In a REST API, authentication is often handled with a header, that contains an a
100100
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.
101101

102102
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.
103+
104+
Want to control access to specific operations or fields? See [Authorization Strategies](\pages\docs\authorization-strategies.mdx).
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
---
2+
title: Authorization Strategies
3+
---
4+
5+
GraphQL gives you complete control over how to define and enforce access control.
6+
That flexibility means it's up to you to decide where authorization rules live and
7+
how they're enforced.
8+
9+
This guide covers common strategies for implementing authorization in GraphQL
10+
servers using GraphQL.js. It assumes you're authenticating requests and passing a user or
11+
session object into the `context`.
12+
13+
## What is authorization?
14+
15+
Authorization determines what a user is allowed to do. It's different from
16+
authentication, which verifies who a user is.
17+
18+
In GraphQL, authorization typically involves restricting:
19+
20+
- Access to certain queries or mutations
21+
- Visibility of specific fields
22+
- Ability to perform mutations based on roles or ownership
23+
24+
## Resolver-based authorization
25+
26+
> **Note:**
27+
> All examples assume you're using Node.js 20 or later with [ES module (ESM) support](https://nodejs.org/api/esm.html) enabled.
28+
29+
The simplest approach is to enforce access rules directly inside resolvers
30+
using the `context.user` value:
31+
32+
```js
33+
export const resolvers = {
34+
Query: {
35+
secretData: (parent, args, context) => {
36+
if (!context.user || context.user.role !== 'admin') {
37+
throw new Error('Not authorized');
38+
}
39+
return getSecretData();
40+
},
41+
},
42+
};
43+
```
44+
45+
This works well for smaller schemas or one-off checks.
46+
47+
## Centralizing access control logic
48+
49+
As your schema grows, repeating logic like `context.user.role !=='admin'`
50+
becomes error-prone. Instead, extract shared logic into utility functions:
51+
52+
```js
53+
export function requireUser(user) {
54+
if (!user) {
55+
throw new Error('Not authenticated');
56+
}
57+
}
58+
59+
export function requireRole(user, role) {
60+
requireUser(user);
61+
if (user.role !== role) {
62+
throw new Error(`Must be a ${role}`);
63+
}
64+
}
65+
```
66+
67+
You can use these helpers in resolvers:
68+
69+
```js
70+
import { requireRole } from './auth.js';
71+
72+
export const resolvers = {
73+
Mutation: {
74+
deleteUser: (parent, args, context) => {
75+
requireRole(context.user, 'admin');
76+
return deleteUser(args.id);
77+
},
78+
},
79+
};
80+
```
81+
82+
This pattern makes your access rules easier to read, test, and update.
83+
84+
## Field-level access control
85+
86+
You can also conditionally return or hide data at the field level. This
87+
is useful when, for example, users should only see their own private data:
88+
89+
```js
90+
export const resolvers = {
91+
User: {
92+
email: (parent, args, context) => {
93+
if (context.user.id !== parent.id && context.user.role !== 'admin') {
94+
return null;
95+
}
96+
return parent.email;
97+
},
98+
},
99+
};
100+
```
101+
102+
Returning `null` is a common pattern when fields should be hidden from
103+
unauthorized users without triggering an error.
104+
105+
## Declarative authorization with directives
106+
107+
If you prefer a schema-first or declarative style, you can define custom
108+
schema directives like `@auth(role: "admin")` directly in your SDL:
109+
110+
```graphql
111+
type Query {
112+
users: [User] @auth(role: "admin")
113+
}
114+
```
115+
116+
To enforce this directive during execution, you need to inspect it in your resolvers
117+
using `getDirectiveValues`:
118+
119+
```js
120+
import { getDirectiveValues } from 'graphql';
121+
122+
function withAuthCheck(resolverFn, schema, fieldNode, variableValues, context) {
123+
const directive = getDirectiveValues(
124+
schema.getDirective('auth'),
125+
fieldNode,
126+
variableValues
127+
);
128+
129+
if (directive?.role && context.user?.role !== directive.role) {
130+
throw new Error('Unauthorized');
131+
}
132+
133+
return resolverFn();
134+
}
135+
```
136+
137+
You can wrap individual resolvers with this logic, or apply it more broadly using a
138+
schema visitor or transformation.
139+
140+
GraphQL.js doesn't interpret directives by default, they're just annotations.
141+
You must implement their behavior manually, usually by:
142+
143+
- Wrapping resolvers in custom logic
144+
- Using a schema transformation library to inject authorization checks
145+
146+
Directive-based authorization can add complexity, so many teams start with
147+
resolver-based checks and adopt directives later if needed.
148+
149+
## Best practices
150+
151+
- Keep authorization logic close to business logic. Resolvers are often the
152+
right place to keep authorization logic.
153+
- Use shared helper functions to reduce duplication and improve clarity.
154+
- Avoid tightly coupling authorization logic to your schema. Make it
155+
reusable where possible.
156+
- Consider using `null` to hide fields from unauthorized users, rather than
157+
throwing errors.
158+
- Be mindful of tools like introspection or GraphQL Playground that can
159+
expose your schema. Use caution when deploying introspection in production
160+
environments.
161+
162+
## Additional resources
163+
164+
- [Anatomy of a Resolver](./resolver-anatomy): Shows how resolvers work and how the `context`
165+
object is passed in. Helpful if you're new to writing custom resolvers or
166+
want to understand where authorization logic fits.
167+
- [GraphQL Specification, Execution section](https://spec.graphql.org/October2021/#sec-Execution): Defines how fields are
168+
resolved, including field-level error propagation and execution order. Useful
169+
background when building advanced authorization patterns that rely on the
170+
structure of GraphQL execution.
171+
- [`graphql-shield`](https://github.com/dimatill/graphql-shield): A community library for adding rule-based
172+
authorization as middleware to resolvers.
173+
- [`graphql-auth-directives`](https://github.com/the-guild-org/graphql-auth-directives): Adds support for custom directives like
174+
`@auth(role: "admin")`, letting you declare access control rules in SDL.
175+
Helpful if you're building a schema-first API and prefer declarative access
176+
control.
177+
178+

website/pages/docs/index.mdx

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@ title: Overview
33
sidebarTitle: Overview
44
---
55

6-
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
7-
while closely following the Specification.
6+
GraphQL.js is the official JavaScript implementation of the
7+
[GraphQL Specification](https://spec.graphql.org/draft/). It provides the core building blocks
8+
for constructing GraphQL servers, clients, tools, and utilities in JavaScript and TypeScript.
89

9-
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
10-
and use the execution/validation from the library. There also a lot of useful utilities for schema-diffing, working with arguments and many more.
10+
This documentation site is for developers who want to:
1111

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

14-
- The GraphQL language
15-
- Document validation
16-
- GraphQL Execution
17-
18-
You can also code along on [a tutorial](/docs/getting-started).
17+
Whether you're writing your own server, building a GraphQL clients, or creating tools
18+
that work with GraphQL, this site guides you through core concepts, APIs, and
19+
advanced use cases of GraphQL.js.

0 commit comments

Comments
 (0)