OpenAPI and the SDK
The OpenAPI document is generated from @OA\ docblocks in the PHP code by
darkaonline/l5-swagger 11 (zircote/swagger-php 6). The TypeScript SDK in front/sdk turns that
document into types with openapi-typescript. Decision record:
OpenAPI docs from Swagger annotations.
Where the annotations live
Section titled “Where the annotations live”| Annotation | Where | Example |
|---|---|---|
@OA\Info, @OA\SecurityScheme (passport, bearer JWT) |
api/packages/core/src/Http/Controllers/CoreController.php |
once for the whole API |
Operations (@OA\Get, @OA\Post, …) |
a Swagger interface per controller, src/Http/Controllers/Swagger/<Name>ControllerSwagger.php; the controller implements it |
BookmarkControllerSwagger in api/packages/bookmarks_notes |
Request schemas (@OA\Schema) |
on the FormRequest class | CreateBookmarkRequest → schema BookmarkCreateRequest |
| Response schemas | on the API Resource class | BookmarkResource |
Keeping the operation docblocks on an interface keeps the controller readable and makes the interface the place to check the documented contract. See Adding an endpoint for a full example.
Generating the document
Section titled “Generating the document”make -C api swagger-generate# = docker compose exec api bash -c "php artisan l5-swagger:generate"The result is api/storage/api-docs/api-docs.json. The api/ folder is mounted into the
container, so the file appears on the host at that path; there is nothing to copy out. It is
git-ignored.
- Swagger UI: http://api.localhost/api/documentation (or
http://<slug>.localhost/api/documentation). - With
L5_SWAGGER_GENERATE_ALWAYS(defaulttrue) the UI regenerates the document on every request, which also refreshes the JSON file. - CI runs
php artisan l5-swagger:generatein theplatformPHPUnit shard, so a broken annotation fails the build.
Regenerating the SDK types
Section titled “Regenerating the SDK types”@ulams/sdk is a framework-free client (createClient(...)) used by front/web. Its request
paths are typed by the generated file:
import type { components, paths } from "./generated/openapi.ts";export type ApiPath = keyof paths;Every request() call in front/sdk/src/client.ts takes an ApiPath, so calling a path the API
does not document is a type error. Paths that are not in the document yet are cast
("/api/demo/login" as ApiPath).
-
Generate the OpenAPI JSON (above).
-
Regenerate the types from the repository root:
Terminal window corepack yarn workspace @ulams/sdk generate# openapi-typescript ../../api/storage/api-docs/api-docs.json -o src/generated/openapi.ts -
Typecheck the SDK and its consumers, then commit
front/sdk/src/generated/openapi.tswith the change that caused it:Terminal window corepack yarn turbo run typecheck --filter=@ulams/sdk --filter=@ulams/web -
Update the CLI from the same document, so every new operation has a command (or an exclusion with a reason) and the coverage check stays green:
Terminal window corepack yarn workspace ulams sync-spec # api-docs.json -> front/cli/spec/openapi.jsoncorepack yarn workspace ulams generate # spec + overrides -> src/generated/commands.tscorepack yarn workspace ulams coverage # fails on an operation without a commandCI fails (
CLI spec is current,sync-spec.mjs --check) when this step was skipped.The course builder and Living Course are the exception: their commands are hand-written (
front/cli/src/commands/builder.ts,living.ts) and list the endpoints they cover.
If you generate the JSON in a container that does not mount the source (for example from a
published api image), copy it out first:
docker cp <container>:/var/www/html/storage/api-docs/api-docs.json api/storage/api-docs/api-docs.jsonLimits of the current document
Section titled “Limits of the current document”The annotations describe request bodies and paths well but almost no response bodies, so most
responses come out as unknown in openapi.ts. The response types in front/sdk/src/types.ts
are written by hand from real API responses; replacing them with generated ones depends on the
roadmap item “OpenAPI spec complete and published”. The endpoint list is on
API endpoints, the reference on API reference
and usage examples of the client on SDK usage.