Photo by Austin Chan on Unsplash

For the past months, I have decided to build one of clients’ system using GraphQL and there are a lot of new concepts for someone who mainly worked on building RESTful interfaces. In this article, I am going to discuss some of my experiences and gotchas when starting with GraphQL.

Before we got into, I want to mention that, our system was built using Graphene Django in backend and React with Apollo Client in frontend. Additionally, I will be talking mainly about the environments instead of what GraphQL actually is.

The Positives: Frontend and Apollo

Connecting frontend to a GraphQL backend using Apollo Client was a very fluid and straightforward operation. There are two very powerful features of using Apollo — the link system and the in memory cache. Let’s talk about the link system first. Apollo’s link system allows integrating custom functionalities into the “Network Stack” of the frontend application. What I mean by “Network Stack?” Apollo Links live in Apollo world. They do not really interfere with the default flow of application. For instance, we were using OAuth2 to authorize the user with the authorization server. In OAuth2, after you receive the token, if the backend allows it, you can reclaim new tokens using a refresh token. When writing frontend code, if a backend responds with an authorization error (e.g “Token expired”), we need to make a separate request to the authorization server to retrieve a new access token using refresh token. In a typical frontend app you can set up this flow using promises that will look like it is just one request. However, with Apollo, I just created a link that performed refresh and resend the request to get the data across. When I added the new link, I did not need to make any changes to accommodate for the new functionality (yes, I added dialogs to show that the authorization failed but that is besides the point). The application just thought of it as it is loading longer. Secondly, the in memory cache of Apollo is very powerful. When you update something in the backend, given the ID, typename (I assume), and the new data, the in memory cache automatically updates the the query data for the changes. There were some other cases where I needed to refetch the data due to sorting from backend but that those issues can also be resolved by sorting the data in both backend and frontend.

The Negatives: Backend and Graphene

I was building this application using Graphene Django and considering how much I enjoyed using Django Rest Framework when building Django applications, Graphene w/ its Django extension really felt out of place. While researching GraphQL for the backend, I have noticed that GraphQL ecosystem is much stronger in NodeJS than in Python. There is a really good way to extend the GraphQL beyond its original functionality in NodeJS (according to blogs) while in Django / Python every bit of extension ends up being a hacky solution than a straightforward experience. After dealing with Graphene Django specifically, I ended up implementing most of my mutations using the base graphene mutation classes instead of using Django’s provided mutation classes. This was the biggest gripe of mine when using Python to implement a GraphQL backend. I think for our future projects, I will definitely be looking to use NodeJS to set up GraphQL API to experience it first hand.

GraphQL Error System

Since this is my first experience with GraphQL but the one thing that was the biggest leap from REST when getting into GraphQL for the first time was the error system. GraphQL errors being in arrays was is not something that is pleasant to work with. This applies to handling errors in frontend specifically. I am going to post a response snippet for errors to explain my point better:

{

"errors": [

{

"message": "User not found",

"path": ["singleUser"]

},

{

"message": "Organization not found",

"path": ["singleOrganization"]

},

{

"message": "Unauthorized",

"path": ["someProtectedContent"],

"extensions": { "status": 401 }

}

],

"allUsers": [...],

"singleUser": null,

"singleOrganization": null,

"someProtectedContent": null

}

Apollo stored these errors in graphQLErrors and I was able to access them fine. However, for any error that I wanted to find in the application, I had to loop through errors array then loop through the “path” array of each error to get the result that I was looking for.

In a RESTful environment, errors in an array are sometimes useful; however, when using REST, we tend to perform multiple request that have their own status codes, responses, and errors. So, errors being in array will still be grouped per request. I think in GraphQL, it would have been better if errors were in the following way:

"errors": [

"singleUser": {

"message": "User not found"

},

"singleOrganization": {

"message": "Organization not found",

},

"someProtecterContent": {

"message": "Unauthorized",

"extensions": { "status": 401 }

}

],

I know it is possible to change the format of errors in GraphQL but as a first timer, I did not want to stray away from standards too much. As time goes on, these kinds of issues and their solutions will be more clear; however, as a first timer in GraphQL, this is the only problem with GraphQL that stood out.

Was it worth it?

Absolutely! I believe that frontend development will always dictate on the best way to deal with APIs and GraphQL provides a more streamlined frontend development experience than REST interfaces. Therefore, for our next projects we will definitely be planning on using GraphQL.