Spektor?.dev

GraphQL Resolvers Middleware and Apollo Server Plugins

April 04, 2020

GraphQL Logo
+

I recently came across a problem where I needed to perform some custom logic on the result of a GraphQL query before it’s returned to the client and now I’d like to share some of the possible solutions in Apollo Server and graphql-compose.

It’s important to understand whether resolver middleware can be used for the specific task or you will need Apollo Server-level middleware. Keep in mind that resolver middleware is run per resolver while some Apollo Server plugins will be run on the final result before it is sent back to the client. You can learn more on when resolver middleware will not be of help in my previous post.

Table of Contents

  1. Resolver middleware
  2. Response middleware in Apollo Server

Resolver middleware:

  1. In graphql-compose you can use wrapResolve helper as follows:
import { schemaComposer } from "graphql-compose"
schemaComposer.Query.addFields({
  someResolver: someResolverFunction.wrapResolve(next => async rp => {
    // do something before the result
    const result = await next(rp)
    // do something after the result
    return result
  }),
})
  1. You can use applyMiddleware utility from graphql-middleware from Prisma Labs like so:
import { applyMiddleware } from "graphql-middleware"
const { ApolloServer } = require("apollo-server-express")
import { schema } from "./someSchema"
const someMiddleware = {
  Query: {
    someResolver: async (resolve, parent, args, context, info) => {
      // do something before the result
      const result = await resolve(parent, args, context, info)
      // do something after the result
      return result
    },
  },
}

const server = new ApolloServer({
  schema: applyMiddleware(schema, userReviewsMiddleware),
  ...otherOptions,
})
  1. willResolveField which comes from graphql-extensions. I couldn’t find any documentation for this option but it exists. Better name for the extension would be didResolveField since it’s called after the field was actually resolved. This is how you can enable it:
const { ApolloServer } = require("apollo-server-express")
const server = new ApolloServer({
  extensions: [
    function middleware() {
      return {
        willResolveField(source, args, context, info) {
          return (error, result) => {
            if (error) {
              // do something
            }
            // do something
            return result
          }
        },
      }
    },
  ],
  ...otherOptions,
})

Response middleware in Apollo Server:

  1. The easiest option which requires minimal configuration is formatResponse which can be enabled like so:
const { ApolloServer } = require("apollo-server-express")
const server = new ApolloServer({
  formatResponse: (result, ctx) => {
    // do something
    return result
  },
  ...otherOptions,
})
  1. Apollo uses graphql-extensions package to allow us to hook into many stages of request execution via plugins. There’s nice documentation on this but some options are only mentioned here. It’s important to understand that request execution flow starts with requestDidStart so even if you’re interested in other plugins you still must use requestDidStart to hook into the flow. Below is an example of willSendResponse where you can change the response:
const { ApolloServer } = require("apollo-server-express")
const server = new ApolloServer({
  plugins: [
    {
      requestDidStart(request) {
        return {
          willSendResponse(result, context) {
            // do something
            return {
              graphqlResponse: result,
              context,
            }
          },
        }
      },
    },
  ],
  ...otherOptions,
})

In case you’re using willSendResponse and formatResponse simultaneously it’s worth noting that formatResponse is executed before willSendResponse.