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.
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 tolocalhost:VALHALLA_URL=http://localhost:8002VROOM_URL=http://localhost:3000PHOTON_URL=http://localhost:2322 -
The API runs in Docker, on the same
hikyaku-netnetwork as the spatial services. The services publish no ports:VALHALLA_URL=http://valhalla:8002VROOM_URL=http://vroom:3000PHOTON_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:
- In Project Settings → API, copy the Project URL.
- 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
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:
- In the Stripe Dashboard, copy the secret key and the publishable key for your mode (test or live).
- For
STRIPE_ISSUING_WEBHOOK_SECRET, use one of these methods:- Run
stripe listenlocally. Use the signing secret that it shows. - Create a webhook endpoint for
issuing.*events. Copy its signing secret from the Dashboard.
- Run
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:
-
Create the shared network. Do this step one time only.
docker network create hikyaku-net -
Put these files in
infra/. The Compose stack needs them.vroom-conf/config.yml. SetroutingServers.valhallatohttp://valhalla:8002.photon/photon-1.1.0.jarandphoton/photon_data/. These are the Photon jar and a prebuilt geocoding index.valhalla_tiles/. Valhalla creates this directory automatically at first boot.
-
Start the stack:
docker compose -f infra/spatial-docker-compose.yml up -d
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.
-
Create
.env.prodin the same directory asinfra/docker-compose.yml. Use the same variables as in the section above. -
Start the API:
cd infradocker 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.