Build GraphQL apps with Hasura and Azure SQL Database

Silvano Coriani

[This post has been created in collaboration with Hasura Inc.]

[On this subject, please also make sure you watch the related Data Exposed episode]

Introduction

Hasura makes data access easy, by instantly composing a GraphQL API that is backed by databases and services so that the developer team (or API consumers) get productive immediately. The nature of GraphQL itself and Hasura’s dynamic approach makes integration and iteration easy.

Hasura’s event engine makes it possible to embrace cloud-native without having to worry about non-functional things scaling, reliability and delivery guarantees. Developers can easily build business logic that publishes and consumes events, that reacts to changes in data or gets scheduled based on time.

The Hasura GraphQL Engine is a blazing-fast GraphQL server that gives you instant, realtime GraphQL APIs over Azure SQL, with webhook triggers on database events, and remote schemas for business logic.

Hasura helps you build GraphQL apps backed by Azure SQL databases or incrementally move to GraphQL for existing applications using Azure SQL.

Image Pic1

Figure 1 Hasura architectural diagram

The Hasura GraphQL Engine is open source. You can check out the complete repo here.

A complete application using Hasura and Azure SQL can be found on GitHub here.

In this guide, we will:
  • Create and configure a new Hasura application
  • Walk through how to integrate your Azure SQL database with Hasura
  • Write a Vue 3 frontend application to consume your brand-new GraphQL API.

By the end of this walkthrough, you’ll understand the fundamentals of Hasura, have a fully functioning real-time GraphQL CRUD API, and maybe even learn a thing or two about Vue.

Requirements
  • js
  • npm/yarn
  • Docker, or a Hasura Cloud account
    • If not using Docker, an Azure SQL database is also required

Solution Setup:

Hasura

If using Docker:

  • Run docker compose up -d to start the following services:
    • Hasura, exposed on localhost:8070
    • Postgres DB to use as Hasura’s metadata storage engine
    • Azure SQL database to use for this tutorial

If using Hasura Cloud:

  • Open your Cloud app, or launch a new one for this tutorial

Image Pic2

  • If required, from the “Add Database” configuration panel use the “Heroku” tab to create & attach a free Heroku Postgres DB

Image Pic3

 

Finally:

  • Edit the .env file in this repo, and set VITE_HASURA_GRAPHQL_ENDPOINT to the endpoint of your Hasura application
  • Using the Hasura web console, navigate to “Data” -> “Connect Database”

Image Pic34jpg

  • Select “MS-SQL” as the Database type, and either:
    • A) Set the connection string type to be Environment variable, and enter HASURA_MSSQL_DATABASE_URL if using Docker
    • B) Enter the connection string to your existing Azure SQL database

Image Pic5

Frontend

  1. Clone this repo
  2. Run yarn install or npm install
  3. Run yarn dev or npm run dev to start the Vue 3 + Vite development server
  4. Visit http://localhost:3000 to check that the application is running

Data Models

We need to add our todo data model to our application before we begin.

The easiest way to do this is via the Hasura web console.

There are two ways that you can create the table:

  1. By using the “Run SQL” page to execute the DDL statement:
CREATE TABLE "todo" (

    id int not null identity(1,1) primary key,

    description nvarchar(200) NOT NULL,

    is_completed bit NOT NULL DEFAULT 0

);

Image Pic6

  1. By using the “Add table” page to graphically create our model:

Image Pic7

After this, we can insert a record or few into the table, so that we have some data to test with and confirm our Vue application is working in a moment.

From the “Run SQL” table, execute the following:

INSERT INTO "todo"

    (description)

VALUES

    ('Learn Vue 3'),

    ('Be awesome');

 

Checking the “Browse Rows” tab in our table, we should see the rows in todo now:

Image Pic8

Try out GraphQL Queries

Once the table is tracked by Hasura, CRUD APIs are instantly created.

If we query from our GraphiQL Explorer page, we should see we now have a functioning GraphQL API to access these todos:

For example, try the following query:

```graphql

    query {

        todo {

            id

            description

            Is_completed

         }

     }

```

You can see the response with the same fields that were requested in the query.

Image Pic9

Similarly, the APIs for inserting, updating and deleting data are also available as GraphQL Mutations.

Let’s try out a mutation to insert a todo:

```

    mutation insert_todo($description: String!) {

        insert_todo_one(object: {

            description: $description
        }) {

                id

                description

                is_completed
         }

    }

```

The “description” variable needs to be sent in the request for the mutation to succeed.

Finally, let us look at an example for a GraphQL Subscription.

```

    subscription fetchTodos {

        todos {

            id

            description

        }

    }

```

The above query will listen to changes in the database for the todos table. If a new row is available, it will return a response with all the todos at that point, including the new one.

 

Building our Vue App

We will walk through building a basic Vue 3 application that can Create/Read/Delete from these todos.

Reading Todos

Starting from a blank canvas, let’s add the code to be able to read and display our SQL Server todo rows.

Our App.vue looks like this:

<template>

    <img alt="Vue logo" src="./assets/logo.png" />

</template>

<script setup>

</script>

 

