Authorization in GraphQL — Complete Guide
In this tutorial, you will learn about Authorization in GraphQL. We cover key concepts, practical examples, and best practices to help you master this topic.
Authorization determines what authenticated users can do. In GraphQL, authorization applies at the field, type, and operation levels. Implement authorization through middleware, resolver guards, or schema directives based on your granularity needs.
What You'll Learn
- Role-based access control (RBAC) patterns
- Field-level authorization
- Authorization directives
- Scope and permission checking
- Data-level access rules (ownership)
Why It Matters
Authorization protects sensitive data and prevents unauthorized actions. Field-level authorization in GraphQL lets you expose the same schema to different user roles with different visible data.
Real-World Use
GitHub's GraphQL API checks Repository permissions on every query. A user can only see repository issues if they have read access. Shopify checks store ownership and app scope permissions.
flowchart TD
Request[Request + User Context] --> AuthCheck[Authorization Check]
AuthCheck --> RoleCheck{Role Check}
RoleCheck -->|Admin| FullAccess[Full Access]
RoleCheck -->|User| UserAccess[Field & Data Rules]
RoleCheck -->|Guest| PublicAccess[Public Fields Only]
UserAccess --> DataCheck{Ownership Check}
DataCheck -->|Own| Full
DataCheck -->|Other| Limited[Limited Fields]
Teacher Mindset
Authorization should be layered. Check operation-level permissions (can mutate?), field-level permissions (can see email?), and data-level permissions (owns this record?). Use directives for declarative authorization.
Code Examples
# Example 1: Authorization directive pattern
directive @auth(requires: Role!) on OBJECT | FIELD_DEFINITION
enum Role {
ADMIN
EDITOR
VIEWER
}
type User {
id: ID!
name: String!
email: String! @auth(requires: ADMIN)
role: Role!
}
type Query {
users: [User!]! @auth(requires: ADMIN)
me: User!
}
// Example 2: Field-level authorization in resolver
const resolvers = {
User: {
email: (parent, args, context) => {
if (context.user.role !== 'ADMIN' && context.user.id !== parent.id) {
return null;
}
return parent.email;
}
}
};
// Example 3: Data ownership check
const resolvers = {
Mutation: {
deleteBook: async (_, { id }, { user }) => {
const book = await db.books.findById(id);
if (!book) throw new Error('Not found');
if (book.authorId !== user.id && user.role !== 'ADMIN') {
throw new Error('Forbidden');
}
await db.books.delete(id);
return true;
}
}
};
Common Mistakes
- Implementing authorization only at the operation level and ignoring field access
- Forgetting to check data ownership for mutations on owned resources
- Hardcoding role checks instead of using configurable permission arrays
- Not handling authorization errors with clear, distinct error codes
- Mixing authentication and authorization logic in the same function
Practice
- Create an RBAC system with ADMIN, EDITOR, and VIEWER roles.
- Implement field-level authorization that hides user emails from non-admins.
- Add ownership checks to a deleteBook mutation.
- Create an authorization directive for schema-level protection.
- Challenge: Implement scope-based permissions where each role has a configurable set of allowed operations.
FAQ
Mini Project
Add a complete authorization system to your library API. Create roles (ADMIN, EDITOR, VIEWER). Restrict book creation to EDITOR and ADMIN. Hide book revenue data from VIEWER. Allow authors to edit only their own books.
What's Next
Next, you will learn about DataLoader for solving the N+1 Problem in GraphQL resolvers.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro