6

Can someone help me understand how to throw authentication errors in a graphQL lambda app? I'm using graphQL-yoga with serverless and I can authenticate a request and either return a user that I get from the jwt, a {} for no token, or throw an authentication error if the token is old. When I throw an error it gets caught in the catch statement of my authenticate block, but I have no idea how to actually return that from the lambda.

const lambda = new GraphQLServerLambda({
  typeDefs,
  context: ({ event, context }) =>
    authenticate(event.headers.Authorization)
      .then(user => ({ db: mainDb, user}))
      .catch(e => {
        console.log('Caught the auth error here');
        throw e;
      }),
   Query: { \\ some queries here.... },
   Mutation: { \\ some mutations here...}
 });

How can I either format the error or throw it from the right spot so that I get an actual formatted error? Instead I get a Unexpected token I in JSON... error in the client. Clearly I need to do some sort of formatting during my throw but it isn't totally obvious to me how to do that.

If it is helpful, here in my exports part. I'm trying everything from try/catch to then/catch and at this point I have seemed to already miss catching the error. Is there a better way to be doing this? The main thing I need is the ability to either authenticate, reject bad tokens, and otherwise just return a {} for a non-logged in user. I'm having the hardest time finding custom authorizers that allow non-logged in users so that's why I am doing the auth directly in my graphQL endpoint

exports.server = (event, context, callback) => {
  try {
    return lambda
      .graphqlHandler(event, context, callback)
      .then(b => b)
      .catch(e => console.log(`can't catch it here ${e}`));
  } catch (e) {
    console.log('or here');
    callback(e);
  }
};
Coherent
  • 1,933
  • 5
  • 24
  • 33

2 Answers2

3

I finally got it! Here is the proper way to throw, catch, and format errors that are generated in a lambda function with serverless. The trick is to create a modified callback which gets passed into the graphQL Handler. When the function gets resolved, it will run through any code that you want.

Also, Seanvm was right that looking into the type of lambda function is important- serverless sets up a proxy-lambda function by default which necessitates formatting the output like I do below and passing null as the error.

exports.server = (event, context, callback) => {
  const modifiedCallback = (error, output) => {
    if (output.body.includes('401')) {
      callback(null, {
        statusCode: 401,
        headers: { 'Content-Type': 'application/javascript' },
        body: JSON.stringify({ message: 'Unauthorized' })
      });
    } else {
      callback(error, output);
    }
  };
  return lambda.graphqlHandler(event, context, modifiedCallback);
};
Coherent
  • 1,933
  • 5
  • 24
  • 33
2

One option to customize your error message would be to create a new instance of the Error class.

An example would be:

.catch(e => {
  console.log('Caught the auth error here');
  throw new Error('Authentication Failed');
 }),

Because the first parameter of the callback function is going to be the error message, you could also stick a generic error directly into the handler function:

 callback("An error happened!");

You can also use a middleware such as Middy to help with error handling:

https://github.com/middyjs/middy/blob/master/docs/middlewares.md#httperrorhandler

A helpful link on NodeJS error handling:

https://www.joyent.com/node-js/production/design/errors

Seanvm
  • 394
  • 1
  • 9
  • Thanks for the reply. I tried throwing errors and I can get a 500 error but nothing else. I am wondering if this might be due to a configuration issue with API gateway. When I use the middy I actually get a 200 code even if I am throwing an error, putting it in a middy callback (as a string, object, or stringified object). – Coherent May 10 '18 at 03:51
  • Are you receiving the custom message as a 500 error? Or is it a generic internal server error? If its the former, I believe you can set a custom error code using square brackets in your message like so: `callback("[418] Your error message");` If its the latter, then you might need to double check that whether you are using the lambda integration with or without proxy - https://stackoverflow.com/questions/42474264/lambda-integration-vs-lambda-proxy-pros-and-cons – Seanvm May 10 '18 at 17:02