The open-source community thrives on collaboration, feedback and support. As a community, we continue to enhance and improve GraphQL Hooks, a super lightweight GraphQL client for React with first-class support for hooks. So far we’ve had contributions to GraphQL Hooks from 24 individuals – you can see the full list here.

GraphQL Hooks supports custom cache plugins, server-side rendering and requires minimal configuration to get up and running quickly. And now, we’re delighted to announce a new addition to the library – subscriptions support! It’s available from version 4.2.0.

GraphQL subscriptions are an operation against your schema, like queries and mutations. You can think of them like queries that get called in response to an event. To show you how to use the new useSubcription hook, we’ve built an example chat application, North Pole Hotline.

The following key features of a chat application are a perfect use case for GraphQL subscriptions:

  • When someone else sends a message, it is added to my messages.
  • When a new user joins the chat, I am notified.

Sample Use Case: North Pole Hotline

This is a fun holiday-inspired real-time chat application. It is perfect for the festive period where you can secretly discuss gift ideas for that special someone. Anyone can visit the site,  create a chat room and invite others to join. Each chat room is isolated and only those who you share the link with can access it. Here’s how it looks:

Introducing GraphQL Subscriptions Support

Enter your name, and click ‘CHAT NOW’ to get started and invite someone to chat!

North Pole Hotline Details

Here are some of the design features of our chat app.

We’re going to use Hasura for our backend – it automatically creates a GraphQL API based off a PostgreSQL database. Best of all, it has subscription support out of the box, so you don’t need to worry about that headache; win-win.

First, here’s how we’ll model the data:

Data Model

To subscribe to new messages added to a chat, there are two general approaches:

  1. Subscribe to all the messages as they get updated, replacing the current list.
  2. Query for all the messages, then subscribe to only new messages.

The first approach is simple and we achieve it with the following query:

subscription LatestMessages($chatId: uuid!) {
  messages(where: {chat_id: { _eq: $chatId}}, order_by: {created_at: asc}) {
    id
    body
    user {
      id
      name
    }
    created_at
  }
}

However, it is not ideal to constantly download all the messages, especially if there are quite a few.

For the second approach, we query the messages and store them in React state – the subscription selection is identical to the first approach but in this case, we change a few parameters to ensure we only get the latest message by applying the following logic:

  1. Order the messages from newest to oldest
  2. Limit the results to 1
  3. Where the created_at is greater than now.

We achieve this with the following query:

# INITIAL_MESSAGES_QUERY
query InitialMessages($chatId: uuid!) {
  messages(where: {chat_id: { _eq: $chatId}}, order_by: { created_at: asc }) {
    id
    body
    user {
      id
      name
    }
    created_at
  }
}

# LATEST_MESSAGE_SUBSCRIPTION
subscription LatestMessage($chatId: uuid!, $now: timestamptz!) {
  messages(where: {chat_id: {_eq: $chatId}, created_at: {_gt: $now}}, order_by: {created_at: desc}, limit: 1) {
    id
    body
    user {
      id
      name
    }
    created_at
  }
}

Now we need to tie these together with some React state and Graphql Hooks:

import React, { useState, useEffect } from "react";
import { useParams } from "react-router-dom";
import { useManualQuery, useSubscription } from "graphql-hooks";

function ChatPage() {
  const { id: chatId } = useParams();
  const [error, setError] = useState();
  const [allMessages, setAllMessages] = useState([]);
  const [fetchInitialMessages] = useManualQuery(INITIAL_MESSAGES_QUERY, {
    variables: {
      chatId
    }
  });

  // onMount, we fetch the initial messages
  useEffect(() => {
    fetchInitialMessages()
      .then(({ data, error }) => {
        if (error) {
          setError(error);
          return;
        }

        setAllMessages(data.messages);
      })
      .catch(error => setError(error));
  }, [fetchInitialMessages]);

  useSubscription(
    {
      query: LATEST_MESSAGE_SUBSCRIPTION,
      variables: {
        chatId
      }
    },
    ({ data, error }) => {
      if (error) {
        setError(error);
        return;
      }

      setAllMessages([...allMessages, ...data.messages]);
    }
  );

  if (error) {
    // render error
  }

  // render messages
}

The North Pole Hotline uses the second approach described above. Therefore, the application displays only the latest message.

Want to join in or dive deeper?

The app is available at https://north-pole-hotline.herokuapp.com/ if you want to play around with it.

If you want to dive into the code and dig a bit deeper you can visit the Github repository. We also have a stripped back subscription example using Fastify within our examples folder.

A special thanks go to Peter Balazs and Matteo Collina for their work on getting subscriptions support for both graphql-hooks and fastify-gql.

View all posts  |  Technology  |  Business  |  Culture  |  Opinion  |  Design
Follow us for more information on this and other topics.
Published by Brian Mullan