How these docs are built
This page
Everything on this site — the pages, the OpenAPI document, llms.txt, the
sitemap, the search index, the Postman collection — was written by
Lusen, a Laravel package that documents
the application it is installed in. The Acme Commerce API does not exist. What
exists is the example folder
that describes it, and a build script that runs the package's real emitters
over that description. Nothing here is a mockup, and this page is one of the
files in that folder.
Three stages#
Lusen works in three stages, with a plain data structure in the middle.
Collect. It reads the route table of the host application and keeps the
routes that match api/*, then reads the Markdown pages the team wrote in
resources/docs. Routes are sorted by path, so the output never depends on
the order they were registered in.
Extract. Each route passes through a pipeline of extractors. The first
reads what the route itself says: path parameters, authentication from
middleware, the API version from the URL. The next read the code behind it —
the controller's docblock, the form request's validation rules for the request
body, the API resource's toArray() for the response, and the model's casts
and migrations for the types a resource leaves unstated. Recorded responses
from the test suite replace generated examples where they exist, and the
attributes a team wrote — Lusen's own, or the ones another tool left behind —
have the last word.
All of it is static analysis. Lusen parses the source rather than running it,
so a build never boots the application, never calls a validation rule and
never opens a database connection. It succeeds in CI against a checkout with
no .env, and it degrades to fewer details rather than to a failed build when
something cannot be read.
Emit. The result is a serialisable description of the API: groups, endpoints, parameters, schemas, responses, examples, and the prose pages beside them. Every surface on this site is an emitter over that one description, which is why they cannot disagree with each other.
The surfaces#
Each one answers a different reader.
| Surface | For |
|---|---|
| Endpoint pages and group pages | A person, one question per page |
The .md twin of every page — this one, say |
A model or an agent, without markup to wade through |
openapi.json |
Generated clients — OpenAPI 3.1, so the schemas are real JSON Schema |
llms.txt and llms-full.txt |
Retrieval models, as an index and as the whole API in one file |
search-index.json |
The search box on every page, with no server behind it |
sitemap.xml |
Crawlers |
postman.json |
Poking the API before writing code |
/.well-known/api-docs |
An agent that has one URL and needs to find the rest |
Every file is static. A web server serves the output as flat files with no PHP on the request path, and every page reads completely with JavaScript disabled. An application that installs Lusen also gets an MCP server, so an assistant can query the documentation instead of scraping it; that one needs a running application, so it is the only surface this static site cannot show.
What is derived on this site#
The example states only the endpoints.
Everything about versions is worked out from them: that v2 is current and
v1 is deprecated, the retirement date, which v1 operation each v2
operation supersedes, and the Changed since v1 list on an operation both
versions expose. The versioning page, the
introduction, the authentication page and the errors page were written by
Lusen from what the endpoints expose, since nobody wrote them here. The
use cases and pagination
pages were written by hand, because a use case is not something a tool can
derive.
Try it on your own application#
composer require fpeposhi/lusen
php artisan lusen:build
That is the whole setup: Lusen discovers api/* routes and documents them with
no configuration. The README covers
what you get, and AUTHORING.md
covers what to write where the inference falls short.
The example#
The tools/ folder holds
everything that produced this site:
demo-spec.php— the fictional API, as the description Lusen's extractors would have builtdemo-pages/— the pages written by hand, including this onebuild-showcase.php— the build, which runs the same emitterslusen:buildrunslanding.html— the front page of this domain, which is about the package rather than the API and is the one file here Lusen did not write
Every written page on this site links to its own source under Edit this page, which is what a real deployment gets from one configuration line.
Free while you are building.
Get an API key