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:
I just published Effortless Real-time GraphQL API with serverless business logic running in any cloud link.medium.com/DET666lI3T11:47 AM - 05 Feb 2019
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,
})
},
}
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>
)
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,
})
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)
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 })
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)
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/
Where does the token actually come from in the gatsby config. I am getting an error
ReferenceError: token is not defined
Please help.
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 likeHASURA_TOKEN='token-string'
. Gatsby docs about environment variables here: gatsbyjs.org/docs/environment-vari...