Guide

API documentation

An OpenAPI 3.0 document, generated from the decorators you already wrote. No annotations, no separate spec file to keep in step, and nothing to remember when an endpoint changes.

What you get

setupSwagger serves two things. The spec itself is always available at {path}/json; the browser UI at {path} needs swagger-ui-express, which the generated project installs for you.

setupSwagger(app, {
  title: 'my-api API',
  version: '1.0.0',
  basePath: MOUNT_PATH,
  appId: APP_ID,
});

Call it after the registries have run, or the spec is generated before the endpoints exist and comes back nearly empty. The generated app.ts already has it in the right place.

Every option

setupSwagger(app, config, path?). The third argument is where the docs mount; it defaults to /api-docs.

OptionDefaultWhat it does
titlerequiredShown as the document title
versionrequiredYour API's version, not the library's
descriptionAuto-generated API documentationFree text under the title
basePaththe configured mountPathThe server URL requests are built against
appIdShown in the Authorize dialog, so nobody has to guess it
restApiKeyfalseWhether the docs require X-Parse-REST-API-Key
host, schemesCarried through for OpenAPI 2 style consumers
3rd argument'/api-docs'Mount path. The spec is always at {path}/json

Turning it on and off

There is deliberately no enabled option. The way to not have documentation is to not mount it, so the switch belongs to your project rather than to the library:

if (DOCS_ENABLED) {
  setupSwagger(app, {title, version, appId: APP_ID}, DOCS_PATH);
}

The generated project already has this, reading DOCS_ENABLED and DOCS_PATH from .env — so switching docs off is an environment variable, not a code change, and the startup banner reports off (DOCS_ENABLED=false) rather than advertising a URL that answers nothing.

Docs are on in production by default

That is a deliberate choice, not an oversight. The document lists only the endpoints you declared, generated from the same decorators that enforce them, so it cannot drift into describing something that is not there — and a public API benefits from published documentation. Set DOCS_ENABLED=false if your API is private and you would rather not publish its shape. It is not an access control either way: hiding the map is not locking the door.

Where the content comes from

Nothing here is written twice. Every part of the document is read from something that already had to exist:

In the specComes from
Schemas@ParseClass and every @ParseField — type, required, description
Paths@Route + the method name. createNote becomes POST /api/notes/createNote
Parameters or request bodyvalidation.fields
SecurityrequiresAuth, requireRoles, validation.requireUser
Summaries, tags, responsesthe swagger block on @CloudFunction

A model with no @ParseClass is absent from the schemas, and a function with no @Route is documented at its /functions/{name} path instead. Both are worth knowing when something you expected is missing.

GET and HEAD get query parameters, not a body

A browser cannot send a body on a GET, so those methods emit parameters: [{in: 'query'}] and everything else emits a JSON requestBody. This matches how validateEntityRoutes merges the query string into the body — which is also why GET parameters arrive as strings and need converting in your handler.

Tell the page your application id

Every request needs X-Parse-Application-Id, and a docs page that does not say which value is a guessing game. A wrong guess comes back as a bare {"error":"unauthorized"} that names nothing — so the endpoint looks broken when the only problem is the header.

setupSwagger(app, {title, version, appId: APP_ID});

The Authorize dialog then reads Parse Application ID. This server: my-api. The application id is not a secret — every client sends it on every request. The master key is the secret, and it is never rendered into the page.

The REST API key is opt-in

Parse only enforces X-Parse-REST-API-Key when restAPIKey is configured on the server, which most deployments — and every project psk new generates — do not do. The spec therefore does not require it by default. Turn it on when you have one:

setupSwagger(app, {title, version, restApiKey: true});

Before 3.0.3 it was asserted unconditionally, which put a header that did nothing into every example the page produced and implied a gate that was not there.

How auth appears in the document

An endpoint that declares requiresAuth or requireRoles is documented as needing a session token or the master key. One that declares neither is documented as open, because it is.

DeclaredDocumented as
nothingapplication id only
requiresAuth: trueapplication id + session token, or + master key
requireRoles: [...]the same — the role check happens after authentication
validation: {requireUser: true}the same, via Parse Server's own validator

The document describes what the decorators enforce. It is not itself a control: a padlock in the page means an endpoint said it needed a session, not that anything checked.

Without swagger-ui-express

The UI package is optional. Without it, setupSwagger logs a warning, skips the browser page, and still serves the spec:

# works either way
curl http://localhost:1337/api-docs/json

That is enough for anything that consumes a spec rather than reads one — client generation, a contract test, an API gateway. If /api-docs returns Cannot GET while /api-docs/json works, the package is simply not installed.

It is generated once

The document is built on the first request and reused until something registers, keyed on the registry's revision rather than a flag — so a late registration still invalidates it and nothing has to remember to. Endpoints added at boot are always included.

Next