Complete API contracts for the Geo module, including routes, auth, DTOs, responses, errors, examples, and integration notes.
Audience: Frontend engineers, mobile engineers, backend engineers, QA, and API consumers.
Scope: The two public reference-data endpoints for Nepal's provinces and districts.
Area Files Inspected What Was Verified Controllers apps/api/src/modules/geo/customer/geo-customer.controller.tsRoutes, methods, guards, rate limits DTOs customer/dto/*.tsQuery validation, pagination flags Services customer/geo-customer.service.tsOrdering, province filter Schema packages/db/src/schema/geo/*.tsCodes, relations Error registry apps/api/src/common/types/error-codes.ts (// GEO)GEO_* codes
Field Value Module name geoModule slug geoPrimary actors guest (any caller), internal systemAPI surfaces mobile (public)Base route prefixes /api/mobile/geoAuth model @Public()Persistence PostgreSQL (seeded reference tables) Runtime source of truth PostgreSQL tables Sibling docs Backend , Features and flows
Term Meaning Source File Used By codeStable machine key — provinces get renamed, codes do not schema Pickers, logic displayOrderOfficial province order (not alphabetical) schema Provinces provinceId / districtIdUUID v7 public ids schema Filters, other modules' FKs
Surface Method Path Actor Auth/Guard Permission Controller Purpose Public GET/api/mobile/geo/provincesGuest @Public() + IpThrottle— GeoCustomerControllerList 7 provinces Public GET/api/mobile/geo/districtsGuest @Public() + IpThrottle— GeoCustomerControllerList 77 districts (or one province's)
Surface Guard/Decorator Identity Shape Permission Guest Allowed Notes Public @Public() + IpThrottlerGuardNone N/A Yes PUBLIC_READ 60/min per IP
DTO Fields Validation GeoListQueryDtopagination (bool, default true per QueryDto base), page, sizeshared QueryDto ListDistrictsQueryDtoprovinceId (UUID v7, optional), + QueryDto base@IsUUID("7")
DTO Fields ProvinceResponseDtoid, code, name, displayOrderDistrictResponseDtoid, code, name, province: { id, code, name }
None — province/district codes are data, not enums.
The province picker. Seven entries in official order; key on code, display name.
@Public(); PUBLIC_READ 60/min; no permission.
200 — data array of { id, code, name, displayOrder }.
{
"message" : "Provinces fetched successfully" ,
"errorCode" : null ,
"data" : [
{ "id" : "0198…" , "code" : "koshi" , "name" : "Koshi" , "displayOrder" : 1 },
{ "id" : "0198…" , "code" : "bagmati" , "name" : "Bagmati" , "displayOrder" : 3 }
]
}
None — a list of a fixed table cannot 404.
The district picker — all 77, or one province's, ordered by name.
Part Required Details Query No provinceId (UUID v7), pagination, page, size
200 — data array of { id, code, name, province }.
{
"message" : "Districts fetched successfully" ,
"errorCode" : null ,
"data" : [
{
"id" : "0198…" ,
"code" : "kathmandu" ,
"name" : "Kathmandu" ,
"province" : { "id" : "0198…" , "code" : "bagmati" , "name" : "Bagmati" }
}
]
}
HTTP Code Condition 404 GEO_PROVINCE_NOT_FOUNDprovinceId names no province
The four merge-trap codes (rukum-east/rukum-west, nawalpur/parasi) are distinct rows in different provinces — never merge them.
Endpoint Pagination Type Default Size Max Size Sort Fields Filters Result Cap /provincesoffset (opt-in) 20 100 displayOrder (fixed)— 7 /districtsoffset (opt-in) 20 100 name (fixed)provinceId77
Unpaginated by default — the whole list is the contract for a picker.
Integration Used? Details Redis cache No 84 rows; direct indexed reads BullMQ No — External API No —
Route Controller Method DTOs Service Method Guards Permissions Cache Jobs DB Touches Errors Documented? GET /api/mobile/geo/provinceslistProvincesGeoListQueryDtoGeoCustomerService.listProvincesPublic+IpThrottle — — — province — Yes GET /api/mobile/geo/districtslistDistrictsListDistrictsQueryDto…listDistrictsPublic+IpThrottle — — — district, province 404 Yes
Covered in §8: full responses (§8.1/8.2), empty-list behavior (empty data before seeding), domain error (404 GEO_PROVINCE_NOT_FOUND), rate-limit behavior (429).
Route ownership (§9.1), request sequence (§9.2).
Consumer Required Knowledge Failure Handling Contract Stability Web frontend Key on code, not name; four split-district codes 404 → refresh list Stable Mobile app PUBLIC_READ 60/min shared budget429 → back off Stable Internal services Resolve districts via GeoLookupService, never direct reads 404 → treat as bad input Stable
Decision Chosen Behavior Alternatives Considered Why This Tradeoff Risk Mitigation Public, unauthenticated Open reads Auth-gated Picker works pre-login Load Rate limit Unpaginated default Full list Page-by-default Picker needs all rows Payload size Tiny data Codes as keys Stable keys Names Renames happen — Documented
Change Affected Consumers Backend Impact Data Impact Migration Needed? Compatibility Plan Province rename Pickers showing name Seed update name columnNo code stable
Every controller route is documented (§4, §8, §13.1).
Every parent route prefix and runtime URL is documented (§2, §4).
Every DTO field, enum, default, transform and validator is documented (§6).
Every response field and nullable field is documented (§8).
Every auth, guard, permission and guest identity branch is documented (§5).
Every success and error branch is documented (§8).
Every DB read, cache and external call is documented (§11).
Every route has examples for success responses and representative failures (§8).
Every endpoint family has route and sequence diagrams (§9).
Every tradeoff and compatibility risk is documented (§13.5, §13.6).
The API doc links to backend and features/flows (§1, See Also).