Blog A.Wolf

Blog Posts

No results for 'undefined'Powered by Algolia

Using Hasura with Docker and a PostgreSQL database - part 1 of 2 - Docker

August 1st, 2021 - 9 min read

We're taking How-to Dockerize your sveltekit app as the base for this post.

Please read that post first or check out the following tag. Either clone the repo and checkout that version or download the zip-file.

This is part one of two blog posts and here we're focusing on the Docker setup. In the second part we'll create the Svelte Todo app. So if you're more interested in the frontend app with Graphql you can directly start with the other post.

What are we building?

We're creating a basic to-do app with adding todos, deleting, and toggling the status.

The data will be stored in a PostgreSQL database and we're querying/mutating it with Graphql. We'll use Hasura as Graphql service. It will be used for the connection from Svelte to the Postgres SQL data.

If you're new to GraphQL, please have a look at the getting started guide from graphql.org for more details (or How to graphql video guides) because I'm not going into the Graphql query details.

Docker compose changes

Below is the complete docker-compose.yml that we're having in the root directory. I will explain every change after the snippet.

version: '3.9'

services:
    web:
        build: ./app
        ports: 
            - "80:3000"
    db:
        image: postgres
        restart: always
        volumes:
            - pgdata:/var/lib/postgressql/data
        ports:
            - "5432:5432"
        environment:
            POSTGRES_PASSWORD: $POSTGRES_PASSWORD

    graphql-engine:
        image: hasura/graphql-engine:v2.0.1.cli-migrations-v3
        ports:
            - "8080:8080"
        depends_on: 
            - db
        restart: always
        volumes:
            - ./hasura/migrations:/hasura-migrations
            - ./hasura/metadata:/hasura-metadata
        environment:
            HASURA_GRAPHQL_DATABASE_URL: $POSTGRES_URL
            HASURA_GRAPHQL_ENABLE_CONSOLE: "false"
            # Note: MIGRATIONS_DIR / METADATA_DIR only needed if we'd like to have a different location in the container
            # HASURA_GRAPHQL_MIGRATIONS_DIR: /hasura-migrations
            # HASURA_GRAPHQL_METADATA_DIR: /hasura-metadata
        ## uncomment next line to set an admin secret
        # HASURA_GRAPHQL_ADMIN_SECRET: $HASURA_ADMIN_SECRET
    adminer:
        image: adminer
        restart: always
        ports:
            - "8090:8080"
volumes:
    pgdata:

