“Client-side only” realtime web applications with Firebase, GraphQL and apollo-client 2.0

Pierre Criulanscy
Spotlight On JavaScript
9 min readSep 25, 2017

TL;DR When you need to work with GraphQL locally (you can’t or doesn’t want to have a GraphQL server) you can use apollo-link-webworker to use a WebWorker as the graphql “server” and even generate proper GraphQL subscriptions from external events source, like Firebase Realtime Database events. You can implement this yourself with the apollo-link-webworker package and you can test the final result here : https://firechat-169ea.firebaseapp.com/

The perfect stack for GraphQL project without GraphQL server

This article requires that you are already familiar with apollo-client 2.0, graphql, and firebase.

So you want to create an amazing web application with real time support and don’t want to struggle with server-side code / maintenance. Let’s say you want to build a realtime chat application and you heard about firebase realtime database. “Scalable real time syncinc for JSON data”, sounds good :

  • you don’t need a server, you can be focused on the front
  • you don’t need to manage the socket connections on the client, firebase does it for you
  • the Firebase Realtime Database Rules might not be the most robust way to handle permissions but it’s here, it works, and it’s quite simple to configure
  • the Firebase Authentication system lets you implement one of the authentication providers (Google, Twitter, Facebook, Github, or even your custom OAuth implementation) included in the SDK

Firebase sounds like a very good choice, it solves many backend issues but what about the frontend part ? You don’t want to struggle with state management, data fetching, caching, etc. You heard about GraphQL, it seems to perfectly suit your needs :

  • you can use a graphql client (relay, apollo, etc.) to handle all the fetching / caching issues
  • the UI decides what is needed to fetch, and only what is needed, no more overfetching / underfetching
  • you can mock your graphql schema during the early stage of development

Since you already know GraphQL, you’re maybe wondering where is the GraphQL server in this stack ? We do not want to manage any server, but the typical graphql stack involves a graphql client on the client side, talking to a graphql server (apollo server for example) :

An example of a typical GraphQL stack

How could we create a graphql “server” on the client side ? And what about realtime update ? How can we use firebase socket to generate some kind of graphql subscriptions ? Let’s see that :)

Using a Web Worker as the graphql “server” and subscriptions handler

In the typical graphql stack with apollo-client, apollo-server and a websocket server, here is how the realtime feature is achieved :

  • client A opens the chat application, a graphql subscription query is sent to the websocket server to subscribe this client to the pubsub channel that will receive the future results
  • client B posts a new message, a graphql mutation query is sent to the apollo-server, which in turns publishes the result in the pubsub channel mentionned above
  • the websocket server resolves the subscription and sends the data to all clients listening for changes in this channel
  • client A receives the new message

With Firebase, we do not own the websocket server used to communicate real time updates. So we need to generate the graphql subscription on the client side, in the webworker. In the next list, the webworker A refers to the webworker instanciated in the client A’s browser, and webworker B to the webworker instanciated in the client B’s browser.

  • client A opens the chat application, a graphql subscription query is sent to the webworker A to subscribe this client to the pubsub channel that will receive the future results. In the meantime, a socket is opened by the Firebase SDK between client A and the firebase realtime database
  • client B posts a new message, the graphql mutation is sent to the webworker B that resolves the mutation by pushing a new message in the firebase realtime database.
  • client A receives the update from firebase through the socket opened by firebase, this result is used to resolve the graphql subscription and send the resolved result to the correct pubsub channel, thus making the UI to update !

The good news is that the new apollo-client@2.0.0 beta version makes this easy to implement thanks to Apollo Links ! We are going to create a Link that, instead of communicating with a server, communicates with a webworker. Webworker are asynchronous, that’s perfectly fine for subscriptions but for “classical” server communication (queries, mutations, …) we do expect to receive a response via a promise, not via a delayed event. Here’s what we gonna need :

yarn add apollo-client@beta apollo-link@beta apollo-cache-inmemory@beta promise-worker firebase graphql-subscriptions subscriptions-transport-wsyarn add --dev worker-loader

The first packages are related to apollo-client@2.0.0, no more networkInterface is needed, instead you need an apollo-cache instance and an apollo-link instance. If you want to learn how to migrate, it’s here : What’s coming in Apollo Client 2.0
We also need the graphql-subscriptions package to use the PubSub object and the subscriptions-transport-ws package to use the SubscriptionClient.
The worker-loader is very usefull to load worker file through webpack.

Implementing the Apollo WebWorker Link

We want to communicate with our webworker via promises when operating a query or a mutation and we want to subscribe to a pubsub via the webworker when operating a subscription. So, let’s implement a PromiseWorkerLink and a SubscriptionWorkerLink. The final WebWorkerLink will be created from a “splitted” link, a way to conditionnaly use one or the other depending on a predicate (in our case if the operation is a subscription)

The PromiseWorkerLink constructor only takes one argument : a worker object (we will create this object later, keep on). The sole purpose of the request implementation is to pass to the PromiseWorker the current operation and return the response to simulate the client <> server communication.

