Skip to content

feat: add public read-only places API (#123) - #137

Merged
yakew7 merged 1 commit into
StudentSuite:mainfrom
aryansk:feat/123-public-places-api
Aug 15, 2026
Merged

feat: add public read-only places API (#123)#137
yakew7 merged 1 commit into
StudentSuite:mainfrom
aryansk:feat/123-public-places-api

Conversation

@aryansk

@aryansk aryansk commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

What

GET /api/places exposes the merged data/places/*.json dataset read-only, so consumers can build on top of it without cloning the repo. Per the issue's "document the existing contract rather than inventing a second one", records are exactly the schema's Place shape — no new fields, no wrappers.

Filterscity (case-insensitive; spaces/hyphens normalize to the dataset's underscore slugs, so New Delhi matches new delhi) and category (one of the six PLACE_TYPES). Unknown category, empty/repeated params, and malformed limit/offset return 400 with a message — never silently ignored.

Pagination — default limit 100, hard max 500, values above clamped (per the issue's own verification example), zero-based offset, and total = matches before slicing so consumers can page to the end.

Headers — permissive CORS (Access-Control-Allow-Origin: *) and cache headers matching reality (dataset changes a few times a week): 6h ISR revalidate + Cache-Control: public, max-age=3600, s-maxage=21600, stale-while-revalidate=86400.

Docs — new /docs/places-api page in the Developers group: shape, filters, pagination, errors, and a link to data/places.schema.json.

One decision to flag: country

The issue lists a country filter, but the dataset has no country field — the schema is id/name/type/city/lat/lng/address/gmaps_link/added_by (+ optional verified/exam/valid_till), and none of the 328 records across 217 cities carry one. Rather than invent a city→country mapping (guesswork for ambiguous cities like springfield/reading, and a second contract the issue warns against), the API rejects country with an explicit 400 explaining the gap and pointing at the docs. The alternative — silently returning everything — seemed worse.

If you'd like country filtering, the cleanest path is adding a country field to the schema + dataset (the API's country branch is a one-line change then), or a maintainer-endorsed city→country table. Happy to implement either if you tell me which.

Verification

  • 76 vitest tests (24 new): the full parse/query contract (defaults, clamp, 400 cases incl. the country decision) plus end-to-end route-handler tests hitting the real GET handler — including the issue's own examples (?city=Mumbai&limit=5, ?limit=999999 clamps).
  • npx eslint clean on changed files, npm run build clean — both /api/places and /docs/places-api emitted (I also verified the route handler in-process: status codes, CORS + cache headers, filters, clamp, 400s).

Closes #123

GET /api/places exposes the merged data/places/*.json dataset
programmatically, so anything can build on top of it without cloning the
repo. Reuses the existing record contract (data/places.schema.json) rather
than inventing a second one.

- Filters: `city` (case-insensitive, slug-normalized like the city pages)
  and `category` (one of the six PLACE_TYPES). Unknown categories, empty or
  repeated values, and malformed limit/offset return 400 with a message —
  never silently ignored.
- Pagination: default limit 100, hard maximum 500 (values above are
  clamped, not dumped), zero-based `offset`, and `total` reflecting the
  full match set before slicing.
- The `country` filter is rejected with an explicit 400: the schema has no
  country field, so no record could satisfy it. Flagged for maintainer
  input rather than inventing a city->country mapping.
- Headers: permissive CORS (Access-Control-Allow-Origin: *) and cache
  headers matching reality (dataset changes a few times a week: 6h ISR
  revalidate + stale-while-revalidate).
- Docs: new /docs/places-api page (registered in the Developers group)
  documenting the shape, filters, pagination, and errors, linking the
  schema.

76 vitest tests (24 new: full parse/query contract unit tests plus
end-to-end route-handler tests covering the issue's own verification
examples), eslint clean, next build clean with /api/places and
/docs/places-api both emitted.

Closes StudentSuite#123
@vercel

vercel Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

@aryansk is attempting to deploy a commit to the studentsuite Team on Vercel.

A member of the Team first needs to authorize it.

@yakew7
yakew7 marked this pull request as ready for review August 15, 2026 14:38
@yakew7
yakew7 merged commit 6cd49e4 into StudentSuite:main Aug 15, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expose a public read-only places API

2 participants