Custom Error classes with Apollo Server
Chris Held
Posted on July 18, 2023
Recently, I was tasked with upgrading an older version of apollo-server-express with the newer @apollo/server package. apollo-server-express is approaching end of life this year and we wanted to take advantage of all of the new features apollo had introduced in the last two years. While there were several differences involved in the upgrade, one particular change caught our attention—the removal of support for custom error classes that we had used extensively used in our application. The challenge we faced was to find a solution that allowed us to seamlessly transition to the new version without disrupting our existing users.
In the previous version, we handled unauthorized access much like the following code snippet:
if (!hasAccess) {
// Removed!
throw new ForbiddenError("You don't have access");
}
The recommendation from the apollo upgrade docs was to replace these instances with GraphQLError
, like so:
if (!hasAccess) {
throw new GraphQLError("You don't have access", extensions: { code: 'FORBIDDEN' });
}
While this technically resolved our problem, it felt cumbersome to add the extensions
property to all our old errors. However, we found a simple and elegant solution inspired by Apollo's previous approach: extending GraphQLError
to create our own custom errors.
To accomplish this, we defined our custom error classes as follows:
export class AuthenticationError extends GraphQLError {
constructor(message: string) {
super(message, {
extensions: {
code: 'UNAUTHENTICATED',
http: {
status: 401,
},
},
});
}
}
export class ForbiddenError extends GraphQLError {
constructor(message: string) {
super(message, {
extensions: {
code: 'FORBIDDEN',
http: {
status: 403,
},
},
});
}
}
Creating our own custom errors allows us to encapsulate setting and sending extensions
in one place, which ensures we’re consistent with how we’re responding to these errors. As an added bonus it gave us an opportunity to send down a 401 or 403 status code when throwing the appropriate error, which had been requested by a few of our clients.
This turned what could have been a lot of work into a really straightforward upgrade path. By simply updating the imports, we were able to resolve the majority of our issues while maintaining the same functionality. This meant that consumers of our API wouldn't need to make any changes to adapt to our system, ensuring a smooth transition.
Posted on July 18, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.