Experiences With GraphQL

A couple of months ago I started working on a side project called TV Chat. My goal with this project was to create a website where people online could come together to chat about tv shows as they were airing live on TV. I'm a big tv watcher, and I like watching TV shows with friends and family. However my tastes don't always align with theirs. So my hope was to create TV Chat so that I could chat with other fans of the show as it was airing!

Alas, the project did not come to complete fruition. As I started thinking about the cost to maintain the chatrooms and worrying about how to monetize the site so it could pay for itself, I got a little overwhelmed with the prospect of actually releasing it as a finished product and decided it would be best to put it so hard on the back burner that it's for all intents and purposes dead.

However I did code a lot when writing this app. I used it as a learning experience to create a GraphQL server with Apollo, and I even went so far to use their experimental support for GraphQL subscriptions (which turned out to be a mistake, more on that later). I learned a lot about GraphQL in this process. Things that I found worked great, and areas of difficulty I didn't expect.

Rather than have all that code go stale sitting on my laptop I figured it'd be better for it to grow old on my GitHub profile. Perhaps mature like a fine wine. Or even better, uphold the motto of 'one man's trash is another man's treasure' and maybe someone picks up development and actually polishes it into a finished product!

Enough jibber jabber. The source code is available here. An online working example is available here.

What follows are my experiences while creating a GraphQL powered React application.

GraphQL

The sad conclusion I arrived at when writing my GraphQL server was that GraphQL is not the panacea I thought it was. Before actually implementing my own GraphQL server I had just done a lot of reading about it. All the features it advertised sounded incredible, especially its ability to format responses in a way that is immediately consumable by a front-end. I wanted the ability to create a server whose schema mirrored the way in which it will be used in the front-end, and this promise was one that made me positively giddy with excitement.

For the most part that worked out fine. Learning the schema language of GraphQL was not difficult. It's fairly small, and after you learn object types and what a query and mutation is you've learned enough to immediately begin using your GraphQL server. That was pretty exciting to me, as it fulfilled the promise I was hoping for.

Things get a little more tricky when you delve into some more advanced GraphQL schema types like enums and union types. I think something that made it trickier for me to understand how to take advantage of these features was by using the graphql-tag package that let me directly write schema without having to manually write the underlying code that was being auto-generated by that package. Had I started more low level I think I would have been a little less lost.

The biggest gotchya that I experienced when writing my GraphQL server was being unaware of the cascade of DB hits that can occur if you don't use use a caching layer such as DataLoader.

Let me try and explain what I mean by that.

For the TV Chat app I have a mongo collection called chatMessages. This collection contains all the messages that are created in a chat room. The schema for the items in this collection is

{
    "user": ObjectId(),
    "show": ObjectId(),
    "message": string,
    "timestamp": Date
}

So when a user loads a chatroom, I fetch down the last 50 items in the chatMessages collection, doing additional lookups to get the full user object so I can display a user's username.

However in a chatroom an individual user may have written more than one chat message (surprising, no?), which means that my naive implementation of this GraphQL server looked up each user every time they occur in the collection. That's not exactly web scale.

For all I could tell the main suggested way to fix this was to use DataLoader as an intermediary between schema resolvers and requests to the database.

I didn't take the time to delve into DataLoader but this gotchya was one that soured me a little to GraphQL.

Don't get me wrong, I still think GraphQL has a lot of advantages over REST. However this example did illustrate a way in which GraphQL can be a little tricker to implement than a REST server.

All that is to say that it gave me a renewed apprehension to GraphQL and enforced the notion of noble old Ben that 'with great power comes great responsibility'.

However if you were to ask me, would I use GraphQL again? I would without a doubt.

Subscriptions

Since this was a chatroom application I needed to support real time updates in the UI. The best way to support real time is via WebSockets. Luckily for me experimental support for subscriptions in GraphQL had just been released, adding a way to synchronize data with a GraphQL server via WebSockets. Hurray!

However that's about where the fun stopped for me. No fault to the Apollo team, but I had a heck of a time getting my GraphQL Subscriptions working in a way that I liked. It took a lot of time reading the source code and looking at examples before I was able to piece together a solution that worked the way I wanted. However I find the resulting code to be very brittle and not intuitive at all, two things that I had not come to expect with GraphQL.

I think I would have been better served if I had just went with a vanilla WebSocket solution. Adding the higher abstractions of GraphQL was fun but difficult. It's code that every time I look at, it takes me a while to get my head back into understanding what is going on.

React

Using React with React Apollo was a lot of fun.

I found it very easy to attach my components to GraphQL requests. However I did have to learn to let go a little bit. I'm used to having explicit control over what HTTP requests are being made, but with React Apollo I had to let go. I had to trust the framework to make the requests that I wanted, when I wanted, and let go of my typical desire to control every facet of the stack. That was a little bit unnerving but I found React Apollo to behave as intuitively as I could expect.

I also found it great that the Apollo Client uses Redux underneath the hood. I love Redux and having that familiarity there gave me a sense of security and stability that was very welcomed. Using the Redux dev tools to inspect what was going on was fun to peek at, but ultimately something that I didn't find myself doing that often as most of the time Apollo just worked.

Conclusion

I had a lot of fun building my first full stack GraphQL implementation.

The server was definitely tricker to implement than the client. I don't think I've found a server implementation that I'm fully happy with yet. I need a few iterations to fully explore the full feature-set of GraphQL but having this as a first introduction was a great experience.

On the client GraphQL lived up to most of my expectations. It works great with React and React Apollo makes it a joy to integrate throughout my application. Even more exciting for me was seeing that it would be pretty easy to incrementally adopt GraphQL on the client.

GraphQL is a really cool piece of technology. I think we're at the height of hype right now and there are some deeper learnings that need to be experienced and shared before I'd expect to see it get wide adoption. But after playing around with it for a couple of months consider me a fan.