Skip to main content

threadline-api-endpoint

Use when adding, changing, or removing an HTTP route in apps/api — including anything that touches the Repository interface, the OpenAPI specification, or an authorization decision. Enforces the six-step order that keeps routes, persistence, policy, spec, tests, and docs in sync.

Aller à l'installation

Informations de source

Dépôt
hoangsonww/Threadline-RealTime-Collab
Dernière activité de la source
17 août 2026 à 05:57
Langue détectée de SKILL.md
anglais
Étoiles
3
Forks
1

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
threadline-api-endpoint
description
Use when adding, changing, or removing an HTTP route in apps/api — including anything that touches the Repository interface, the OpenAPI specification, or an authorization decision. Enforces the six-step order that keeps routes, persistence, policy, spec, tests, and docs in sync.
# Adding an API endpoint An endpoint in `apps/api` is six artefacts, not one. Skipping any of them produces a change that passes locally and fails in review, in CI, or — worst — in production against a database the test suite never exercises. Do them in this order. The order matters: each step's output is the next step's input, and doing them backwards produces a route whose spec and tests were written against an interface that then changed. ## 1. Domain type — `apps/api/src/domain.ts` Only if the shape is genuinely new. Reuse the existing vocabulary where one fits — `User`, `Organization`, `Membership`, `Room`, `RoomMembership`, `RoomEvent`, `CalendarEvent`, `PersonalAccessToken`, `Scope`. If the endpoint needs a new scope, add it to the `scopes` array. `Scope` is derived from that array, so nothing else needs editing — and a scope invented inline in a route rather than added here will not typecheck. ## 2. Persistence — `apps/api/src/repository.ts` Three edits, always together: 1. Add the method to the **`Repository` interface**. 2. Implement it in **`MemoryRepository`**. 3. Implement it in **`MongoRepository`**. Implementing only one is the single most common way to break this codebase, and the failure is asymmetric: miss `MemoryRepository` and every test fails immediately; miss `MongoRepository` and everything passes until deployment. TypeScript catches a missing interface implementation, so let it — add the interface method **first**, then let the two compile errors tell you what to write. Never import `mongodb` outside this file. That is [ADR 0003](../../../docs/decisions/0003-repository-interface.md). ## 3. Route — `apps/api/src/application.ts` Place it next to its siblings — routes are grouped by resource, and a new one dropped at the end of the file is a review comment. Every route does these in order: 1. **Authenticate.** Reuse the existing session / PAT middleware; do not re-derive identity. 2. **Validate** the request body, params, and query with Zod. An unvalidated body reaching the repository is a defect regardless of whether it currently causes a bug. 3. **Load** the resources the decision needs — the `Membership`, the `Room`, the `RoomMembership`. 4. **Decide** by calling `policy.ts`: - `canOrganization(membership, action)` — `read`, `create_room`, `manage_members`, `schedule` - `canRoom(membership, room, roomMembership, action)` — `read`, `join_live`, `write`, `manage` - `canInviteToOrganization(membership, organization)` — join-code visibility and rotation 5. **Act**, then respond. **Never infer access from an id alone.** Possessing a room id is not evidence of membership in it. If you find yourself writing a role comparison inline, stop — that logic belongs in `policy.ts`, and duplicating it there is how the two copies diverge. If a genuinely new kind of decision is needed, extend `policy.ts` rather than special-casing the route. Widening `RoomAction` or `OrganizationAction` is the normal way to do that. Also check what the response body contains. MongoDB's internal `_id` has leaked through before — see the `fix(api): stop leaking Mongo's internal _id` commit. Return domain objects, not raw documents. ## 4. Specification — `apps/api/src/openapi.ts` Document the operation: path, method, summary, request schema, every response status you can actually return, and **the required scope**. The spec is served live by the running service, so an undocumented operation is invisible to every consumer that reads it. Verify: `npm run openapi` writes `openapi.json`; `apps/api/src/openapi.test.ts` asserts structural properties of the document. ## 5. Tests — `apps/api/src/app.test.ts` `supertest` against `createApp()` with a `MemoryRepository`. Cover, at minimum: - The **authorized** path — the caller who should succeed, does. - The **denied** path — a caller who should not, gets 403 (or 404 where the endpoint deliberately hides existence; there is precedent for that in the organization join-code routes, which close an existence oracle). - **Validation** — a malformed body is rejected before it reaches persistence. An endpoint whose denial is untested is not finished. The 200 path tends to be exercised by hand during development; the 403 path only ever runs in the test suite. ## 6. Documentation — `docs/api.md` Add the route, its scope, and its authorization rule. A behavior change without a doc update is treated as incomplete in this repository, not as a follow-up. ## Verify ```bash npm run typecheck npm test npm run lint npm run openapi # regenerates the spec; confirms it still builds npm run docs:links # if you edited any heading in docs/ ``` Report what you ran and what it said. ## Checklist - [ ] Domain type added or reused - [ ] `Repository` interface method added - [ ] `MemoryRepository` implements it - [ ] `MongoRepository` implements it - [ ] Route validates with Zod - [ ] Authorization decision made by `policy.ts`, not inline - [ ] Response returns domain objects, not raw database documents - [ ] Operation documented in `openapi.ts` with its required scope - [ ] Test for the authorized path - [ ] Test for the denied path - [ ] Test for a malformed request - [ ] `docs/api.md` updated - [ ] `npm run check` passes
Voir sur GitHub