3

We are trying to use custom error status codes with GraphQL so that our upstream services can better utilize the errors thrown.

We have a simple error class that extends the base Error class to include a status code:

export class ErrorWithStatusCode extends Error {
  public statusCode: number;

  constructor({
    message,
    name,
    statusCode,
  }: {
    message: string;
    name: string;
    statusCode: number;
  }) {
    super(message);

    this.name = name;
    this.statusCode = statusCode;
  }
}

export class BadRequestError extends ErrorWithStatusCode {
  constructor(message: string) {
    super({
      message,
      name: `BAD_REQUEST`,
      statusCode: 400,
    });
  }
}

And then we throw that error in our code like so:

if (existingResource) {
  throw new BadRequestError('An account with the email address already exists');
}

Inside our logs we see the Lambda invoke error:

2022-04-29T13:42:56.530Z    0e1688ac-89f1-46a7-b592-d6aeceb83fd7    ERROR   Invoke Error    
{
    "errorType": "BAD_REQUEST",
    "errorMessage": "An account with the email address already exists",
    "name": "BAD_REQUEST",
    "statusCode": 400,
    "stack": [
        "BAD_REQUEST: An account with the email address already exists",
        "    at Runtime.main [as handler] (/var/task/index.js:26908:19)",
        "    at processTicksAndRejections (internal/process/task_queues.js:95:5)"
    ]
}

Then in our VTL template, we just associate all properties outside of name and message to the custom errorInfo object as described here

$util.error(String, String, Object, Object) Throws a custom error. This can be used in request or response mapping templates if the template detects an error with the request or with the invocation result. Additionally, an errorType field, a data field, and a errorInfo field can be specified. The data value will be added to the corresponding error block inside errors in the GraphQL response. Note: data will be filtered based on the query selection set. The errorInfo value will be added to the corresponding error block inside errors in the GraphQL response. Note: errorInfo will NOT be filtered based on the query selection set.

#if (!$util.isNull($ctx.error))
  $utils.error($ctx.error.errorMessage, $ctx.error.name, $ctx.result, $ctx.error)
#end

$utils.toJson($ctx.result)

But when we get the error back from AppSync (using the console), we do not have any additional error properties:

{
  "data": null,
  "errors": [
    {
      "path": [
        "createResource"
      ],
      "data": null,
      "errorType": null,
      "errorInfo": {
        "message": "An account with the email address already exists",
        "type": "Lambda:Unhandled"
      },
      "locations": [
        {
          "line": 2,
          "column": 5,
          "sourceName": null
        }
      ],
      "message": "A custom error was thrown from a mapping template."
    }
  ]
}

Borduhh
  • 1,975
  • 2
  • 19
  • 33

0 Answers0