That was the easy part, let’s see how to implement the SubscriptionWorkerLink now. The idea behind this link, is to use the SubscriptionClient exposed by the subscriptions-transport-ws package. This class is expected to be used with a socket server, through the socket implementation you want (Socket.io, websocket, etc.). You just pass the socket implementation you want to use as a third parameters of SubscriptionClient.
Let’s use this to implement a “fake” socket implementation that instead of communicating with a real socket server, communicates instead with the webworker. To do so, we must implement the send method, the onerror setter and the onmessage setter. We also have to define some static constant that are read during the process in the subscriptions-transport-ws package. Just for integrity reason, we also accept the same two parameters in the constructor url and protocol even if this is not relevant for us :

Let’s explain this a little bit :

  • The SubscriptionWorkerLink takes a worker as an argument (it’s the same worker object than before, we still do not know how to create this object, but keep on).
  • We then instanciate the SubscriptionClient. The first and second arguments are not relevant for us (the server url for the first, and the socket options for the second). The last argument is the implementation of our “webworker socket server”.
  • The request method only delegates the request to the subscriptionClient instance (internally, the client handles the subscriptions, incoming message and the result to send back).
  • The WorkerInterface just acts as a “fake” implementation of a socket server. The send method is the method called by the SubscriptionClient instance when it wants to send a message to the socket server, in our case we just have to post a message to the webworker instead.
  • The onmessage setter handles the subscription results. We have to check for the type of the received message data. SubscriptionClient set this to MessageType.GQL_DATA.

With these two links, we can generate the final link with ApolloLink.split:

Implementing the WebWorker file

The next step is to implement that worker object that we are talking about since the beginning. The webworker acts as the graphql “server”, it handles the execution of incoming requests. We can simulate the “server-like” communication by using promises. For the “socket server like” communication it’s “easy” : we just have to communicate through natural webworker events by publishing in the pubsub trough async iterator. The code below is extracted from my repository : apollo-link-webworker so if you don’t understand everything, it’s not a big deal (the code is mostly extracted from the subscriptions-transport-ws package from apollo) :

registerPromiseWorker registers the current worker as a promise worker, it enables the possibility to communicate with this worker through promises. We use this to simulate the client <> server communication.

The onmessage implementations is here to simulate the websocket client <> websocket server communication. Notice that this worker handles both roles : the worker is the “websocket client” and also is the “websocket server”.

When queries or mutations are handled, the execute from the graphql package is called, and when a subscription is handled, the subscribe method is used that does all the “magic” by resolving incoming request to real result through the graphql schema (we are going to talk about it soon).

Notice the self.postMessage on line 40. The message we are posting here is the result of the resolved subscription. This is then received in the WorkerInterface.onmessage implemented above to pass the result to the registered handler (the client cache update logic, from react-apollo for example, we’re going to see that later, keep on ;) ).

The pubsub is an instance of PubSub class from the graphql-subscriptions package :

The GraphQL part : schema, context, firebase models and connectors

Let’s put aside the implementation details of the webworker link and all the stuff we mentionned above to speak about our real application logic : the GraphqlQL schema, context, and firebase database communication !

For the rest of this post, I assume you’re already familiar with GraphQL, firebase, apollo-client and react.

The firebase database

Our firebase database modeling our chat application has this shape :

The firechat database shape

As you can see I keep it to the very minimum, a message has a content, id, and a userId field.
A user only hasid and username fields.

And this is the UI (yeah, I know…) :

The GraphQL schema

Nothing special to say here, if you’re familiar with graphql nothing should be scaring you right now. We’re gonna talk about the resolvers in a moment. The most interesting part here is the Subscription.messageAdded resolver where we use the asyncIterator on the pubsub instance, iterating over the OnMessageAdded channel results. OnMessageAdded will be the operation name we’re gonna use for our messageAdded subscription.

The models

Our models are a very thin layer that exposes an api used by the resolvers :

Let’s implement the firebaseConnector object. We use dataloader to save some bandwidth (it does get very important when you are on a paid plan because it escalates very quickly) :

The firebase connector

We export a simple createFirebaseConnector factory that just accepts an instance of the firebase realtime database object. Since we have access to the database object, we decide to link here the realtime update received from firebase to our subscription system through the pubsub instance.

The context

Tying it all together

Ok we now have all the necessary pieces, we must now tie it all together by instanciating an apollo-client instance :

And we’re done !

You can now use this apollo-client instance like you would with any other client instance ! Let’s see an example with the MessageInput component I used to handle the message submission in the UI screenshot and the MessagesList component that handles the message list and subscriptions to new messages :

MessageInput

MessagesList

The OnMessageAdded operation’s name is the name used through the whole pipe.

Introducing apollo-link-webworker

Since you don’t want to write all this boilerplate every time (and neither do I), I wrote a package, apollo-link-webworker,that exposes a createWebWorkerLink factory to build this Link, and some utility functions to help you build your own worker. It’s still in early alpha, not tested, not to be use in production, but contribution are very welcomed !

Example app with firebase authentication

You can see the chat application I’m talking about since the beginning of this post on this repo : firechat repository. The application is online here : https://firechat-169ea.firebaseapp.com/ (you’ll need to accept popup for the authentication to work properly)

--

--