This story was moved to: https://jonathancardoso.com/en/blog/graphql-mutation-arguments-validation-with-yup-using-graphql-middleware/

Recently I found out about the awesome library by Prisma called graphql-middleware , which allows to run arbitrary code before / after your GraphQL resolvers are called.

In this post I will show how we can create a middleware to validate the input arguments of any GraphQL Mutation using Yup.

Initial Code

For this example, we are going to create a simple graphql-yoga server, with the following initial code:

Step 1: Adding the middleware

Let’s name our middleware yupValidation :

According to the docs, the middleware can be an object with the keys matching the types / fields they should be executed on. In our case, we are saying that we want to run our middleware on every mutation.

Adding it to the GraphQLServer instance:

const server = new GraphQLServer({

typeDefs,

resolvers,

middlewares: [yupMutationMiddleware()],

});

It currently does nothing interesting, we are just calling the mutation resolver and returning it. But see those console.log there? They can show us something interesting if we submit a mutation to this endpoint right now, let’s do it, send this mutation to the endpoint (you can use the playground that runs automatically at the same port than the server):

The following is going to be logged in the console:

The order is important here, your middleware is executed before the field resolve is called, and you can call the resolve function anytime inside your middleware function.

Step 2: Adding the validation schema

Let’s change the way we define our AddUser mutation, instead of having the resolve placed directly, let’s use an object. And inside that object, let’s also declare a validationSchema property, this is going to tell us how to validate the args of the mutation, and will be a Yup schema:

Do not forget the import * as yup from ‘yup’ at the top of the file

Step 3. Implementing the middleware

Great! We have everything setup, now we need to make it really work. 😅

The basic idea is to determine if the mutation being called was defined with a validationSchema property, if yes, use it to validate the args , and return errors if any were found.

But wait, how am I supposed to get this validationSchema property inside the middleware?!?

We are going to use the notably forgotten, but nonetheless important, info property. If you never used it or have no idea on what it is, there is an awesome post by Prisma demystifying the info argument.

The general idea is to use a hidden feature of graphql-js, that if you pass extra properties on the type definition of your GraphQL schema, they are going to be available on the info argument of the resolvers.

⚠ Notice: Like I said above, this relies on something not well documented, which probably may change in future versions of graphql-js and/or graphql-tools , so use at your own risk. I, personally, think this is pretty safe to use.

The following code will retrieve the mutation field like it was defined and passed to graphql-js:

const mutationField = info.schema.getMutationType().getFields()[info.fieldName];

info.schema.getMutationType() is self-explanatory, it returns the root type Mutation , then we use their getFields() method to retrieve all their fields, which is basically a hash with all the defined mutations, with each mutation name as key.

info.fieldName has the name of the current field resolver, since our middleware is only going to be run against mutations, we can be 100% certain that the getFields() called before is going to have a key with that fieldName .

Everything now should be pretty straightforward, we just need to verify if there is a validationSchema , and if yes, validate the args against it. If any error is found, we are going to return yup’s validation message as an error field in result of the mutation call, instead of calling the mutation resolve .

Too much talk, show me the code.

Here it goes:

Simple, right? And now, if for instance you call the mutation with a firstName that does not match the validation you declared:

Just in case you want something that works out of the box, I’ve created a package with this middleware, but with some improvements and tests, check it out: