Building blog CMS in ReasonML with GraphQL and Serverless using Hasura
Tags: serverless, graphql, react, reason
Published: 2019-06-21 09:54:28

---
title: Building blog CMS in ReasonML with GraphQL and Serverless using Hasura
published: true
description: In this series of blog posts we will see how you can write your ReasonML apps with ReasonReact, GraphQL powered by Hasura and serverless business logic. 
tags: serverless, graphql, react, reasonml
series: blog cms in ReasonML with graphql and serverless using Hasura
---

This is the first part of blog post series where we will create blog cms using Hasura for GraphQL API, and serverless functions for logic and on the client we will write modern and robust code using ReasonML syntax. Let's get started.

# ReasonML intro

First of all, before getting into actual code writing, let's discuss why ReasonML? Even though it's a topic for a stand-alone blog post, I will try to give you a brief overview. ReasonML gives us a fantastic type system powered by Ocaml,  but as far as syntax goes, it looks pretty close to Javascript. It was invented by [Jordan Walke](https://github.com/jordwalke), the guy who created React and is used in production at Facebook messenger. Recently various companies also adopted Reason and use it in production because of it's really cool paradigm: "If it compiles - it works."
This phrase is a very bold statement, but in fact, because Reason is basically a new syntax of OCaml language, it uses [Hindley Milner](https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system) type system so it can infer types in compile time. 

### What it means for us as developers?
It means that typically we don't write that many types, if at all, as we write in TypeScript for example and can trust the compiler to infer these types. 

Speaking of compilation, Reason can be compiled to OCaml, which in turn can compile to various targets such as binary, ios, android etc, and also we can compile to human-readable JavaScript with the help of Bucklescript compiler. In fact that what we will do in our blog post.

### What about npm and all these packages we are used to in JavaScript realm?

 In fact, BuckleScript compiler gives us powerful Foreign function interface [FFI](https://en.wikipedia.org/wiki/Foreign_function_interface) that lets you use JavaScript packages, global variables, and even raw javascript in your Reason code. The only thing that you need to do is to accurately type them to get the benefits from the type system. 

Btw if you want to learn more about ReasonML, I streamed 10h live coding Bootcamp on Youtube, that you can view on my [channel](https://www.youtube.com/c/VladimirNovickDev)

# ReasonReact 

When using Reason for our frontend development, we will use [ReasonReact](https://reasonml.github.io/reason-react/). There are also some community bindings for VueJs, but mainly, when developing for web we will go with ReasonReact. If you've heard about Reason and ReasonReact in the past, recently ReasonReact got a huge update making it way easier to write, so the syntax of creating Reason components now is not only super slick but looks way better than in JavaScript, which was not the case in the past. Also, with the introduction of hooks, it's way easier to create ReasonReact components and manage your state.

## Getting started

In official ReasonReact docs, the advised way to create a new project is to start with `bsb init` command, but let's face it. You probably want to know how to move from JavaScript and Typescript. So in our example, we will start by creating our project with create-react-app.
 
We will start by running the following command:

```bash
npx create-react-app reason-hasura-demo
```

It will create our basic React app in JavaScript, which we will now change into ReasonReact. 

### Installation 

If it's the first time you set up ReasonML in your environment, it will be as simple as installing bs-platform. 

```bash
yarn global add bs-platform
```

Also, configure your IDE by installing appropriate [editor plugin](https://reasonml.github.io/docs/en/editor-plugins)

I use [reason-vscode](https://marketplace.visualstudio.com/items?itemName=jaredly.reason-vscode) extension for that. I also strongly advise using `"editor.formatOnSave": true,` vscode setting, because Reason has a tool called `refmt` which is basically built in Prettier for Reason, so your code will be properly formatted on save.

### Adding ReasonML to your project

Now it's time to add ReasonML. We will install `bs-platform` and `reason-react` dependencies.

```bash
yarn add bs-platform --dev --exact
yarn add reason-react --exact
```

And get into the configuration. For that create `bsconfig.json` file with the following configuration:

```json
{
  "name": "hasura-reason-demo-app",
  "reason": { "react-jsx": 3 },
  "bsc-flags": ["-bs-super-errors"],
  "sources": [
    {
      "dir": "src",
      "subdirs": true
    }
  ],
  "package-specs": [
    {
      "module": "es6",
      "in-source": true
    }
  ],
  "suffix": ".js",
  "namespace": true,
  "bs-dependencies": [
    "reason-react"
  ],
  "ppx-flags": [],
  "refmt": 3
}
```

Let's also add compilation and watch scripts to our package.json

```bash
"re:build": "bsb -make-world -clean-world",
"re:watch": "bsb -make-world -clean-world -w",
```

If you run these scripts, what will basically happen is all `.re` files in your project will be compiled to javascript alongside your `.re` files. 

### Start configuring our root endpoint 

Let's write our first reason file, by changing index.js from 

```react
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(<App />, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

```

to 

{% gist https://gist.github.com/vnovick/9d3e8334de3f368b23c7515e5338be44
file=index.re %}


Basically what I am doing here is render my App component into the dom with 


{% gist https://gist.github.com/vnovick/9d3e8334de3f368b23c7515e5338be44
file=index.partial.re %}


And with 


{% gist https://gist.github.com/vnovick/9d3e8334de3f368b23c7515e5338be44
file=index.partial2.re %}


I import register and unregister methods from `serviceWorker.js` file so I can use Javascript in Reason. 


to run our project, we need to run 

```bash
npm run re:watch
```

so our Bucklescript will build files for the first time and watch for changes whenever new files are added.

and in different tab let's just run `npm start` and see our React app.


### Basic styling

Styling with ReasonML can be either typed due to `bs-css` which is based on `emotion` or untyped. For simplicity, we will use untyped. Let's delete index.css and App.css we have from 'create-react-app', create `styles.css` file and import two packages:

```
yarn add animate.css
yarn add tailwind --dev
```

now in our `styles.css` file, we will import tailwind 

```css
@tailwind base;

@tailwind components;

@tailwind utilities;

```

and add styles build script in `package.json`

```json
"rebuild-styles": "npx tailwind build ./src/styles.css -o ./src/index.css",
```

### Writing our first component.

Let's rename our App.css file to App.re, delete all its contents, and write simple ReasonReact component.

{% gist https://gist.github.com/vnovick/9d3e8334de3f368b23c7515e5338be44
file=app.re %}


Nice right? With ReasonML, we don't need to import or export packages, and in fact, each file is a module, so if our file name is App.re, we can simply use <App /> component in a different file. 


### String to element

In ReasonReact, if you want to add text in component, you do it by using `ReasonReact.string`


Also, I prefer the following syntax:

{% gist https://gist.github.com/vnovick/9d3e8334de3f368b23c7515e5338be44
file=App.preferred.re %}


You will see it quite a lot in this project. This syntax is *reverse-application operator* or *pipe* operator that will give you an ability to chain functions so `f(x)` is basically written as `x |> f`.

Now you might say, but wait a second that will be a tedious thing to do in ReasonReact. every string needs to be wrapped with ReasonReact.string. There are various approaches to that. 

A common approach is to create `utils.re` file somewhere with something like 

`let ste = ReasonReact.string` and it will shorten our code to 

{% gist https://gist.github.com/vnovick/9d3e8334de3f368b23c7515e5338be44
file=App.ste.re %}


Through the project, I use `ReasonReact.string` with a pipe so the code will be more self-descriptive.


# What we will be creating

So now when we have our ReasonReact app, it's time to see what we will be creating in this section:

{% youtube vl4MU4z-tXw %}

This app will be a simple blog, which will use GraphQL API, auto-generated by [Hasura](https://hasura.io), will use subscriptions and ReasonReact.

## Separate app to components


We will separate apps to components such as `Header`, `PostsList`, `Post` `AddPostsForm` and `Modal`. 

### Header

Header will be used for top navigation bar as well as for rendering "Add  New Post" button on the top right corner, and when clicking on it, it will open a Modal window with our `AddPostsForm`. `Header` will get `openModal` and `isModalOpened` props and will be just a presentational component.

We will also use javascript `require` to embed an SVG logo in the header.

{% gist https://gist.github.com/vnovick/9d3e8334de3f368b23c7515e5338be44
file=Header.re %}

Header button will stop propagation when clicked using `ReactEvent.Synthetic` ReasonReact wrapper for React synthetic events and will call `openModal` prop passed as labeled argument (all props are passed as labeled arguments in ReasonReact).

### Modal

`Modal` component will also be a simple and presentational component 

{% gist https://gist.github.com/vnovick/9d3e8334de3f368b23c7515e5338be44
file=Modal.re %}
  
For modal functionality in our `App.re` file, we will use `useReducer` React hook wrapped by Reason like so:

{% gist https://gist.github.com/vnovick/9d3e8334de3f368b23c7515e5338be44
file=App.state.re %}

Notice that our `useReducer` uses [pattern matching](https://reasonml.github.io/docs/en/pattern-matching) to pattern match on `action` [variant](https://reasonml.github.io/docs/en/variant). If we will, for example, forget `Close` action, the project won't compile and give us an error in the editor.

![](https://thepracticaldev.s3.amazonaws.com/i/6vi9ge6khwlfd0gwfnz2.png)

### PostsList, Post

Both PostsList and Post will be just presentational components with dummy data.


### AddPostForm

Here we will use React `setState` hook to make our form controlled. That will be also pretty straightforward:

{% gist https://gist.github.com/vnovick/9d3e8334de3f368b23c7515e5338be44
file=AddPost.nographql.re %}

`onChange` event will look a bit different in Reason but that mostly because of it's type safe nature:

```react
<input onChange={e => e->ReactEvent.Form.target##value |> setCoverImage
}/>
```

# Adding GraphQL Backend using Hasura

Now it's time to set GraphQL backend for our ReasonReact app. We will do that with [Hasura](https://hasura.io). 

In a nutshell, Hasura auto-generates GraphQL API on top of new or existing Postgres database. You can read more about Hasura in the following blog post [blog post](https://dev.to/vladimirnovick/effortless-real-time-graphql-api-with-serverless-business-logic-running-in-anycloud-2j6b) or follow Hasura on Youtube [channel](https://www.youtube.com/c/hasurahq.

We will head to [hasura.io](https://hasura.io) and click on Docker image to go to the doc section explaining how to set Hasura up on docker.

We will also [install Hasura cli](https://docs.hasura.io/1.0/graphql/manual/hasura-cli/install-hasura-cli.html) and run `hasura init` to create a folder with migrations for everything that we do in the console.

Once we have Hasura console running, let's set up our posts table:

![](https://thepracticaldev.s3.amazonaws.com/i/x0zcu7tg2rmm749ajcym.png)

and users table:

![](https://thepracticaldev.s3.amazonaws.com/i/u989669hcfldwmqt2bs1.png)

We will need to connect our posts and users by going back to posts table -> Modify and set a Foreign key to users table:

![](https://thepracticaldev.s3.amazonaws.com/i/7645yppez4e6d8er20f6.png)
 
We will also need to set relationships between posts and users so user object will appear in auto-generated GraphQL API.

![](https://thepracticaldev.s3.amazonaws.com/i/6inqg6nwna81ppv917rm.png)

Let's head to the console now and create first dummy user:

```graphql
mutation {
  insert_users(objects: {id: "first-user-with-dummy-id", name: "Test user"}) {
    affected_rows
  }
}

```

Let's now try to insert a new post:

```graphql

mutation {
  insert_posts(objects: {user_id: "first-user-with-dummy-id", title: "New Post", content: "Lorem ipsum - test post", cover_img: "https://images.unsplash.com/photo-1555397430-57791c75748a?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=80"}) {
    affected_rows
  }
}

```

If we query our posts now will get all the data that we need for our client:

```graphql
query getPosts{
  posts {
    title
    cover_img
    content
    created_at
    user {
      name
      avatar_url
    }
  }
}
```

# Adding GraphQL to our app

Let's install a bunch of dependencies to add GraphQL to our ReasonReact app and start getting blog posts in real-time.

```bash
yarn add @glennsl/bs-json apollo-boost apollo-link-ws graphql react-apollo reason-apollo subscriptions-transport-ws
```

When we work with Reason, we want to run an introspection query to our endpoint so we will get our graphql schema introspection data as json. It will be used to give us graphql queries completion and type checking in the editor later on, which is pretty cool and best experience ever.

```bash
yarn send-introspection-query http://localhost:8080/v1/graphql    
```
We also need to add `bs-dependencies` to our `bsconfig.json`

```json
"bs-dependencies": [
    "reason-react",
    "reason-apollo",
    "@glennsl/bs-json"
  ],
  "ppx-flags": ["graphql_ppx/ppx"]

```
We've added `graphql_ppx` ppx flag here - that will allow us to write GraphQL syntax in ReasonML later on.

Now let's create a new `ApolloClient.re` file and set our basic ApolloClient

{% gist https://gist.github.com/vnovick/9d3e8334de3f368b23c7515e5338be44
file=ApolloClient.basic.re %}


# Adding queries and mutations


### Queries

Let's head to our `PostsList.re` component and add the same query we ran previously in Hasura graphiql:

{% gist https://gist.github.com/vnovick/9d3e8334de3f368b23c7515e5338be44
file=PostsList.basic.re %}

Now we can use `GetPostsQuery` component with render prop to load our posts. But before that, I want to receive my GraphQL API result typed, so I want to convert it to [Records](https://reasonml.github.io/docs/en/record).

It's as simple as adding types in `PostTypes.re` file

{% gist https://gist.github.com/vnovick/9d3e8334de3f368b23c7515e5338be44
file=PostTypes.re %} and opening them in any file that will use them `open PostTypes`

The final version of `PostsList` component will look as following:

{% gist https://gist.github.com/vnovick/9d3e8334de3f368b23c7515e5338be44
file=PostsList.re %}


### Mutations 

To add mutation to our `AddPostForm`, we start in the same way as with queries:

{% gist https://gist.github.com/vnovick/9d3e8334de3f368b23c7515e5338be44
file=AddPost.basic.re %}

The change will be in the render prop. We will use the following function to create variables object:

```
let addNewPostMutation = PostMutation.make(~title, ~content, ~sanitize, ~coverImg, ());
```

to execute mutation itself, we simply need to run 


```
mutation(
  ~variables=addNewPostMutation##variables,
  ~refetchQueries=[|"getPosts"|],
  (),
) |> ignore;
```

The final code will look like this:

{% gist https://gist.github.com/vnovick/9d3e8334de3f368b23c7515e5338be44
file=AddPost.re %}

# Adding Subscriptions

To add subscriptions we will need to make changes to our `ApolloClient.re`. Remember we don't need to import anything in Reason, so we simply start writing. 

Let's add `webSocketLink`

{% gist https://gist.github.com/vnovick/9d3e8334de3f368b23c7515e5338be44
file=websocketlink.re %}

and create a link function that will use `ApolloLinks.split` to target WebSockets, when we will use subscriptions or `httpLink` if we will use queries and mutations. The final ApolloClient version will look like this:

{% gist https://gist.github.com/vnovick/9d3e8334de3f368b23c7515e5338be44
file=ApolloClient.final.re %}

Now to change from query to subscription, we need to change word `query` to `subscription` in graphql syntax and use `ReasonApollo.CreateSubscription` instead of `ReasonApollo.CreateQuery`


# Summary and what's next

In this blog post, we've created a real-time client and backend using Hasura, but we haven't talked about Serverless yet. Serverless business logic is something we will look into in the next blog post. Meanwhile, enjoy the read and start using ReasonML. 

You can check out the code here:
[https://github.com/vnovick/reason-demo-apps/tree/master/reason-hasura-demo](https://github.com/vnovick/reason-demo-apps/tree/master/reason-hasura-demo) and follow me on Twitter [@VladimirNovick](https://twitter.com/VladimirNovick) for updates.