What is a realtime update?

When data gets updated in the source (database, service) and that change gets instantly propagated to the other side (browser, app, or any other client), that is considered a realtime update. Sending and receiving messages in a chat app, getting push notifications or tracking stock market prices are examples of realtime updates.

Realtime updates can be achieved with:

  • Polling
  • Server-Sent Events
  • WebSockets

Polling is when the client sends requests to the server periodically, to check for new data. There are two types of polling: Short Polling and Long PollingShort polling is an AJAX-based timer that sends requests at fixed delays. It is simple, not server consuming, but the downside is that data is not returned when the event actually happens. Long polling is Comet based. It is more complex and server consuming, but you are notified when the event happens with no delay.

Server-Sent Events is when the client automatically receives updates from the server via an HTTP connection. The client does not ask the server for updates. SSE connections can only push data to the browser.

WebSocket is a persistent connection between the client and the server. This is a communications protocol providing full-duplex communication channels over a single TCP connection. WebSockets connections can both send data to the browser and receive data from the browser.

Polling vs SSE vs WebSocket

Polling vs SSE vs WebSocket

There are hundreds of realtime application frameworks out there but some of the popular ones which can be used are:

  • Socket.io
  • Firebase
  • PubNub
  • Fanout
  • Pusher
  • Realtime.co

One of the possible solutions, based on GraphQL is HasuraHasura GraphQL Engine is a fast GraphQL server that gives you instant, realtime GraphQL APIs over Postgres.

First of all, what is GraphQL?

GraphQL

GraphQL is a query language that describes how a client should request information from an API. Simply put, GraphQL lets us specify in the request what data we want to be returned in the response.

A common problem with REST is over and under fetching. The problem is difficult to resolve because of the way REST works. The only way for the client to get data is by using endpoints that return a fixed data structure. It’s very difficult to design the API in a way that it’s able to provide clients with their exact data needs.

GraphQL provides an easy way to structure the needed data. The query to fetch products with only product id and product name looks like this:

"`
query {
  product {
    id
    name
  }
}
"`

Response:

"`
{
  "data": {
    "product": [
      {
        "id": "e5666a64-ee61-41b7-bda1-a0e7754becfc",
        "name": "Chromebook"
      },
      {
        "id": "ae3f6a2a-867c-4364-af61-a1d2991888f4",
        "name": "Dell"
      }
    ]
  }
}
"`

GraphQL is optimized for performance and flexibility while REST is focused on keeping the service’s reliability. GraphQL lets you pick the fields you want to get in response, so the request will always be the smallest possible. Additionally, you can inquire about multiple entities in one request.

GraphQL features are great, but is it worth the complete backend rewrite? Initially I thought it wouldn’t be a viable option. But then I saw that it doesn’t have to be that way. It’s possible to add to an existing, live Postgres database. This way we only need to adapt some parts of the application.

Hasura

Given a Postgres database, the Hasura GraphQL engine can automatically generate a GraphQL schema and process GraphQL queries, subscriptions, and mutations. It automatically generates GraphQL schema components when you track a Postgres table/view in Hasura and create relationships between them.

The Hasura GraphQL Engine fronts a Postgres database instance and can accept GraphQL requests from your client apps. It can be configured to work with your existing auth system and can handle access control using field-level rules with dynamic variables from your auth system.

Hasura is optimized for low memory footprint & latency. On the Heroku free tier, it consumes ~50MB of RAM even while serving more than ~1000 requests/per second.

Demo

Let’s assume your backend application is deployed and running on Heroku. In case you don’t have an existing project, I have created a small demo project as an example that you can clone from here. After cloning the project set the database configuration and deploy it to Heroku.

After that we need to create and deploy the new Hasura application. Hasura offers a quick and easy way to deploy to Heroku. Click here.

After configuration, the application screen should look similar to this:

Initial screen

Initial screen

This is GraphiQL interface, a tool that helps you structure GraphQL queries. As we can see, all available tables are listed in the explorer. In my project example I only have one table — projects, so it is the only one shown.

Now, all we have to do is to create a subscription. A subscription is essentially a query where the client receives an event whenever the value of any field changes upstream. Let’s say we only want id and product name.

subscription {
  product {
    id
    name
  }
}

My database contains only two products at this time.

{
  "data": {
    "product": [
      {
        "id": "e5666a64-ee61-41b7-bda1-a0e7754becfc",
        "name": "Chromebook"
      },
      {
        "id": "ae3f6a2a-867c-4364-af61-a1d2991888f4",
        "name": "Dell"
      }
    ]
  }
}

Now, after each update to the projects table, changes will automatically be reflected.

Let’s do a mutation query to add a new record to the database.

mutation {
  insert_product(objects: [{
    name: "Macbook Pro"
  }]) {
    affected_rows
  }
}

We specified that we want to see the number of affected rows in the response, so the response looks like this:

{
  "data": {
    "insert_product": {
      "affected_rows": 1
    }
  }
}

Now, the product’s subscription response is automatically updated over a WebSocket protocol.

{
  "data": {
    "product": [
      {
        "id": "e5666a64-ee61-41b7-bda1-a0e7754becfc",
        "name": "Chromebook"
      },
      {
        "id": "ae3f6a2a-867c-4364-af61-a1d2991888f4",
        "name": "Dell"
      },
      {
        "id": "504a9fdd-e585-4f7c-8d2a-a67f58851635",
        "name": "Macbook Pro"
      }
    ]
  }
}

The only thing left to do now is to include these queries in your frontend application. For that, the Apollo library can be used.

Leave a Reply