In the db service, we changed the volume to pgdata and add an empty volumes: pgdata: at the end of the file. This will create persistence in a location of the Docker host by using the default driver (that's typically the local driver). For more details, see the volumes config docs.

The new graphql-engine service will use the hasura image. Port is directly mapped to 8080 on the host. depends_on means that the service can only be started if the db service is running. This is needed as Hasura needs the database to work with.

restart: always will restart the service if it was stopped.

volumes we're mapping folders of our project root that we'll create, so we're having our migrations in place. Migrations are used to initialize our database. We don't need to add these folders manually. We're adding them with the Hasura CLI.

Adminer service could be removed because Hasura can replace this completely. But for now, we change the host port to 8090, so there is no conflict with the graphql-engine service.

There are some environment variables to configure Hasura:

  • HASURA_GRAPHQL_ENABLE_CONSOLE: If "true" the service will start the console.
  • HASURA_MIGRATIONS_DIR / HASURA_METADATA_DIR: Specifes the location of our migrations / metadata - optional, using default location (see Hasura Migrations section below for more details)
  • HASURA_ADMIN_SECRET: If set there should be a password in the console - I haven't tested this.

.env file in the project root:

# POSTGRES config
POSTGRES_PASSWORD=password
POSTGRES_URL=postgres://postgres:password@db:5432/postgres

# Hasura config
# HASURA_ADMIN_SECRECT=...

Side note to the graphql-engine service

You can further improve this setup by adding docker-compose-wait and wait for the db port 5432. In a Dockerfile in the hasura folder. This is left as an exercise but will be available in the final code.

This will improve startup as depends_on only says that the service is running but not if the port is accessible. Without waiting the graphql-engine will likely restart multiple times until the port is ready.

Hasura migrations

Install Hasura CLI with npm install --global hasura-cli. Next, check that HASURA_GRAPHQL_ENABLE_CONSOLE is set to "false". That's needed to work with the migrations. It tells the service that there is no need to start the console on port 8080 as we're using the CLI to manage and start the console for local development.

Restart the graphql-engine and run hasura init hasura --endpoint http://localhost:8080 inside the project root.

This will create a subfolder hasura where the migrations/metadata are stored.

Note

Avira Antivirus heuristic quarantined Hasura on Windows 10. Reducing the heuristic level from medium to low fixed this false-positive.

Change to the hasura directory and run hasura console. This opens a console that will be connected to the hasura folder.

Click on data tab and create the table. Fill the field like in the below screenshot by adding:

  • id: UUID, gen_random_uuid(), unique, primary-key
  • title: Text
  • completed: Boolean default false
  • created_at: Timestamp default now()::timestamp

Hasura create todo table uuid with created_at

Add as method gen_random_uuid to the id so the backend will use that method to generate the unique id. It will generate a UUID_v4 for every new row.

The created_at will use a UTC timestamp by using now() as default method. The ::timestamp ensures that it is a UTC timestamp. For the timestamp type, you could also use time without time zone but in Postgres SQL timestamp will always default to the UTC timestamp.

Or even better, use the type time with-out time zone and use as default the following with braces (now() at time zone 'utc') so it's crystal clear that your storing the created_at field as UTC timestamp. For more details, have a look at the following SO answer.

Finally, click Add table. There is a todo table in the console and if you're looking in your hasura folder you will see the added migration & metadata that were created - we're using this to initially create our database.

To run the migrations on Docker start, it's important to use the Hasura image with .cli-migrations-v3 in its name. This will include /bin/hasura-cli so it will automatically run the migrations on startup.

The service will use the migrations located at the default location /hasura-migrations & /hasura-metadata - we could change this with the environment variables and the volumes keys. (It's the location inside the container that's why there is the slash pointing to the root folder.)

Some words to the migrations config version, we're using v3 here as it is the latest version. But v2 will also work, the main difference is that in v3 migration config supports multiple databases - so the migrations are more complicated.

But if you're not planning to create the migrations manually, I'd use version 3 as the Hasura console will generate everything for us.

Note: Don't be confused by v3 not selectable in the Hasura docs, v3 is the version of the migration config and not the Hasura version v1.x/v2.x.

Seeding the database

Follow the Hasura docs.

For step 3 use the following SQL (no id required because the DB will generate UUIDs):

INSERT INTO todo (title, completed, created_at) VALUES
  ('Learn Svelte', false, now() - interval '2 second'),
  ('Learn Docker', false, now() - interval '1 second'),
  ('Learn Graphql', true, now());

Hasura seed todos

We're adding the created_at with intervals so we're having different timestamps. Learn Graphql is the newest entry as it's using now() directly. This is only relevant for seeding.

Before running the SQL insert, don't forget to select "This is a migration" checkbox and pick a name e.g. insert_seed_todos. This will add the insert as migration.

Now, click run and have a look in the hasura\migrations folder. The insert_seed_todos should be added there.

The new data will be also displayed in the data tab of the console with three new entries.

Testing the Graphql queries with the Husara console

Change into the hasura folder and run hasura console or change HASURA_GRAPHQL_ENABLE_CONSOLE to "true" and restart the graphql-engine.

The CLI will automatically open the console and for the other approach go to localhost:8080 to have the console. (Use the first one if you're using migrations and you want the changes to persist.)

Click on API tab to get to a GraphQL playground. On the bottom left, you can select query, mutation, or subscription and after hitting the plus. It's possible to customize the query and run it with the play button. Husara todo query

In the data route, you can view, add, edit, or delete your data. The action tab can be used to configure custom business logic and the events are triggered on data change (if configured in that tab). For more details on actions and events, please have a look at the Hasura docs

Troubleshooting

Some tips to issues I've had during development and how to fix them:

  • failed to get version from server: failed making version api call during start of hasura console: Check that Docker compose started and everything is running correctly. Also check if http://localhost:8080/v1/version is available in a browser - maybe the graphql-engine is not started and you're just starting the cli console too early.
  • Hasura console not starting & uninstalling not working. Manually remove hasura-cli in c:\program files\nodejs\ (3 files, hasura, hasura.cmd & hasura.ps1) and reinstall with npm i -g hasura-cli

What's next

The Docker setup is working as expected and is a good starting point for new apps with Svelte & Postgres SQL.

If your setup is working properly you should now have at least 3 running services (with-out Adminer):

  • graphql-engine for Hasura
  • postgres for the database
  • web for the frontend

The best way to check the setup is by using the Hasura console and test the Graphql playground.

So the Docker setup is finished. Now, we can focus on the frontend app with Svelte.

You can continue with part 2.

©2024 Alexander Wolf