What we want to do is:

  • Write a function that can query our GraphQL API for the todos
  • When the page is created, initiate this request
  • Store the todos in the component state on result
  • Display the todos

To query our todos, we can use fetch():

export async function getTodos() {

    const req = await fetch(HASURA_GRAPHQL_ENDPOINT, {

        method: "POST",

        body: JSON.stringify({

            query: `
    
                query {
                
                    todo {
                    
                        id
                        
                        description
                        
                        is_completed
                    
                    }
                
                }
                
            `,

        }),

    })

    const res = await req.json()

    return res ? .data ? .todo

}

Integrating this into our App.vue, looks something like this:

<template>
    <img alt="Vue logo" src="./assets/logo.png" />
    <h1 v-if="state.todos.length == 0">
      No todos
    </h1>
    <section v-else>
        <div :key="todo.id" v-for="todo in state.todos">
            <p>ID: {{ todo.id }}</p>
            <input v-model="todo.description" />
            <input
                type="checkbox"
                v-model="todo.is_completed"
                :checked="todo.is_completed"
            />
        </div>
    </section>
</template>

<script setup>
import { onMounted, reactive } from "vue"

const state = reactive({
  todos: []
})

async function getAndSetTodos() {
    const todos = await getTodos()
    state.todos = todos
}

onMounted(async () => {
    getAndSetTodos()
})
</script>

After we save, Vite will hot-reload our app. Assuming all is well, our page should now look like this:

Image Pic10

Success! We’ve already integrated data-access from our Azure SQL Database!

Delete Todo

The next step is to be able to update the todos we are able to read.

We have already wired up 2-way data binding to the fields of the todos being displayed. We just need to write a function that can receive a “todo” object and pass it along to Hasura to delete in the database.

We can do that like this:

export async function deleteTodoById(id) {
    const req = await fetch(HASURA_GRAPHQL_ENDPOINT, {
        method: "POST",
        body: JSON.stringify({
            query: `
              mutation DeleteTodoByPk($id: Int!) {
                delete_todo_by_pk(id: $id) {
                  id
                }
              }
            `,
            variables: {
                id,
            },
        }),
    })
    const res = await req.json()
    return res?.data?.delete_todo_by_pk
}

<template>
    <img alt="Vue logo" src="./assets/logo.png" />
    <h1 v-if="state.todos.length == 0">
      No todos
    </h1>
    <section v-else>
        <div :key="todo.id" v-for="todo in state.todos">
            <p>ID: {{ todo.id }}</p>
            <input v-model="todo.description" />
            <input
                type="checkbox"
                v-model="todo.is_completed"
                :checked="todo.is_completed"
            />
            <button @click="deleteTodoById(todo.id)">Delete</button>
        </div>
    </section>
</template>

If we press the “Delete” button on our of todos, we should see it disappear in the database.

Create Todo

Finally, let’s write some code to allow us to insert new todo items.

We will need the underlying GraphQL mutation function first:

export async function createTodo(description) {
    const req = await fetch(HASURA_GRAPHQL_ENDPOINT, {
        method: "POST",
        body: JSON.stringify({
            query: `
              mutation CreatTodo($description: String!) {
                insert_todo_one(object: {
                  description: $description
                }) {
                  id
                  description
                  is_completed
                }
              }
            `,
            variables: {
                description,
            },
        }),
    })
    const res = await req.json()
    return res?.data?.insert_todo_one
}

And now we need to wire up an input box and submit button to trigger the todo insert:

<template>
    <img alt="Vue logo" src="./assets/logo.png" />

    <p>Create New Todo</p>
    <label for="todo-description">
      Description
    </label>
    <input name="todo-description" v-model="state.newTodoDescription" />
    <button @click="createTodo(state.newTodoDescription).then(getAndSetTodos)">
      Create
    </button>

    <hr />

    <h1 v-if="state.todos.length == 0">
      No todos
    </h1>
    <section v-else>
        <div :key="todo.id" v-for="todo in state.todos">
            <p>ID: {{ todo.id }}</p>
            <input v-model="todo.description" />
            <input
                type="checkbox"
                v-model="todo.is_completed"
                :checked="todo.is_completed"
            />
            <button @click="deleteTodoById(todo.id)">Delete</button>
        </div>
    </section>
</template>

<script setup>
import { reactive, onMounted } from "vue"
import { getTodos, createTodo, deleteTodoById } from "./utils"

const state = reactive({
    newTodoDescription: "",
    todos: [],
})

async function getAndSetTodos() {
    const todos = await getTodos()
    state.todos = todos
}

onMounted(async () => {
    getAndSetTodos()
})
</script>

In summary

We learnt how to create a GraphQL backend using Hasura connected to Azure SQL. We started off by creating data models in Azure SQL, tracked them on Hasura for generating GraphQL APIs. Once the APIs were available, we were able to integrate them into a Vue.js app. This implementation could be replicated to any frontend framework of choice (like React.js / Angular) etc.

In the end, we get high performance, scalable GraphQL backend with Hasura and Azure SQL.

0 comments

Comments are closed. Login to edit/delete your existing comments