DEV Community

Vladimir Novick for Hasura

Posted on

Create a real time web app with Hasura and Gatsby

A bit more than a month ago we live streamed how to create Gatsby blog with Hasura. This blog post summarizes it. If you haven't watched this video as well as other videos we have on Hasura Youtube channel I strongly suggest to check it out

What we were creating.

The idea was to create a simple blog powered by GraphQL backend. For backend we chose Hasura because lots of reasons I layed out in this blog post:

We started by deploying Hasura to Heroku and creating bunch of tables, connecting them with foreign keys and immediately we had queries subscriptions and mutations generated for us by Hasura. We had some weird behaviors though. Columns were dropping, fields were changing. It was kinda weird at first and then we realized that there were people trolling with us. Because endpoint was not secured and console was enabled, people were able to connect to it and change schema.

When we realized that we secured an endpoint by using HASURA_GRAPHQL_ADMIN_SECRET environment variable which we tried to keep secret from our audience. Finally everything was stable and people just could not troll us.

Creating gatsby site

We created our gatsby site by running
gatsby new . gatsbyjs/gatsby-starter-hello-world

to get very basic setup.

Integrating Hasura GraphQL into Gastby

We installed gatsby-source-graphql and configured it to work with HASURA_GRAPHQL_ADMIN_SECRET, but in real world scenario we will have Authorization bearer token, so our gatsby.config will look like this:

const fetch = require("isomorphic-fetch")
const createHttpLink = require("apollo-link-http")


/// Rest of the code

{
  resolve: "gatsby-source-graphql",
  options: {
    typeName: "hasura",
    fieldName: "blog",
    // Create Apollo Link manually. Can return a Promise.
    createLink: () => {
     return createHttpLink({
       uri: 'hasura graphql endpoint',
         headers: {
           'Authorization': `bearer ${token}`,
         },
         fetch,
    })
  },
}

Enter fullscreen mode Exit fullscreen mode

To read more about Authentication you can read it in my blog post "Hasura Authentication Explained

In our live stream we used our admin secret just for simplicity.

Note: You cannot use admin secret in production environment cause your client will be able to do any changes to database

So at that point we were able to query our blog field

Creating pages

We started creating our pages by exporting our query and referencing the data from blog field. Basically under blog field we had all queries that we can run with Hasura engine.

Once we had our first page with a list of blog posts, Jason started creating other pages by using Gatsby createPages api

In order to create these pages programmatically what had to be done is the following:
After getting result from Hasura posts graphql query, create each page programmatically with path/${post.id} path. In that way we were able to link to these pages later on. For post page component we created a new template/post.js file. There we added real time functionality later on.

Adding GraphQL subscriptions

That was a tricky part. Gatsby queries Hasura GraphQL endpoint on build, but we had to add some real time capabilities using subscriptions, so we needed our post page to behave like a single page app of sorts.

So we had to set up Apollo with ApolloProvider and subscriptions. If you've dealt with react-apollo in the past you probably know that you need to create two links and create a new instance of ApolloClient you can read more about it here:

With Gatsby it's similar, but a bit different. We had to create a root element wrapper and specify it in both gatsby-browser.js and gatsby-ssr.js so Gatsby will get it rendered both when doing server side rendering and both when rendering on the client. This element looked like this.

export const wrapRootElement = ({ element }) => (
  <ApolloProvider client={client}>{element}</ApolloProvider>
)
Enter fullscreen mode Exit fullscreen mode

Dealing with SSR and subscriptions

Defining HttpLink on the client is easy, but when SSR is involved you need to pass fetch implementation to HttpLink, so it won't fail.

import fetch from "isomorphic-fetch"

//rest of the code

const http = new HttpLink({
  uri: "https://hasura-gatsby-livestream.herokuapp.com/v1alpha1/graphql",
  fetch,
})
Enter fullscreen mode Exit fullscreen mode

With subscriptions it was similar, but instead of passing fetch we had to use a package ws and do a custom check if we are running in the browser or not:

//...
import ws from "ws"
//...

const wsForNode = typeof window === "undefined" ? ws : null

//...

const wsClient = new SubscriptionClient(
  "wss://hasura-gatsby-livestream.herokuapp.com/v1alpha1/graphql",
  {
    reconnect: true
  },
  wsForNode
)

//...
const websocket = new WebSocketLink(wsClient)

Enter fullscreen mode Exit fullscreen mode

After configuring our apollo in that way we were able to subscribe to changes from our post template by using

useSubscription(SUBSCRIPTION_NAME, { suspend: false }, variables: { id })
Enter fullscreen mode Exit fullscreen mode

Yeah we used React hooks.

Summary

So what is the purpose of this blog post. First of all a recap on something cool we did a bit more than a month ago, second remind that Gatsby can be used with real time data and that Hasura will give you GraphQL API on top of your existing or new Postgres in no time and also to remind that live coding with other folks in the community is one of my top priorities so if you have something you wanna live code together involving Hasura and some other awesome tool, feel free to reach out to me on twitter. My Twitter DMs are open.

And subscribe to our Youtube channel or Join Discord

Top comments (3)

Collapse
 
hub_tent profile image
Tent hub

Hello Vladimir
Nice video. I was a bit confused about the WebSocket part of the video, though it was cool. So I tried to implement the exact same code not understanding it with my own Hasura endpoint and headers. And of course I have errors:

failed: WebSocket is closed before the connection is established.

Everything is exactly the same. Like a copy, paste and change the variables scenario.

on the terminal

success Build manifest and related icons - 0.252s
success onPostBootstrap - 0.291s

info bootstrap finished - 10.426 s

success run queries - 0.087s - 9/9 103.14/s

{ [Function: WebSocket]
CONNECTING: 0,
OPEN: 1,
CLOSING: 2,
CLOSED: 3,
createWebSocketStream: [Function: createWebSocketStream],
Server: [Function: WebSocketServer],
Receiver: [Function: Receiver],
Sender: [Function: Sender] }

wss://hasura-gatsby-livestream.herokuapp.com/v1alpha1/graphql

Your Gatsby GraphQL Playground is running on http://[YOUR_LOCALHOST]/___playground

You can now view DigitalTentHub in the browser.

localhost:8000/

Collapse
 
rparticledd profile image
Rahul Sharma

Where does the token actually come from in the gatsby config. I am getting an error
ReferenceError: token is not defined

Please help.

Collapse
 
lightstrike profile image
lightstrike

It's a placeholder, you can either paste in a string or (my recommendation), put it in an .env file and reference like bearer ${process.env.HASURA_TOKEN}, where your .env file has a row like HASURA_TOKEN='token-string'. Gatsby docs about environment variables here: gatsbyjs.org/docs/environment-vari...