SyntaxStudy
Sign Up
GraphQL Resolver Middleware with graphql-middleware
GraphQL Beginner 1 min read

Resolver Middleware with graphql-middleware

graphql-middleware (graphql-shield) lets you apply cross-cutting concerns—authentication, authorization, logging, validation—as composable middleware layers that wrap resolvers without modifying them. Rules are defined as functions that return true (allow), false (deny), or throw an error. graphql-shield builds on graphql-middleware to provide a declarative permissions layer. Rules can be composed with and, or, and not combinators, and the allow/deny shorthand lets you define a blanket policy for a type with field-level overrides.
Example
// npm install graphql-middleware graphql-shield

import { applyMiddleware } from 'graphql-middleware';
import { shield, rule, and, or, not, allow, deny } from 'graphql-shield';
import { makeExecutableSchema } from '@graphql-tools/schema';

// Rules
const isAuthenticated = rule({ cache: 'contextual' })(
  async (parent, args, { currentUser }) => currentUser !== null
);

const isAdmin = rule({ cache: 'contextual' })(
  async (parent, args, { currentUser }) => currentUser?.role === 'ADMIN'
);

const isPostOwner = rule({ cache: 'strict' })(
  async (parent, { id }, { currentUser, db }) => {
    const post = await db.posts.findById(id);
    return post?.authorId === currentUser?.id;
  }
);

// Permissions matrix
const permissions = shield({
  Query: {
    me:       isAuthenticated,
    users:    isAdmin,
    post:     allow,            // public
  },
  Mutation: {
    createPost:  isAuthenticated,
    updatePost:  or(isAdmin, isPostOwner),
    deletePost:  or(isAdmin, isPostOwner),
    adminAction: and(isAuthenticated, isAdmin),
  },
});

// Apply middleware to schema
const baseSchema = makeExecutableSchema({ typeDefs, resolvers });
export const schema = applyMiddleware(baseSchema, permissions);