A tale of three Graphql clients

Yusinto Ngadiman
jsdownunder
Published in
4 min readOct 3, 2017

One project. Three Graphql clients.

Recently I was involved in a project which required real time information to be displayed on my react app. The app had no real time capabilities prior to this. The closest thing to anything real-time in the app was feature toggling, implemented using ld-redux and Launch Darkly’s js client. Under the hood, this uses server sent events which are like web sockets but the data flows in one direction only from the server to the clients.

My spider senses told me that graphql subscriptions was the perfect fit for implementing real time capabilities. The app is already using relay classic with my own graphql server, so it’s perfect! Right? Well.. not so fast.

Relay Classic is … classic. They should really have picked a better name for it like: Relay Outdated or Relay Complicated or Relay Life Sux. Quick googling reveals that the only option to get subscriptions working for relay classic is this npm package. It involves directly dealing with sockets, which is not the best/cleanest option.

Relay Modern

You must have heard of the Relay Modern which is the latest version of Relay with subscriptions support. It’s a much better version of Relay. Easy! Upgrade relay and implement subscriptions. Right? Well… again not so fast.

The upgrade from relay classic to modern is not a trivial upgrade. There are syntax changes. Routing is a problem because relay modern is incompatible with react router 4 due to query aggregation issues. No server side rendering for relay modern (in classic there are 3rd party packages which make this possible).

Apollo

I then began to consider the possibility of using Apollo alongside Relay Classic in the project. Two graphql clients? Really? For one thing the size of including the extra packages is a negative right? Secondly it feels like a hack, a new developer will look at the code and say “Wtf? Why are there 2 graphql clients?” It will be confusing to say the least.

Size is definitely a concern. I had to add the following packages to get apollo to work:

react-apollo
apollo-client
add-graphql-subscriptions
subscriptions-transport-ws

I build my app using webpack with BundleAnalyzerPlugin to inspect the sizes of these packages. In total, adding apollo adds about 25kb of overhead to the app (see below). This is not too bad, don’t forget if I don’t use apollo I would still need to add other packages to get subscriptions to work.

Package sizes to illustrate apollo’s overhead — total 25kb

To make it as least confusing as possible, I restrict the use of apollo to a single route only, the real time route. Apollo is setup at the root of this route only, so only this part of the app is touched. As illustrated in the gist below, only the ApolloLanding component has access to apollo.

Only apollo-path is set up with apollo

The ApolloLanding component looks something like this:

Example component to demonstrate a localised apollo route

Graphcool and graphql-request

That’s the story on the client side. On the server side, I use Graphcool to serve the subscriptions. The actual data gets inserted from an external service into graphcool via aws lambda. This lambda code is written in node, using a lightweight graphql client called graphql-request written by Johannes Schickling from graphcool.

The external service invokes this lambda at high frequency (e.g. once a minute) so the data in graphcool is updated also at high frequency which means that clients get frequent updates from the service. Where does graphql-request fit in this picture?

The lambda code that updates graphcool has to perform graphql mutations. Graphql mutations (like queries) are just http posts so it’s perfectly ok to use node-fetch or isomorphic-fetch like so:

Performing mutation using node-fetch or isomorphic-fetch

Or using graphql-request:

Mutations using graphql-request

I prefer graphql-request because it feels cleaner to me.

Conclusion

Having both relay and apollo in one project is definitely not the best way to go. However, given the time constraints I think it made sense at the time . I should further optimise the apollo landing route by using webpack dynamic imports. This ensures the component and its packages are not loaded until needed on the client side.

The long term solution is to upgrade relay to relay modern though. I want statically compiled queries! However there are challenges ahead. I need to solve server side rendering, so I think I have to look at pre-rendering instead.

Plus graphcool rocks! Check them out!

Thanks for reading.

--

--

Yusinto Ngadiman
jsdownunder

Tea lover. Frontend engineer at Qantas. Views and opinions are my own.