Build Your First GraphQL API in 10 Minutes with Hasura

Christian Nwamba

GraphQL seems shiny on the frontend, and frontend developers love it because of the flexibility to pick and choose the right size of data for our UI. Where the developer experience gets ugly is when you try to build the backend that supports a GraphQL API.

I am traditionally a frontend developer, but lately, I find that I have to build the API that supports my frontend projects. It gets frustrating when I want to use GraphQL on the frontend project, and for the life of me, I can’t get a GraphQL API running as quickly as I would have liked.

Between these struggles, I’ve finally found a way to optimize my developer experience when building a GraphQL API without sacrificing the users’ experience, thanks to Hasura! In the next 10 minutes, you’re going to roll out a GraphQL API with real data and data relationships.

GraphQL API Backend with Hasura

The trick to setting up a GraphQL API without the hassle is to use Hasura. I have been using it for a while and loved it so much that I started on an online workshop on building fullstack GraphQL projects with Hasura. Alongside fundamental GraphQL features like Queries, Mutations, and Subscriptions, here are a few things Hasura handles for you:

  • Schemas
  • Relationships
  • User Authorization
  • Extensibility with Serverless

You can set up a backend using either of the following options:

  1. Use a docker-compose file and a single docker command to get up and running locally
  2. Deploy to the Cloud directly

Create a GraphQL API Locally

To use Docker, install Docker if you don’t already have it installed. Next, create a project folder and add a docker-compose.yml file with the following:

version: '3.6'
services:
  postgres:
    image: postgres:12
    restart: always
    volumes:
    - db_data:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: postgrespassword
  graphql-engine:
    image: hasura/graphql-engine:v1.1.0
    ports:
    - "4400:8080"
    depends_on:
    - "postgres"
    restart: always
    environment:
      HASURA_GRAPHQL_DATABASE_URL: postgres://postgres:postgrespassword@postgres:5432/postgres
      HASURA_GRAPHQL_ENABLE_CONSOLE: "true"
volumes:
  db_data:

You don’t need to worry about what this configuration does but if you are new to Docker and curious, this file:

  1. Creates a Postgres database for your app’s data using a Postgres image
  2. Creates a GraphQL engine using a Hasura image
  3. Starts up everything and exposes the API on port 4400

You can start the app by running the following on the same directory where you have the config file:

$ docker-compose up -d

When the process completes in the terminal, go to localhost:4400. You should see:

Screenshot: Hasura console

Believe it or not, you have a GraphQL API live at http://localhost:4400/v1/graphql. It’s not useful yet because we do not have data in the Postgres database just yet.

Create a GraphQL API on the Cloud

Setting up in the Cloud is one form away. Fill out this form, and a Microsoft Azure wizard will generate a GraphQL API for you.

Add a Table to Store Data

To persist data, we need to first create a table where this data can be stored. Let’s create some tables!

We want to model a zoo database where they can store animals and the class each animal belongs to. We can have a class table and an animal table. An animal can belong to a class while a class can have many animals. A dog is a mammal while an alligator, a lizard, and a snake are reptiles.

  • Create Table Page: Go to the create table page by clicking on Data from the Navbar and click the Create Table button.

Screenshot: creating a table in Hasura

  • Configure Table: Create a class table with just two fields: id and type. Then select id to be the primary key:

Screenshot: table properties

  • Save Table: Click the Add Table button at the bottom of the page.

Head back to the GraphiQL page, and you should find queries for the table you created. You can try a query to get the list of animal classes:

query MyQuery {
  class {
    type
  }
}

The query will return an empty list as expected.

Screenshot: query result

You are currently seeing only the available queries. You can also explore mutations and subscriptions by selecting it from the ADD NEW dropdown at the bottom of the Explorer pane and clicking the plus button:

Screenshot: query type selection

Run this mutation to add a class to the class table:

mutation MyMutation {
  insert_class(objects: {type: "Mammal", id: 1}) {
    affected_rows
  }
}

This will return the affected rows count:

{
  "data": {
    "insert_class": {
      "affected_rows": 1
    }
  }
}

You can also confirm this by taking a look at the Data page where we created the table and inspecting the class table.

