GraphQL resolver middleware

 by Robin Wieruch
 - Edit this Post

GraphQL resolvers are used to resolve GraphQL queries to actual data. In this GraphQL tutorial, you will learn how to set up a GraphQL middleware for these resolvers for dealing with authorization and permissions. The following code snippet shows a GraphQL resolver with arguments that creates a new entity in a database.

export default {
Mutation: {
createFreeCourse: async (
parent,
{ courseId, bundleId },
{ me }
) => {
await createCourse({
uid: me.uid,
courseId,
bundleId,
amount: 0,
paymentType: 'FREE',
});
return true;
},
},
};

In this scenario, a user creates a course with a GraphQL mutation called createFreeCourse. It takes some arguments from the resolver's function arguments and also the user itself from the resolver's context. Now, if a user isn't authenticated, it shouldn't be possible to access the database:

export default {
Mutation: {
createFreeCourse: async (
parent,
{ courseId, bundleId },
{ me }
) => {
if (!me) {
return new Error('Not authenticated as user.');
}
await createCourse({
uid: me.uid,
courseId,
bundleId,
amount: 0,
paymentType: 'FREE',
});
return true;
},
},
};

This authorization check happens quite some time for a larger GraphQL server with lots of resolvers. In order to get rid of this manual work, we can write a middleware function with the graphql-resolvers package for this and all other resolvers in another file:

import { skip } from 'graphql-resolvers';
export const isAuthenticated = (parent, args, { me }) =>
me ? skip : new Error('Not authenticated as user.');

After all, this middleware function is just another GraphQL resolver. We can import it in our previous resolver and combine it with the graphql-resolvers package to one protected resolver (also called guarded resolver):

import { combineResolvers } from 'graphql-resolvers';
import { isAuthenticated } from './middleware/isAuthenticated';
export default {
Mutation: {
createFreeCourse: combine(
isAuthenticated,
async (parent, { courseId, bundleId }, { me }) => {
await createCourse({
uid: me.uid,
courseId,
bundleId,
amount: 0,
paymentType: 'FREE',
});
return true;
}
),
},
};

Every time this GraphQL resolver runs, it does the authentication check before running the actual resolver. Let's take this one step further with another permission check. First, define another resolver middleware function:

import { skip } from 'graphql-resolvers';
export const isFreeCourse = (parent, { courseId, bundleId }) => {
const price = getPrice(courseId, bundleId);
return price === 0
? skip
: new Error('This course is not for free.');
};

And second, use it for your actual resolver:

import { combineResolvers } from 'graphql-resolvers';
import { isAuthenticated } from './middleware/isAuthenticated';
import { isFreeCourse } from './middleware/isFreeCourse';
export default {
Mutation: {
createFreeCourse: combine(
isAuthenticated,
isFreeCourse,
async (parent, { courseId, bundleId }, { me }) => {
await createCourse({
uid: me.uid,
courseId,
bundleId,
amount: 0,
paymentType: 'FREE',
});
return true;
}
),
},
};

As you can see, it doesn't end with two combined resolvers. You can add more onto the stack for a more elaborate permission and authorization handling. In addition, you can test them as standalone or combined resolvers.

Keep reading about 

We will test the following GraphQL resolver which has authorization and permission checks in place. If the user isn't authenticated, the resolver returns an error. If the requirements for the database…

Before we start to build full-fledged GraphQL applications, on the client- and server-side, let's explore GraphQL with the tools we have installed in the previous sections. You can either use GraphiQL…

The Road to React

Learn React by building real world applications. No setup configuration. No tooling. Plain React in 200+ pages of learning material. Learn React like 50.000+ readers.

Get it on Amazon.