API: createApp, OpenAPI, Swagger e Redoc¶
createApp é a fábrica que monta uma aplicação Express já cabeada: parse de
JSON, request-id, CORS opcional, /health, seus routers, a documentação
(Swagger + Redoc a partir de schemas Zod) e, por último, o stack de tratamento
de erros. É o porte de api.app + api.server do tempest-fastapi-sdk.
Você quase nunca configura essas peças na mão — passa opções para createApp e
sobe com runServer.
1. O mínimo que sobe¶
import { createApp, runServer } from "tempest-express-sdk";
const app = await createApp();
await runServer(app, { host: "127.0.0.1", port: 8000 });
Isso já te dá:
- Body JSON parseado (limite
100kb) +urlencoded. X-Request-IDem toda resposta (gerado se o cliente não mandar).GET /healthrespondendo{ "status": "ok", "checks": {} }.- O envelope canônico de erro para qualquer
AppExceptionou rota não-casada.
createApp é assíncrono
Ele é async porque o hook configure pode ser assíncrono (abrir conexão,
carregar chaves…). Sempre use await.
2. Registrando seus routers: o hook configure¶
Routers e paths de OpenAPI entram pelo hook configure, que roda depois dos
middlewares e antes do stack de erro — a ordem certa para o Express.
import { createApp, runServer } from "tempest-express-sdk";
import { usersRouter } from "@/api/routers/users";
const app = await createApp({
configure: (app) => {
app.use(usersRouter);
// qualquer app.use / app.get vai aqui
},
});
await runServer(app, { port: 8000 });
Não registre erro handler na mão
Não chame registerExceptionHandlers dentro do configure — o createApp
já o registra por último, que é onde o Express exige que fique. Adicionar
antes faz o handler não capturar as rotas registradas depois.
3. Opções do createApp¶
Todas opcionais. As mais usadas:
| Opção | Tipo | Default | Para |
|---|---|---|---|
corsOrigins |
string \| string[] \| false |
false (sem CORS) |
Libera origens. "*" ou lista. |
health |
HealthRouterOptions \| false |
monta /health |
Health check; false remove. |
configure |
(app) => void \| Promise |
— | Monta routers e paths OpenAPI. |
openapi |
CreateAppOpenApi |
— | Liga Swagger/Redoc (seção 4). |
catalog |
MessageCatalog |
— | Mensagens de erro localizadas. |
errorHandling |
opções | — | Repassado ao handler de exceções. |
jsonLimit |
string |
"100kb" |
Tamanho máximo do body JSON. |
Exemplo cabeado:
const app = await createApp({
corsOrigins: ["https://app.exemplo.com", "http://localhost:5173"],
jsonLimit: "1mb",
health: {
checks: [
{
name: "db",
check: async () => {
await db.raw("SELECT 1");
return true;
},
},
],
},
configure: (app) => {
app.use(usersRouter);
},
});
Bind: 127.0.0.1 vs 0.0.0.0
O default de runServer é 127.0.0.1 (só local). Use host: "0.0.0.0"
apenas quando outro host precisa alcançar o serviço (ex.: um front num
container separado).
checks é uma lista de { name, check }
Cada probe é { name: string, check: () => Promise<boolean> | boolean }. O
/health roda todas, expõe o resultado em checks ({ [name]: boolean }) e
degrada para 503 com status: "degraded" se qualquer uma falhar (ou
lançar). Sem checks, responde 200 com { status: "ok", checks: {} }.
4. Documentação automática (OpenAPI → Swagger + Redoc)¶
Aqui está o pulo do gato: como cada schema Zod do SDK carrega .openapi(),
descrições e exemplos fluem direto para a documentação. O fluxo tem 3 passos.
Passo 1 — crie um registry¶
import { createOpenApiRegistry, z } from "tempest-express-sdk";
const registry = createOpenApiRegistry();
Passo 2 — registre schemas e paths¶
registry.register(nome, schema) publica um schema como componente
reutilizável; registry.registerPath({...}) descreve uma rota.
const Item = registry.register(
"Item",
z.object({
id: z.string().uuid().openapi({ description: "ID do item." }),
name: z.string().openapi({ description: "Nome do item." }),
}),
);
registry.registerPath({
method: "get",
path: "/api/items/{id}",
summary: "Busca um item",
request: { params: z.object({ id: z.string().uuid() }) },
responses: {
200: { description: "ok", content: { "application/json": { schema: Item } } },
},
});
Passo 3 — passe o registry para o createApp¶
const app = await createApp({
configure: (app) => {
app.use(itemsRouter);
},
openapi: {
registry,
info: { title: "Minha API", version: "1.0.0", description: "Demo." },
servers: [{ url: "http://127.0.0.1:8000" }],
},
});
Com isso o app passa a servir:
| Rota | O quê |
|---|---|
GET /openapi.json |
O documento OpenAPI 3.0 gerado. |
GET /docs |
Swagger UI (interativo, servido offline). |
GET /redoc |
Redoc (referência de leitura). |
Opções do bloco openapi (CreateAppOpenApi):
| Campo | Default | Para |
|---|---|---|
registry |
— (obrigatório) | O registry populado. |
info |
— (obrigatório) | { title, version, description? }. |
servers |
— | Lista { url, description? }. |
v31 |
false |
Emite OpenAPI 3.1 no lugar de 3.0. |
jsonPath |
/openapi.json |
Rota do JSON. |
swaggerPath |
/docs |
Mount do Swagger; false desliga. |
redocPath |
/redoc |
Mount do Redoc; false desliga. |
swagger |
— | { title? } da página do Swagger. |
redoc |
— | { title?, scriptUrl? } da página do Redoc. |
Swagger é 100% offline; Redoc usa CDN
Os assets do Swagger UI vêm do pacote swagger-ui-dist e são servidos
localmente em ${swaggerPath}/assets — nenhuma chamada externa. O Redoc
carrega o bundle (~1 MB) da CDN jsDelivr por padrão; para self-hostar, passe
redoc: { scriptUrl: "/vendor/redoc.standalone.js" }.
Sem barra final também funciona
A partir da v0.20.1 os assets do Swagger usam caminho absoluto
(/docs/assets/...), então GET /docs e GET /docs/ renderizam a UI
completa. Antes, visitar /docs sem a barra buscava /assets/... e a página
subia sem estilo (assets 404). Se você fixou uma versão anterior, atualize.
5. Montando a doc manualmente (avançado)¶
Se você não usa createApp (app Express legado, por exemplo), monte as peças na
mão:
import express from "express";
import {
createOpenApiRegistry,
generateOpenApiDocument,
mountOpenApiJson,
mountSwaggerUi,
mountRedoc,
} from "tempest-express-sdk";
const app = express();
const registry = createOpenApiRegistry();
// ... registry.register / registerPath ...
const document = generateOpenApiDocument(registry, {
info: { title: "Minha API", version: "1.0.0" },
});
mountOpenApiJson(app, "/openapi.json", document);
mountSwaggerUi(app, "/docs", "/openapi.json", { title: "Minha API" });
mountRedoc(app, "/redoc", "/openapi.json");
generateOpenApiDocument devolve um objeto JSON puro — dá para salvá-lo num
arquivo, versioná-lo ou servi-lo de onde quiser.
Recapitulando¶
createApp(options)monta middlewares → routers (configure) → docs → erro, nessa ordem;runServer(app, { host, port })sobe.- Registre routers dentro de
configure; não registre o error handler à mão. - Documentação em 3 passos:
createOpenApiRegistry()→register/registerPath→ passe o registry emopenapi. Ganha/openapi.json,/docse/redoc. - Swagger é offline; Redoc usa CDN (self-hostável via
scriptUrl). /docse/docs/funcionam igual — assets em caminho absoluto. 🚀