Run the following mutation to delete a row:

mutation MyMutation {
  delete_class(where: {id: {_eq: 1}}) {
    affected_rows
  }
}

Add a Table using SQL Commands

One cool thing about Hasura is how it allows you to manipulate your Postgres database directly through SQL commands. To go to the SQL editor, go to the Data page and click SQL from the sidebar:

Screenshot: SQL editor

Run the following SQL command:

CREATE TABLE IF NOT EXISTS public."animal" (
  "name" TEXT,
  "class_id" INT,
  "id" SERIAL PRIMARY KEY
);

You should see an animal table appear on the list of tables, and you can also see a queryable animal at the GraphiQL page.

The SERIAL type sets the id as an integer that increments automatically. This is what you want most times for IDs when you are not using something like UUID.

Add a Relationship

Since we know that animals belong to a class, let’s see how we can add the relationship to our database tables. Relationships in Hasura come in two ways:

  1. Table relationship
  2. Object relationship

Table Relationship

A table relationship is the same thing as running an SQL command to add a foreign key. Since animal represents the many part of the relationship, we are going to add a foreign key to it.

Go to the Data page, select animal from the sidebar and click the Modify tab:

Screenshot: Modify tab

Scroll down to Foreign Keys and click Add. Complete the foreign key form as shown in the screenshot the click Save:

Screenshot: foreign key setup

Object Relationships

Though we have just told Postgres about our relationship, our GraphQL engine does not know about this. If you have ever written a GraphQL schema, our schema theoretically still looks like this:

type Class {
  id: ID
  type: String
}

type Animal {
  id: ID
  name: String
}

There is still no relationship between these two entities. To prove this to you, try running the following query:

query MyQuery {
  class {
    animals {
      name
    }
  }
}

You should get an error that says animals is not found in type class:

{
  "errors": [
    {
      "extensions": {
        "path": "$.selectionSet.class.selectionSet.animals",
        "code": "validation-failed"
      },
      "message": "field \"animals\" not found in type: 'class'"
    }
  ]
}

This is how you would introduce a relationship to them through the schema:

type Class {
  id: ID
  type: String
  animal: [Animal]
}

type Animal {
  id: ID
  name: String
  class: Class
}

This kind of relationship is called an Object relationship. You need to add an object relationship to both sides.

Go to each of the tables and click the Relationships tab. You should see a suggestion to add an object relationship — add it. This suggestion is based on the fact that we already related both tables with a foreign key.

Screenshot: array relationship

Screenshot: object relationship

Seeding a Database

Seeding a database allows you to have enough dataset to test with. You can seed through Hasura using the SQL editor we saw earlier in this article.

Run the following SQL to seed the class table:

INSERT INTO public."class" ("id", "type") VALUES
    (1,'Mammal'),
    (2,'Bird'),
    (3,'Reptile'),
    (4,'Fish'),
    (5,'Amphibian'),
    (6,'Bug'),
    (7,'Invertebrate');

Run the following SQL to seed the animal table:

INSERT INTO public."animal" ("name", "class_id")  VALUES
    ('aardvark',1),
    ('antelope',1),
    ('bass',4),
    ('bear',1),
    ('boar',1),
    ('buffalo',1),
    ('calf',1),
    ('carp',4),
    ('catfish',4),
    ('cavy',1),
    ('cheetah',1),
    -- Get full list from gist https://gist.github.com/christiannwamba/f6f1aa1b87c455c88764b749ad24d458

Now you can start trying out interesting queries:

# Get classes with their animals
query MyQuery {
  class {
    animals {
      name
    }
  }
}

# Get animals with the class they belong to
query MyQuery {
  animal {
    name
    class {
      type
    }
  }
}

# Get animals that are mammals
query MyQuery {
  animal(where: {class: {type: {_eq: "Mammal"}}}) {
    name
  }
}

# ...and so on

That’s it for now! Don’t forget to try out mutations and subscriptions too! 😎

  Tweet It

🕵 Search Results

🔎 Searching...

Sponsored by #native_company# — Learn More
#native_title# #native_desc#
#native_cta#