timescale.info
Sign inSign up

How OpenChronology was built

Every merged pull request of openchronology/timeline-app, its first week of commits and its releases; and before them the 2020 PureScript editor, from snapshots of its deleted repositories in the Wayback Machine. Written to this server through the HTTP API.

327 moments · 2020-03-09T00:15:08Z CE – 2026-11-02T20:11:21Z CE · by @athan · @athan

#183 · HTTP API docs: OpenAPI 3.1 description and a reference to try it (#178)

  • timeline-app
  • pull request
  • api

Closes #178. The server's HTTP API is now described in OpenAPI 3.1: 92 operations and 97 schemas. - **The description:** served at `/api/openapi.json`, so it loads into Swagger UI, Redoc, Postman, Insomnia or a code generator. - **The reference:** a page at `/docs/api` that reads the description. ## Why our own reference rather than Swagger UI The issue asks for "Swagger (or better)". I built a reference page instead of embedding Swagger UI. - **No third-party code:** Swagger UI is several megabytes of someone else's script. It would need adding as a dependency and fitting into the site's strict content security policy. - **Same look and policy:** the reference is a server-rendered page in the site's own style, under the same nonce-based policy as every other page. - **Swagger UI still works:** anyone who wants it can point it at `/api/openapi.json`. ## The reference (`/docs/api`) - **Layout:** a contents column of operations grouped by tag, and a filter to find one. Operations open in place, and a link to one (`#op-…`) opens it. - **Each operation shows:** - who may call it: anyone, signed in with the CSRF token, or API keys with their scope; - its parameters, and its request body with the fields of the schema it uses; - each response. - **Try it:** sends a request from the page as the signed-in account, using its CSRF token, or with an API key typed in. The key is kept only while the page is open. Changes say they are real requests. - **Schemas:** listed at the end, with links between them. - **Where it's linked:** the footer of every page, and Account → API keys. - **Widths:** checked at desktop and phone widths. ## Keeping it right The description is code in `server/openapi/`: one module for each area (`timelines`, `collaboration`, `accounts`, `community`), sharing `common.mjs`. That shared module holds the security schemes, the standard error responses and helpers. - **`test/openapi.test.mjs`** checks the description is well formed: - every reference resolves and no schema is unused; - every operation has an ID, a summary, tags, security and a success response; - path parameters match their paths. It also scans `server/http.mjs` and fails if any route the server names isn't described: exact paths, route patterns (each alternative, with and without optional parts), timeline actions, account security steps and administration routes. - **`test/openapi-postgres.mjs`** checks the other direction. It sends all 90 described operations that the request handler serves to a running server. None may get "Unknown endpoint." or "Method not allowed." (`/api/live` and `/api/oembed` are served by Next.js itself.) - **Linting:** Redocly's linter, run once through `npx` and not added as a dependency, says the description is valid OpenAPI 3.1. It warns about four things that are deliberate: - the provider callback answers with a redirect; - the health check and `/api/openapi.json` never refuse; - `/api/auth/{provider}/…` sits beside `/api/auth/api-keys/{id}`. ## A bug this found: guests couldn't "Fork in browser" `POST /api/timelines/{id}/browser-fork` is how the app copies a public timeline into a guest's browser. Every timeline POST first checks that the account may supply data, so guests got 401 "Sign in to supply data.". No test caught it because the browser test mocks that route. The check no longer applies to this copy, which only reads and keeps nothing on the server. `openapi-postgres.mjs` now copies a public timeline as a guest over HTTP. ## Other notes from describing it These are described as the code behaves; none is changed here. - **Numbers as text:** counts and revisions that PostgreSQL returns as bigints arrive as JSON strings. - **Admin settings:** `GET /api/admin/settings` answers the same as `GET /api/admin/users`. - **Unlinking providers:** unlinking a sign-in provider that isn't linked still answers `{ok: true}`. ## Tests New: - `openapi.test.mjs`; - `openapi-postgres.mjs`, added to CI; - checks in `next-platform.mjs`:…

Sources

Open #183 · HTTP API docs: OpenAPI 3.1 description and a reference to try it (#178) in the timeline