GraphQL Middleware

6.1.35 · maintenance · verified Wed Apr 22

graphql-middleware is a schema wrapper designed to allow developers to compose reusable middleware functions around GraphQL resolvers. This utility enables the execution of arbitrary code both before and after a resolver is invoked, facilitating tasks such as argument modification, result transformation, logging, authentication, and error handling. The current stable version, 6.1.35, demonstrates ongoing maintenance with recent updates focused on bug fixes and dependency compatibility, particularly with newer GraphQL versions. Its release cadence is primarily driven by these maintenance needs rather than frequent new feature introductions. Key differentiators include its intuitive API, which offers complete control over the resolver lifecycle, and its broad compatibility with any standard GraphQL schema, integrating seamlessly with popular GraphQL server implementations like Apollo Server. This library promotes a clear separation of concerns, improving code structure by centralizing cross-cutting logic that would otherwise be duplicated across multiple resolvers. Developers can define middleware at various levels, from global application to specific fields, following an an "onion"-like execution principle.

Common errors

Warnings

Install

Imports

Quickstart

Demonstrates how to apply both function-based and object-based middleware to an executable GraphQL schema using Apollo Server, showcasing the 'onion' execution principle and resolver modification capabilities.

import { ApolloServer } from 'apollo-server';
import { makeExecutableSchema } from '@graphql-tools/schema';
import { applyMiddleware, IMiddlewareFunction, IMiddleware } from 'graphql-middleware';

const typeDefs = `
  type Query {
    hello(name: String): String
    bye(name: String): String
  }
`;

const resolvers = {
  Query: {
    hello: (root: any, args: { name?: string }, context: any, info: any) => {
      console.log(`3. resolver: hello`);
      return `Hello ${args.name ? args.name : 'world'}!`;
    },
    bye: (root: any, args: { name?: string }, context: any, info: any) => {
      console.log(`3. resolver: bye`);
      return `Bye ${args.name ? args.name : 'world'}!`;
    },
  },
};

// Function-based middleware for logging input and result
const logInput: IMiddlewareFunction = async (resolve, root, args, context, info) => {
  console.log(`1. logInput: ${JSON.stringify(args)}`);
  const result = await resolve(root, args, context, info);
  console.log(`5. logInput`);
  return result;
};

const logResult: IMiddlewareFunction = async (resolve, root, args, context, info) => {
  console.log(`2. logResult`);
  const result = await resolve(root, args, context, info);
  console.log(`4. logResult: ${JSON.stringify(result)}`);
  return result;
};

const schema = makeExecutableSchema({ typeDefs, resolvers });

const schemaWithFunctionMiddleware = applyMiddleware(schema, logInput, logResult);

// Object-based middleware for modifying arguments and results on specific fields
const beepMiddleware: IMiddleware = {
  Query: {
    hello: async (resolve, parent, args: { name?: string }, context, info) => {
      // Override arguments
      const argsWithDefault = { name: 'Bob', ...args };
      const result = await resolve(parent, argsWithDefault, context, info);
      // Modify returned value
      return (result as string).replace(/Trump/g, 'beep');
    },
  },
};

const finalSchema = applyMiddleware(schemaWithFunctionMiddleware, beepMiddleware);

const server = new ApolloServer({
  schema: finalSchema,
});

async function startServer() {
  const { url } = await server.listen({ port: 8008 });
  console.log(`🚀 Server ready at ${url}`);
  console.log('Test queries:');
  console.log('  query { hello(name: "World") }  // Expects "Hello World!" with logging');
  console.log('  query { bye }                   // Expects "Bye world!" with logging');
  console.log('  query { hello(name: "Trump") }  // Expects "Hello beep!" due to middleware');
}

startServer();

view raw JSON →