QUESTPIE
Agents

OpenAPI and Scalar

One module reads the collections, globals and routes you already wrote, emits an OpenAPI 3.1 document, and serves a browsable Scalar reference beside it.

View markdown

Someone wants to call your API. A partner, a colleague, an agent. This page is how you hand them the whole surface without writing a spec by hand.

It is probably already on

The starters put openApiModule in modules.ts for you. Open yours and check.

src/questpie/server/modules.ts
import { openApiModule } from "@questpie/openapi";

export default [openApiModule] as const;

Add it if it is missing, then run questpie generate. Codegen has to see the module before its two routes exist.

Point it at your base path

The starters mount the fetch handler at /api. The generator does not know that. It prefixes every documented path with its own basePath, which defaults to /. The scaffolded config/openapi.ts never sets it. So a fresh app documents //posts, not /api/posts.

One line fixes it.

src/questpie/server/config/openapi.ts
import { openApiConfig } from "@questpie/openapi";

export default openApiConfig({
	info: { title: "My API", version: "1.0.0" },
	basePath: "/api",
	scalar: { theme: "purple" },
});

openApiConfig() hands your object straight back. It exists for the types. Codegen discovers the file, and the spec route reads it when it builds the document. If your handler sits at the root, write basePath: "", not "/".

What you get

Two endpoints, under whatever base path your fetch handler uses.

EndpointServes
GET /api/openapi.jsonThe OpenAPI 3.1 document, as JSON
GET /api/docsThe Scalar reference UI, rendered from that document

Start the app and open http://localhost:3000/api/docs. Every collection, global and route is listed, each with a try-it panel. The page pulls Scalar from jsDelivr, so it needs network access to render.

`/api/docs` ignores `config/openapi.ts`

Only the spec route reads that file. The Scalar route always renders the defaults: title QUESTPIE API, theme purple, paths under /. Mount your own docs route to get your config back. See Serving it yourself.

What lands in the document

Five sources merge into one file.

SourceWhat it contributesTag
CollectionsThe CRUD set, plus count, batch writes, versions and revertCollections: <name>
GlobalsGet, update, schema, versions and revertGlobals: <name>
RoutesOne operation per method, typed by your Zod schemasRoutes: <segment>
Better AuthIts own document, or four fallback endpointsAuth
SearchPOST /search and POST /search/reindex/{collection}Search

Every document also carries four shared component schemas and two security schemes. Generated paths lists each one.

The document is built once

/api/openapi.json builds the document on the first request and caches it for the life of the app instance. It answers with an ETag and a one-hour Cache-Control, and returns 304 when your If-None-Match matches. So a change to config/openapi.ts needs a restart, not just another request.

Listing an endpoint does not open it

Both routes are ordinary QUESTPIE routes with no access rule. Anyone who can reach them can read your whole API shape. That does not make the endpoints callable. Every collection, global and route in the document is still checked against its own access rules when the call arrives.

The two security schemes say how to authenticate. They say nothing about what you are allowed to do.

A public route says so in the spec

.access(true) on a route emits security: [] on that operation. That opts it out of the document's security scheme. A function rule, false, or no rule at all inherits the scheme instead.

Where each topic lives

TopicPage
Every config option and its defaultConfiguration
The exact paths a collection, global or route emitsGenerated paths
Mounting the routes yourself, or generating in a build stepServing it yourself
Naming and tagging one operationRoute metadata
Who may call what the document listsAccess control

Next

MCP is the other half. OpenAPI describes your API to whoever reads it. MCP hands the same surface to an agent as callable tools.

On this page