Files
baya-monorepo/archive/docs/integration/domains/geography.md
T
2026-08-02 20:01:31 +03:30

3.5 KiB

geography — provinces, cities, districts

Client seam client/src/services/geography/ · USE_GEOGRAPHY_MOCK = false (real) · 13 server ops Last verified: 2026-07-30 against commit d3ec723 and swagger.v1.json (2026-07-29).

The reference hierarchy every address, service area and search filter is keyed on. Also the home of the single most load-bearing null in the schema.

Endpoints

Method Path Auth Verdict
GET /api/v1/geo/provinces anonymous wired
GET /api/v1/geo/cities anonymous wired
GET /api/v1/geo/districts anonymous wired
GET /api/v1/geo/tree anonymous unwired — the client fetches the three levels separately and caches each
POST /api/v1/admin_geo/create_province admin unwired — no console screen
POST /api/v1/admin_geo/update_province/{id} admin unwired
POST /api/v1/admin_geo/set_province_active/{id} admin unwired
POST /api/v1/admin_geo/create_city admin unwired
POST /api/v1/admin_geo/update_city/{id} admin unwired
POST /api/v1/admin_geo/set_city_active/{id} admin unwired
POST /api/v1/admin_geo/create_district admin unwired
POST /api/v1/admin_geo/update_district/{id} admin unwired
POST /api/v1/admin_geo/set_district_active/{id} admin unwired

No phantoms. The nine admin_geo routes are DynamicPermission + sensitive; geography is seeded, so there is no console editor. Mutations are action-style (admin_geo/create_city, never admin_geo/cities).

districtId = null means whole-city

This is the one rule to get right, and it reads in both directions:

  • On a nurse service area (see service-areas.md), districtId = null means the nurse covers the entire city, not "no district".
  • On a search query, a customer in district D must match both a nurse whose area names D and a nurse whose area is whole-city. See search.md.
  • On an address it is genuinely optional metadata — a missing district does not widen anything.

Never coerce the null to 0 or to a sentinel id, and never write a query that drops whole-city rows.

Shape rules the JSON does not express

  • Every level returns both nameFa and nameEn; the client picks by locale.
  • isActive filters the pickers. An inactive city must stay resolvable — an existing address or booking references it — so it is filtered from selection, never deleted.
  • Two Neshan keys exist and they are different products. The server's geocoder key is Seams:Geocoding:ApiKey (server-side address→point); the client's map key is NEXT_PUBLIC_NESHAN_KEY (a web key for the embeddable map/search). Never share one value between them. With the client key unset, AddressMapPicker falls back to a bounded-canvas grid, which is why dev, CI and jsdom all work without it. See ../config-matrix.md.
  • Geocoding is a seam: Seams:Geocoding:Provider = mock (default) or neshan. The mock resolves a deterministic point near the city centroid; an address containing NO_GEO resolves to null coordinates so the "saved without a map pin" state is testable per-request.

Enums

None. All three levels are integer ids with localised names.

Open REQs

None. REQ-008 (accept the client-picked pin) and REQ-009 (provinceId on the address DTO) were delivered and are documented in addresses.md.