Compatibility-first REST API for Tanzania location data backed by PostgreSQL and Prisma ORM 7.
- Prisma ORM 7 with a generated client in
src/generated/prisma - Dual API base paths:
/v1is canonical and/apiremains as a compatibility alias - Reproducible Prisma migration + seed flow for local development and CI
- Request IDs, structured HTTP logs, cache headers, and safer full-text search
- Dependabot and GitHub Actions for ongoing dependency updates and verification
- Node.js
22.13.0+ - pnpm
10.7.0+ - PostgreSQL
16+recommended
-
Install dependencies.
pnpm install
-
Create your environment file.
cp .env.example .env
-
Start PostgreSQL and update your connection strings if needed.
- Local and test environments use a direct PostgreSQL
DATABASE_URL. - Production can use either a direct PostgreSQL
DATABASE_URLor a Prisma AccelerateDATABASE_URL. - If
DATABASE_URLpoints at Prisma Accelerate, also provideDIRECT_DATABASE_URLso migrations can talk to Postgres directly. - Legacy environments that already use
DIRECT_URLare still accepted as a fallback for direct Postgres access.
- Local and test environments use a direct PostgreSQL
-
Apply the checked-in schema and seed deterministic fixture data.
pnpm db:migrate pnpm db:seed
โ ๏ธ WARNING:pnpm db:seedis destructive โ it truncates all tables before inserting fixture data. Do not run it against a database you need to preserve. -
Start the development server.
pnpm dev
pnpm db:migrate
pnpm db:seed
pnpm lint
pnpm typecheck
pnpm build
pnpm test
pnpm test:ci
pnpm openapi:json-
API routes are protected by per-IP rate limits with both sustained and burst thresholds
-
/searchhas a stricter limit than the rest of the API because it is the easiest expensive endpoint to abuse -
Request bodies are capped with
REQUEST_BODY_LIMIT, even though the public API is mostly read-only -
Rate limiting keys off Express
req.ip; if you deploy behind a trusted proxy/load balancer, setTRUST_PROXYso Express resolves the real client IP correctly -
All limits are configurable with environment variables:
REQUEST_BODY_LIMIT=16kb TRUST_PROXY="loopback, linklocal, uniquelocal" RATE_LIMIT_WINDOW_MS=60000 RATE_LIMIT_MAX_REQUESTS=120 RATE_LIMIT_BURST_WINDOW_MS=10000 RATE_LIMIT_BURST_MAX_REQUESTS=30 SEARCH_RATE_LIMIT_WINDOW_MS=60000 SEARCH_RATE_LIMIT_MAX_REQUESTS=30 SEARCH_RATE_LIMIT_BURST_WINDOW_MS=10000 SEARCH_RATE_LIMIT_BURST_MAX_REQUESTS=10
pnpm db:migrateis the supported entrypoint for schema changes in this repo- On a fresh database it bootstraps the historical
initmigration, marks that baseline as applied, and then deploys later migrations - On an existing database that already has the older Prisma migration history, it only applies the new additive migrations
- Prefer
pnpm db:migrateover callingprisma migrate deploydirectly DATABASE_URLmay point at direct Postgres or Prisma Accelerate- If
DATABASE_URLpoints at Prisma Accelerate,pnpm db:migratestill requires a direct Postgres URL inDIRECT_DATABASE_URL DIRECT_URLremains supported as a legacy alias forDIRECT_DATABASE_URL
-
pnpm testexpects a database that has already been migrated and seeded -
pnpm test:cirunsgenerate,db:migrate,db:seed, and the Jest suite in one command -
For a clean local verification flow, run:
pnpm db:migrate pnpm db:seed pnpm test
/v1: canonical path for current integrations/api: compatibility alias for older consumers
Both base paths return the same payload shapes.
GET /v1/countriesGET /v1/regionsGET /v1/districtsGET /v1/wardsGET /v1/places
GET /v1/countries/:idGET /v1/regions/:regionCodeGET /v1/districts/:districtCodeGET /v1/wards/:wardCodeGET /v1/places/:id
GET /v1/countries/:countryCode/regionsGET /v1/regions/:regionCode/districtsGET /v1/districts/:districtCode/wardsGET /v1/wards/:wardCode/places
GET /v1/search?q=nzuguni
All collection endpoints support:
pagelimitsearch
Additional filters:
/regions:countryId/districts:countryId,regionCode/wards:countryId,regionCode,districtCode/places:countryId,regionCode,districtCode,wardCode
- Swagger UI:
http://localhost:8080/api-docs - OpenAPI JSON:
http://localhost:8080/openapi.json pnpm openapi:jsonexports the spec togenerated/openapi/openapi.json
- Prisma configuration lives in prisma.config.ts
- The checked-in migration chain now creates the
general.search_vectorcolumn, trigger, and GIN index used by/search - Seed data is intentionally small and deterministic so CI and tests can assert exact results
- The seed is destructive by design for local/CI fixture setup; do not run it against a database you expect to preserve unchanged
.github/dependabot.ymlopens weekly update PRs for npm packages and GitHub Actions.github/workflows/ci.ymlvalidates every PR against Postgres on Node22.13.0
pnpm prepareandpnpm hooks:installconfigurecore.hooksPathto.githooks- Pre-commit runs
pnpm hooks:pre-commit(lint+typecheck) - Pre-push runs
pnpm hooks:pre-push, which first builds the app, then creates a temporary Postgres database and runspnpm test:ci - Pre-push requires
DIRECT_DATABASE_URLor legacyDIRECT_URLto be a direct PostgreSQL URL - Pre-push refuses non-local databases by default; set
ALLOW_REMOTE_PREPUSH_DB=1only if you intentionally want hook verification against a remote direct Postgres instance
This project is licensed under the CopyLeft License. See LICENSE.