|
1 | 1 | # Location Data API |
2 | 2 |
|
3 | | -A RESTful API for Tanzania location data including countries, regions, districts, wards, and places. |
| 3 | +Compatibility-first REST API for Tanzania location data backed by PostgreSQL and Prisma ORM 7. |
4 | 4 |
|
5 | | -## Features |
| 5 | +## What Changed |
6 | 6 |
|
7 | | -- Hierarchical location data with proper relationships |
8 | | -- RESTful API with clean structure |
9 | | -- Input validation and error handling |
10 | | -- Pagination and search support |
| 7 | +- Prisma ORM 7 with a generated client in `src/generated/prisma` |
| 8 | +- Dual API base paths: `/v1` is canonical and `/api` remains as a compatibility alias |
| 9 | +- Reproducible Prisma migration + seed flow for local development and CI |
| 10 | +- Request IDs, structured HTTP logs, cache headers, and safer full-text search |
| 11 | +- Dependabot and GitHub Actions for ongoing dependency updates and verification |
11 | 12 |
|
12 | | -## Tech Stack |
| 13 | +## Requirements |
13 | 14 |
|
14 | | -- Node.js / Express |
15 | | -- PostgreSQL |
16 | | -- Prisma ORM |
17 | | -- Jest & Supertest for testing |
| 15 | +- Node.js `>=20.19.0` |
| 16 | +- pnpm `10.7.0+` |
| 17 | +- PostgreSQL `16+` recommended |
18 | 18 |
|
19 | | -## Getting Started |
| 19 | +## Quick Start |
20 | 20 |
|
21 | | -### Prerequisites |
| 21 | +1. Install dependencies. |
22 | 22 |
|
23 | | -- Node.js LTS |
24 | | -- [Tanzania Locations Database](https://github.com/HackEAC/tanzania-locations-db) running 🏃🏿♂️🏃🏿♀️ |
25 | | -- npm or yarn |
26 | | - |
27 | | -### Installation |
| 23 | + ```bash |
| 24 | + pnpm install |
| 25 | + ``` |
28 | 26 |
|
29 | | -1. Clone the repository |
| 27 | +2. Create your environment file. |
30 | 28 |
|
31 | 29 | ```bash |
32 | | - git clone https://github.com/yourusername/locations-API.git |
33 | | - cd locations-API |
| 30 | + cp .env.example .env |
34 | 31 | ``` |
35 | 32 |
|
36 | | -2. Install dependencies |
| 33 | +3. Start PostgreSQL and update `DATABASE_URL` if needed. |
| 34 | + |
| 35 | +4. Apply the checked-in schema and seed deterministic fixture data. |
37 | 36 |
|
38 | 37 | ```bash |
39 | | - npm install |
| 38 | + pnpm db:migrate |
| 39 | + pnpm db:seed |
40 | 40 | ``` |
41 | 41 |
|
42 | | -3. Create `.env` for your environment |
| 42 | + > ⚠️ **WARNING**: `pnpm db:seed` is destructive — it truncates all tables before inserting fixture data. Do not run it against a database you need to preserve. |
| 43 | +
|
| 44 | +5. Start the development server. |
43 | 45 |
|
44 | 46 | ```bash |
45 | | - echo DATABASE_URL="postgresql://postgres:password@localhost:5433/locations" > .env |
| 47 | + pnpm dev |
46 | 48 | ``` |
47 | 49 |
|
48 | | - The above `DATABASE_URL` is for the [Tanzania-locations-database](https://github.com/HackEAC/tanzania-locations-db) Docker container provision. |
| 50 | +## Useful Scripts |
49 | 51 |
|
50 | | -4. Sync up your API with the locations database: |
| 52 | +```bash |
| 53 | +pnpm db:migrate |
| 54 | +pnpm db:seed |
| 55 | +pnpm lint |
| 56 | +pnpm typecheck |
| 57 | +pnpm build |
| 58 | +pnpm test |
| 59 | +pnpm test:ci |
| 60 | +pnpm openapi:json |
| 61 | +``` |
51 | 62 |
|
52 | | - - **a.** Pull existing DB schema into your Prisma schema |
| 63 | +## Migration Behavior |
53 | 64 |
|
54 | | - ```bash |
55 | | - pnpx prisma db pull |
56 | | - ``` |
| 65 | +- `pnpm db:migrate` is the supported entrypoint for schema changes in this repo |
| 66 | +- On a fresh database it bootstraps the historical `init` migration, marks that baseline as applied, and then deploys later migrations |
| 67 | +- On an existing database that already has the older Prisma migration history, it only applies the new additive migrations |
| 68 | +- Prefer `pnpm db:migrate` over calling `prisma migrate deploy` directly |
57 | 69 |
|
58 | | - - **b.** Create migration init files |
| 70 | +## Testing |
59 | 71 |
|
60 | | - ```bash |
61 | | - mkdir prisma/migrations/init |
62 | | - ``` |
| 72 | +- `pnpm test` expects a database that has already been migrated and seeded |
| 73 | +- `pnpm test:ci` runs `generate`, `db:migrate`, `db:seed`, and the Jest suite in one command |
| 74 | +- For a clean local verification flow, run: |
63 | 75 |
|
64 | | - - **c.** Mark the current schema as baseline |
| 76 | + ```bash |
| 77 | + pnpm db:migrate |
| 78 | + pnpm db:seed |
| 79 | + pnpm test |
| 80 | + ``` |
65 | 81 |
|
66 | | - ```bash |
67 | | - pnpx prisma migrate diff \ |
68 | | - --from-empty \ |
69 | | - --to-schema-datamodel prisma/schema.prisma \ |
70 | | - --script > prisma/migrations/init/migration.sql |
71 | | - ``` |
| 82 | +## API Base Paths |
72 | 83 |
|
73 | | - - **d.** Create migration history manually |
| 84 | +- `/v1`: canonical path for current integrations |
| 85 | +- `/api`: compatibility alias for older consumers |
74 | 86 |
|
75 | | - ```bash |
76 | | - pnpx prisma migrate resolve --applied init |
77 | | - ``` |
| 87 | +Both base paths return the same payload shapes. |
78 | 88 |
|
79 | | - ✅ Now you're synced! Future `prisma migrate dev` or `migrate deploy` will work cleanly. |
| 89 | +## Main Endpoints |
80 | 90 |
|
81 | | -5. Start development server |
| 91 | +### Collections |
82 | 92 |
|
83 | | - ```bash |
84 | | - npm run dev |
85 | | - ``` |
| 93 | +- `GET /v1/countries` |
| 94 | +- `GET /v1/regions` |
| 95 | +- `GET /v1/districts` |
| 96 | +- `GET /v1/wards` |
| 97 | +- `GET /v1/places` |
86 | 98 |
|
87 | | -6. Build application |
| 99 | +### Detail Routes |
88 | 100 |
|
89 | | - ```bash |
90 | | - npm run build |
91 | | - ``` |
| 101 | +- `GET /v1/countries/:id` |
| 102 | +- `GET /v1/regions/:regionCode` |
| 103 | +- `GET /v1/districts/:districtCode` |
| 104 | +- `GET /v1/wards/:wardCode` |
| 105 | +- `GET /v1/places/:id` |
92 | 106 |
|
93 | | -7. Start production server |
| 107 | +### Nested Routes |
94 | 108 |
|
95 | | - ```bash |
96 | | - npm run start |
97 | | - ``` |
| 109 | +- `GET /v1/countries/:countryCode/regions` |
| 110 | +- `GET /v1/regions/:regionCode/districts` |
| 111 | +- `GET /v1/districts/:districtCode/wards` |
| 112 | +- `GET /v1/wards/:wardCode/places` |
98 | 113 |
|
99 | | -## API Endpoints |
| 114 | +### Search |
100 | 115 |
|
101 | | -### Countries |
102 | | -- `GET /v1/countries` - Get all countries |
103 | | -- `GET /v1/countries/:id` - Get country by ID |
| 116 | +- `GET /v1/search?q=nzuguni` |
104 | 117 |
|
105 | | -### Regions |
106 | | -- `GET /v1/regions` - Get all regions |
107 | | -- `GET /v1/regions/:regionCode` - Get region by code |
108 | | -- `GET /v1/regions/:regionCode/districts` - Get districts in a region |
| 118 | +## Collection Query Parameters |
109 | 119 |
|
110 | | -### Districts |
111 | | -- `GET /v1/districts` - Get all districts |
112 | | -- `GET /v1/districts/:districtCode` - Get district by code |
113 | | -- `GET /v1/districts/:districtCode/wards` - Get wards in a district |
| 120 | +All collection endpoints support: |
114 | 121 |
|
115 | | -### Wards |
116 | | -- `GET /v1/wards` - Get all wards |
117 | | -- `GET /v1/wards/:wardCode` - Get ward by code |
118 | | -- `GET /v1/wards/:wardCode/places` - Get places in a ward |
| 122 | +- `page` |
| 123 | +- `limit` |
| 124 | +- `search` |
119 | 125 |
|
120 | | -### Places |
121 | | -- `GET /v1/places` - Get all places |
122 | | -- `GET /v1/places/:id` - Get place by ID |
| 126 | +Additional filters: |
123 | 127 |
|
124 | | -### Search |
125 | | -- `GET /v1/search?q=nzuguni` - Fulltext search for locations by name |
| 128 | +- `/regions`: `countryId` |
| 129 | +- `/districts`: `countryId`, `regionCode` |
| 130 | +- `/wards`: `countryId`, `regionCode`, `districtCode` |
| 131 | +- `/places`: `countryId`, `regionCode`, `districtCode`, `wardCode` |
126 | 132 |
|
127 | | -## Running Tests |
| 133 | +## Docs |
128 | 134 |
|
129 | | -```bash |
130 | | -npm test |
131 | | -``` |
| 135 | +- Swagger UI: `http://localhost:8080/api-docs` |
| 136 | +- OpenAPI JSON: `http://localhost:8080/openapi.json` |
| 137 | +- `pnpm openapi:json` exports the spec to `generated/openapi/openapi.json` |
| 138 | + |
| 139 | +## Database Notes |
| 140 | + |
| 141 | +- Prisma configuration lives in [prisma.config.ts](./prisma.config.ts) |
| 142 | +- The checked-in migration chain now creates the `general.search_vector` column, trigger, and GIN index used by `/search` |
| 143 | +- Seed data is intentionally small and deterministic so CI and tests can assert exact results |
| 144 | +- The seed is destructive by design for local/CI fixture setup; do not run it against a database you expect to preserve unchanged |
| 145 | + |
| 146 | +## Dependency Automation |
| 147 | + |
| 148 | +- `.github/dependabot.yml` opens weekly update PRs for npm packages and GitHub Actions |
| 149 | +- `.github/workflows/ci.yml` validates every PR against Postgres on Node `20.19.0` and `22` |
132 | 150 |
|
133 | 151 | ## License |
134 | 152 |
|
135 | | -This project is licensed under the CopyLeft License – see the LICENSE file for details. |
| 153 | +This project is licensed under the CopyLeft License. See [LICENSE](./LICENSE). |
0 commit comments