Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set http status code in GraphQL

I want to set an http status code in my GraphQL authentication query, depending on if auth attempt was successful (200), unauthorised (401) or missing parameters (422).

I am using Koa and Apollo and have configured my server like so:

const graphqlKoaMiddleware = graphqlKoa(ctx => {
  return ({
    schema,
    formatError: (err) => ({ message: err.message, status: err.status }),
    context: {
      stationConnector: new StationConnector(),
      passengerTypeConnector: new PassengerTypeConnector(),
      authConnector: new AuthConnector(),
      cookies: ctx.cookies
    }
  })
})

router.post("/graphql", graphqlKoaMiddleware)

As you can see, I have set my formatError to return a message and status but currently only the message is getting returned. The error message comes from the error that I throw in my resolver function.

For example:

const resolvers = {
  Query: {
    me: async (obj, {username, password}, ctx) => {
      try {
        return await ctx.authConnector.getUser(ctx.cookies)  
      }catch(err){
        throw new Error(`Could not get user: ${err}`);
      }
    }
  }
}

My only issue with this method is it is setting the status code in the error message and not actually updating the response object.

Does GraphQL require a 200 response even for failed queries / mutations or can I some how update the response objects status code? If not, How do I set the aforementioned error object status code?

like image 618
Stretch0 Avatar asked Aug 22 '26 22:08

Stretch0


2 Answers

Unless the GraphQL request itself is malformed, GraphQL will return a 200 status code, even if an error is thrown inside one of the resolvers. This is by design so there's not really a way to configure Apollo server to change this behavior.

That said, you could easily wire up your own middleware. You can import the runHttpQuery function that the Apollo middleware uses under the hood. In fact, you could pretty much copy the source code and just modify it to suit your needs:

const graphqlMiddleware = options => {
  return (req, res, next) => {
    runHttpQuery([req, res], {
      method: req.method,
      options: options,
      query: req.method === 'POST' ? req.body : req.query,
    }).then((gqlResponse) => {
      res.setHeader('Content-Type', 'application/json')

      // parse the response for errors and set status code if needed

      res.write(gqlResponse)
      res.end()
      next()
    }, (error) => {
      if ( 'HttpQueryError' !== error.name ) {
        return next(error)
      }

      if ( error.headers ) {
        Object.keys(error.headers).forEach((header) => {
          res.setHeader(header, error.headers[header])
        })
      }

      res.statusCode = error.statusCode
      res.write(error.message)
      res.end()
      next(false)
    })
  }
}
like image 157
Daniel Rearden Avatar answered Aug 25 '26 12:08

Daniel Rearden


For apollo-server, install the apollo-server-errors package. For authentication errors,

import { AuthenticationError } from "apollo-server-errors";

Then, in your resolver throw new AuthenticationError('unknown user');

This will return a 400 status code.

Read more about this topic in this blog

like image 25
larsivi Avatar answered Aug 25 '26 10:08

larsivi