Two supported workflows:
- Full Docker stack (recommended) — API, workers, Postgres, Redis, MinIO
- Host Node + Compose infra — only databases/cache in Docker
Full Docker details: docker.md.
| Tool | Full Docker | Host Node |
|---|---|---|
| Docker / Podman + Compose | Yes | Yes (for infra) |
| Node.js 20+ and npm | Optional (smoke UI only) | Yes |
docker compose up --build
curl -sS http://localhost:8080/v1/health| Endpoint | URL |
|---|---|
| API | http://localhost:8080 |
| Postgres (host tools) | localhost:55432 |
| Redis | localhost:56379 |
| MinIO API / console | http://localhost:59000 / :59001 |
Compose automatically runs schema migrate and seed. Internal hostnames
are postgres, redis, and minio. App services use:
API_KEY_PEPPER=compose-local-pepper-change-meARTIFACT_STORAGE_MODE=s3→ MinIO
docker compose exec api node dist/cli/create-api-client.js \
--name "Local Dev" \
--tier free \
--scopes read:public,read:internal,write:ingestion
export API_KEY='<printed api_key>'Or use the smoke UI create-key page (below).
# SQL fixture rows (fixed historical dates in migrations)
docker compose --profile fixtures run --rm migrate-fixtures
# HTML fixture ingestion (uses dates inside fixtures/ — often “today”)
docker compose run --rm --no-deps \
-v "$PWD/fixtures:/app/fixtures:ro" \
worker node dist/workers/ingestion-worker.js --fixture ppac_rsp
docker compose run --rm --no-deps \
-v "$PWD/fixtures:/app/fixtures:ro" \
worker node dist/workers/ingestion-worker.js --fixture igl_pricesFixtures are not inside the image; the -v mount is required for HTML ingestion.
Browser UI for PIN price lookup and creating API keys:
cd .smoke-frontend
npm install
npm run dev| Page | URL |
|---|---|
| Prices | http://localhost:5173 |
| Create key | http://localhost:5173/keys.html |
Vite proxies /api → http://localhost:8080 by default (VITE_API_TARGET).
Create-key uses Compose DB defaults (localhost:55432 + Compose pepper). If you
override API pepper, set CREATE_CLIENT_API_KEY_PEPPER / API_KEY_PEPPER in
.smoke-frontend/.env to the same value.
See .smoke-frontend/README.md.
curl -sS http://localhost:8080/v1/health
curl -sS -H "Authorization: Bearer $API_KEY" \
"http://localhost:8080/v1/prices?pin=110001&fuel=PETROL"
curl -sS -H "Authorization: Bearer $API_KEY" \
"http://localhost:8080/v1/prices/history?pin=110001&fuel=PETROL&from=2026-07-01&to=2026-07-11"
curl -sS -H "Authorization: Bearer $API_KEY" \
"http://localhost:8080/v1/prices/price_example/components"
curl -sS -H "Authorization: Bearer $API_KEY" \
"http://localhost:8080/v1/stations?pin=110001"
curl -sS -H "Authorization: Bearer $API_KEY" \
"http://localhost:8080/v1/stations/outlet_example"
curl -sS -H "Authorization: Bearer $API_KEY" \
"http://localhost:8080/v1/stations/outlet_example/prices?fuel=PETROL"
curl -sS -H "Authorization: Bearer $API_KEY" \
"http://localhost:8080/v1/sources"
curl -sS -H "Authorization: Bearer $API_KEY" \
"http://localhost:8080/v1/sources/src_ppac_rsp"
curl -sS -H "Authorization: Bearer $API_KEY" \
"http://localhost:8080/v1/coverage?fuel=PETROL&state=Delhi"After HTML fixture ingestion for today, current prices for fixture PINs (e.g.
110001) return rows. SQL-only fixtures may only hit history/components for
older applicable_date values.
Use this when developing the TypeScript API with hot reload.
docker compose up -d postgres redis minio
npm ci
cp .env.example .envMinimum .env for Compose infra (note ports and pepper if you want keys to
match the Docker API later):
NODE_ENV=development
PORT=8080
DATABASE_URL=postgres://oilapi:oilapi@localhost:55432/oilapi
REDIS_URL=redis://localhost:56379
API_KEY_PEPPER=dev-pepper
ARTIFACT_STORAGE_MODE=localTo talk to MinIO from the host instead of local disk artifacts:
ARTIFACT_STORAGE_MODE=s3
S3_BUCKET=oil-api-artifacts
S3_REGION=us-east-1
S3_ENDPOINT=http://localhost:59000
S3_FORCE_PATH_STYLE=true
AWS_ACCESS_KEY_ID=oilapi
AWS_SECRET_ACCESS_KEY=oilapi-secretCreate the MinIO bucket once (Compose minio-init does this when the full stack
runs; for infra-only you may need the same init or the MinIO console).
npm run db:migrate
npm run db:seed
npm run db:fixtures # local only; blocked when NODE_ENV=production
npm run dev # API: tsx watch src/server.tsOptional workers on the host:
npx tsx src/workers/scheduler.ts
npx tsx src/workers/ingestion-worker.tsnpx tsx src/workers/ingestion-worker.ts --fixture ppac_rsp
npx tsx src/workers/ingestion-worker.ts --fixture igl_pricesPepper must match the process you will call:
# Host API with .env API_KEY_PEPPER=dev-pepper
npx tsx src/cli/create-api-client.ts \
--name "Local Dev" --tier free \
--scopes read:public,read:internal,write:ingestion
export API_KEY=<printed key>docker run --rm --name oilapi-redis -p 56379:6379 redis:7-alpineREDIS_URL=redis://localhost:56379| How you run the API | Default pepper |
|---|---|
docker compose app services |
compose-local-pepper-change-me |
Host .env.example |
dev-pepper |
Creating a key with one pepper and calling an API that uses the other yields
401 Unauthorized. Always align API_KEY_PEPPER (and smoke-frontend create
env) with the running API.
| Doc | Purpose |
|---|---|
| docker.md | Compose services, image, ops, troubleshooting |
| api-contract.md | Envelopes, auth, endpoints |
| runbook.md | Ops commands, replay, staleness |
| security.md | Keys, scopes, production requirements |