Setting up a Walking Skeleton

Accessing Database from Deno Server-Side Application


Learning Objectives

  • You know how to access a database from a Deno-based server-side application.

Here, we add a client to access the database from the Deno-based server-side application and modify the application to read data from the database.

Database client and setup

When working with databases, we need a database client that takes care of the communication with the database. In our case, we use Postgres.js.

Modify the deno.json file in the server folder to match the following.

{
  "imports": {
    "@hono/hono": "jsr:@hono/hono@4.8.12",
    "postgres": "npm:postgres@3.4.7"
  }
}

The above modification adds the Postgres.js client as a dependency to the server-side application, after which the dependency can be imported using import postgres from "postgres";. This allows us to create an instance of the database client and use it to make queries to the database.

The npm: prefix is used to indicate that the dependency is from the npm registry. Deno supports importing dependencies from multiple registries.

Loading Exercise...

Then, as the database client expects a certain format for the connection details, we need to modify the environment variables. Modify the project.env to match the following.

POSTGRES_USER=username
POSTGRES_PASSWORD=password
POSTGRES_DB=database

FLYWAY_USER=username
FLYWAY_PASSWORD=password
FLYWAY_URL=jdbc:postgresql://postgresql_database:5432/database

PGUSER=username
PGPASSWORD=password
PGDATABASE=database
PGHOST=postgresql_database
PGPORT=5432

The above modification adds the PG* environment variables that the Postgres.js client expects. Note that the PGHOST is set to the name of the database service defined in the compose.yaml, which is postgresql_database.

Finally, modify the compose.yaml configuration of the server service to match the following. The key changes are adding the depends_on directive to state that the server service depends on the database service and should be started after it, and the env_file directive to load the environment variables from the project.env file.

  server:
    build: server
    restart: unless-stopped
    volumes:
      - ./server:/app
    ports:
      - 8000:8000
    env_file:
      - project.env
    depends_on:
      - database

With these changes, the server-side application has a driver for accessing the database service and the necessary environment variables needed to actually connect to the database.

Loading Exercise...

Accessing database from server-side application

To access the database from the server-side application, we need to create an instance of the database client and use it to make queries to the database.

Let’s modify the server-side application to import the database client and to make an instance of it, and then add an endpoint that retrieves todos from the database. Modify the app.js to match the following.

import { Hono } from "@hono/hono";
import { cors } from "@hono/hono/cors";
import { logger } from "@hono/hono/logger";
// importing database client
import postgres from "postgres";

const app = new Hono();
// creating an instance of the database client
const sql = postgres();

app.use("/*", cors());
app.use("/*", logger());

let visits = 0;
app.get("/api/visits", (c) => {
  visits++;
  return c.json({ visits });
});

// retrieving todos from database on requests to /api/todos
app.get("/api/todos", async (c) => {
  const todos = await sql`SELECT * FROM todos`;
  return c.json(todos);
});

export default app;

Now, when we run docker compose up --build, we see that the project starts up. In addition, we can access the todos from the database by making a request to the server.

curl localhost:8000/api/todos
[]%

As there are no todos in the database, the response is an empty array.

Where's the password?

We can access the database even though we did not provide the password in the code. This is because the Postgres.js client reads the connection details from the environment variables, which we set in the project.env file.

More specifically, Docker Compose loads the environment variables from the project.env file to the environment of the server service, and the Postgres.js client reads the connection details from there.


Loading Exercise...

Adding data to the database

Let’s visit the database through the command line and add a todo.

docker exec -it postgresql_database psql -U username database
psql (17.5 (Debian 17.5-1.pgdg120+1))
Type "help" for help.

database=# INSERT INTO todos (name) VALUES ('Finish walking skeleton');
INSERT 0 1
database=# \q

Now, when we make a request to the server, we see the todo in the response.

curl localhost:8000/api/todos
[{"id":1,"name":"Finish walking skeleton","created_at":"2025-08-02T09:10:39.835Z"}]%
Loading Exercise...

Summary

In summary:

  • We added a database client to the Deno-based server-side application.
  • We modified the server-side application to access the database and retrieve data from it.
  • We added data to the database and verified that the server-side application can access it.

The file structure of the walking skeleton did not change. However, the server-side application now has the functionality for accessing the database.

Loading Exercise...