Here I have a resolver for coupon code redemption. It should throw an error when the code doesn't exist or has a claim. Apollo server provides a few errors we can use to communicate errors to a client.
import { OperationContext } from "../../../graphql/OperationContext"
import { assertUserId } from "../../../auth/assertUserId"
import * as appSumoCodesDb from "../db"
import { UserInputError } from "apollo-server-lambda"
interface Input {
code: string
}
export const redeemAppSumoCode = async (
_: any,
{ input: { code } }: { input: Input },
context: OperationContext
) => {
const userId = assertUserId(context)
const appSumoCode = await appSumoCodesDb.getAppSumoCodeById(code)
if (!appSumoCode || appSumoCode.userId !== userId) {
throw new UserInputError("Invalid code")
}
// ...
}
We can go to the source code and see that they all extend ApolloErro. So if we need a custom error, we can copy one of the classes from here and change the name with a code.
export class UserInputError extends ApolloError {
constructor(message: string, extensions?: Record<string, any>) {
super(message, "BAD_USER_INPUT", extensions)
Object.defineProperty(this, "name", { value: "UserInputError" })
}
}
Apollo Server always responds with 200, but if API threw an error, we'll receive errors with a response. I formatted the error to include only the message and extensions. If we want to provide extra information, we can add an object as a second parameter to ApolloError, and it will appear in the extensions object. To omit stack trace from extension, set NODE_ENV
to production.
const server = new ApolloServer({
typeDefs,
resolvers,
// ...
formatError: ({ message, extensions }) => {
return {
message,
extensions,
}
},
})
I run Apollo Server on AWS Lambda and use Sentry for error monitoring. Don't forget to pass ignoreSentryErrors, otherwise, your lambda will crash if you reach Sentry's quote.
exports.handler = Sentry.AWSLambda.wrapHandler(server.createHandler(), {
ignoreSentryErrors: true,
})