Skip to main content

NestJS Setup

hikyaku-api is the NestJS service for routing, optimisation, payments, card issuing, geocoding, invitations, mail, and scheduled jobs. For its position in the system, refer to Architecture. This guide shows how to install the API, its database, and its spatial stack (Valhalla, VROOM, Photon).

Prerequisites​

  • Node.js 20+
  • pnpm
  • Docker and Docker Compose
  • A provisioned Supabase project. If you do not have one, do the Supabase Setup guide first.
  • A Stripe account. Billing and card issuing both use Stripe. For local development, you can use test-mode keys.
  • SMTP credentials, for invitations, password resets, and other transactional email.

Environment Variables​

Copy .env.example to .env.local. Then set each variable as this section shows.

cp .env.example .env.local

DB_URL​

The Postgres connection string for the normal queries of the app.

DB_URL=postgresql://<user>:<password>@<host>:5432/postgres

DB_MIGRATION_URL​

The connection string for the TypeORM migration CLI. Migrations run only as an explicit deploy step. pnpm start:prod runs them automatically. During development, run them with pnpm migration:run. Migrations never run when the app boots.

caution

Use the Supabase Direct connection (port 5432). Do not use the 6543 transaction pooler, because DDL and advisory locks are not reliable with transaction pooling. If you do not set this variable, the CLI uses DB_URL.

DB_MIGRATION_URL=postgresql://<user>:<password>@<host>:5432/postgres

VALHALLA_URL, VROOM_URL, PHOTON_URL​

The base URLs of the three spatial services. To run these services, refer to Start the Spatial Stack.

  • The API runs directly on the host (pnpm start:dev). The spatial services run in Docker and publish their ports to localhost:

    VALHALLA_URL=http://localhost:8002
    VROOM_URL=http://localhost:3000
    PHOTON_URL=http://localhost:2322
  • The API runs in Docker, on the same hikyaku-net network as the spatial services. The services publish no ports:

    VALHALLA_URL=http://valhalla:8002
    VROOM_URL=http://vroom:3000
    PHOTON_URL=http://photon:2322

SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY​

The same Supabase project as the web frontend. The API uses the service role key, not the anon key.

How to find them:

  1. In Project Settings → API, copy the Project URL.
  2. On the same page, under Project API keys, copy the service role key. Do not copy the anon/public key.
SUPABASE_URL=https://abcdefghijklmnop.supabase.co
SUPABASE_SERVICE_ROLE_KEY=sb_secret_xxxxxxxxxxxx
caution

The service role key bypasses row-level security. Keep it only in this server-side environment. Do not put it in the web application.


CORS_DOMAIN​

A comma-separated list of the origins that can call this API. Usually, this is the origin of your frontend.

CORS_DOMAIN=http://localhost:3000,https://hikyaku.org

STRIPE_API_KEY, STRIPE_PUBLISHABLE_KEY, STRIPE_ISSUING_WEBHOOK_SECRET​

Billing and card issuing use Stripe.

How to find them:

  1. In the Stripe Dashboard, copy the secret key and the publishable key for your mode (test or live).
  2. For STRIPE_ISSUING_WEBHOOK_SECRET, use one of these methods:
    • Run stripe listen locally. Use the signing secret that it shows.
    • Create a webhook endpoint for issuing.* events. Copy its signing secret from the Dashboard.
STRIPE_API_KEY=sk_test_xxxxxxxx
STRIPE_PUBLISHABLE_KEY=pk_test_xxxxxx
STRIPE_ISSUING_WEBHOOK_SECRET=whsec_xxxxxx

MAILER_SENDER_EMAIL, MAILER_HOST, MAILER_USER, MAILER_PASSWORD​

The SMTP credentials that the API uses to send invitations, password resets, and other transactional email.

MAILER_SENDER_EMAIL=auth@hikyaku.org
MAILER_HOST=smtp.your-provider.com
MAILER_USER=xxxx
MAILER_PASSWORD=xxxxxx

APP_URL​

The frontend origin. The API uses it to make the links in emails.

APP_URL=http://localhost:3000

SENTRY_DSN​

Optional. Sentry error tracking is enabled only when you set this variable. To disable Sentry, leave it empty.


PORT​

Optional. This variable is not in .env.example. By default, the API listens on port 3002. To use a different port, set PORT.

Bootstrap the Database​

Option A: Docker Compose job

cd infra/db
docker compose up

This job runs setup_db.sh and then refresh_timezones.sh in a temporary container. Before you start the job, edit the DB_HOST, DB_PORT, DB_NAME, DB_USER, and DB_PASSWORD values in infra/db/docker-compose.yml. Use the Supabase Direct connection host, not the pooler, because the roles and schema scripts run DDL.

Option B: Run the scripts directly

cd infra/db
./setup_db.sh # prompts for DB_HOST/PORT/NAME/USER/PASSWORD if unset
./refresh_timezones.sh # populates the timezone table

If time zones change in the world, run refresh_timezones again.

After this baseline, TypeORM migrations in src/database/migrations apply all schema changes. pnpm start:prod applies them automatically. During development, apply them with pnpm migration:run.

Start the Spatial Stack​

Valhalla, VROOM, and Photon run as a separate Compose project. Thus, you can redeploy the API without a rebuild of the spatial tiles, which is slow. By default, these services publish no ports. Only other containers on a shared Docker network can reach them. They are not available on the internet.

For topics that this guide does not include, refer to the documentation of each project:

  1. Create the shared network. Do this step one time only.

    docker network create hikyaku-net
  2. Put these files in infra/. The Compose stack needs them.

    • vroom-conf/config.yml. Set routingServers.valhalla to http://valhalla:8002.
    • photon/photon-1.1.0.jar and photon/photon_data/. These are the Photon jar and a prebuilt geocoding index.
    • valhalla_tiles/. Valhalla creates this directory automatically at first boot.
  3. Start the stack:

    docker compose -f infra/spatial-docker-compose.yml up -d
caution

The first boot is slow. Valhalla downloads a regional OSM extract and builds routing tiles before it answers requests. To monitor the progress, run docker compose -f infra/spatial-docker-compose.yml logs -f valhalla.

If the API runs directly on the host and not in Docker, bind the spatial ports to localhost. Then the API can reach them. Add a ports entry for each service (for example, "127.0.0.1:8002:8002"). Do not publish the ports to all interfaces. For the full exposure table (localhost, LAN, tailnet, or public), refer to infra/README.md.

Run the API​

Development. Make sure that the database is bootstrapped and that the API can reach the spatial stack. Then run:

pnpm install
pnpm start:dev

By default, the API listens on http://localhost:3002. The interactive Swagger docs are at http://localhost:3002/api-docs.

Self-hosted with Docker. This method uses the prebuilt image.

  1. Create .env.prod in the same directory as infra/docker-compose.yml. Use the same variables as in the section above.

  2. Start the API:

    cd infra
    docker compose up -d

This command runs the migrations and then starts the server. For production, the Compose file has a Cloudflare Tunnel service in a comment. The tunnel is the correct path to the internet, not published ports. For the reason, refer to Architecture.

Post-Setup Checklist​