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.
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.
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 aDockerfile
in thehasura
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 thegraphql-engine
will likely restart multiple times until the port is ready.
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:
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.
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());
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.
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.
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
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
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):
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.