Documentazione OpenAPI / Swagger
Entrambi gli stack producono un documento OpenAPI dagli schemi che già scrivi, ma con meccanismi molto diversi:
- Express: Efesto scrive file
.yamlsu disco dalle tue dichiarazioniswaggerModele_<verb>Swagger. Li unisci in un unico documento e servi una UI. - Bun/Elysia: Efesto non scrive nulla.
@elysiajs/openapicostruisce il documento in memoria dai tuoi schemi TypeBox e daldetaildi ogni rotta, e lo serve.
Express
Da dove proviene la documentazione
Due dichiarazioni su un servizio alimentano il documento:
swaggerModel— la categoria della rotta (modelName) e i suoischemascondivisi. Gli schemi sono globali nell'intero progetto e referenziati ovunque con@Name._<verb>Swagger— metadati per endpoint:operationId,summary,parameters,requestBody,responses.
import { BaseApiService, SwaggerModel, SwaggerOptions } from "efesto";
class Users extends BaseApiService {
constructor() {
super(__filename);
}
swaggerModel: SwaggerModel = {
modelName: "User",
schemas: [
{
name: "User",
properties: {
id: "number",
name: "string!",
email: "string::email!",
},
},
{
name: "CreateUser",
properties: { name: "string!", email: "string::email!" },
},
],
};
_postSwagger: SwaggerOptions = {
operationId: "createUser",
summary: "Create a user",
requestBody: "@CreateUser", // forma breve
responses: { 201: "@User" }, // forma breve
};
async _post(req, res) {
return res.status(201).json(await createUser(req.body));
}
}
export default Users;
Forme brevi
Scrivere OpenAPI grezzo è verboso; i Magic Types di Efesto lo comprimono. Tutte e quattro qui sotto sono equivalenti alla forma estesa a oggetto:
| Forma breve | Significato |
|---|---|
name: "string" | una proprietà di tipo string |
"string::email" | string con formato email |
"string|Joe" | string con esempio Joe |
requestBody: "@CreateUser" | un corpo application/json con quello schema |
responses: { 200: "@User" } | un 200 application/json con quello schema |
responses: { 200: "@User[]" } | un 200 array di User |
Vedi Magic Types per la grammatica completa.
Parametri
I parametri di percorso, query e header vanno in parameters (lo schema accetta
anche la forma breve Magic Type):
_getSwagger: SwaggerOptions = {
operationId: "getUsers",
parameters: [
{ in: "query", name: "page", schema: "number?" },
{ in: "header", name: "X-Request-Id", schema: "string" },
],
responses: { 200: "@User[]" },
};
Il parametro di percorso di un file [id].ts viene aggiunto per te; documenta qui
quelli aggiuntivi.
File generati e servire la UI
All'avvio, Efesto scrive in relativeDirSwaggerDeclarationsPath (default
swagger-declarations/):
- un
.yamlper ognimodelName(es.user.yaml) con gli schemi e i percorsi di quella rotta; - un
index.yamlche unisce il tuo file di base con$refa quei file per-modello.
Un file lo fornisci tu stesso: baseIndex.yaml, la base del documento (versione
OpenAPI, servers, info, securitySchemes, components condivisi, security e un
paths: {} vuoto):
openapi: "3.0.0"
servers:
- url: http://localhost:3000/api/v1
info:
title: My API
version: 1.0.0
components:
securitySchemes:
headerAuth: { type: apiKey, in: header, name: X-Auth-Token }
security:
- headerAuth: []
paths: {}
Poi unisci index.yaml in un unico documento e servilo con
swagger-ui-express:
import SwaggerParser from "@apidevtools/swagger-parser";
import swaggerUi from "swagger-ui-express";
import fs from "fs";
// dopo il middleware Efesto: unisci tutti gli .yaml in un solo apis.json
const bundled = await SwaggerParser.bundle("swagger-declarations/index.yaml");
fs.writeFileSync("swagger-declarations/apis.json", JSON.stringify(bundled));
app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(bundled));
modelNameGli endpoint il cui servizio omette modelName vengono raggruppati sotto la categoria
n-a. Assegna a ogni rotta un modelName chiaro per mantenere il documento
organizzato.
Bun/Elysia
Non c'è alcun swaggerModel, nessun _<verb>Swagger e nessuna generazione di file. Il
documento OpenAPI è costruito dai tuoi schemi TypeBox
(body/query/params/response) e dal detail di ogni rotta, poi servito da
@elysiajs/openapi.
Abilitalo con l'opzione swagger (attiva di default); il documento è servito su
<prefix>/openapi:
const app = efesto({
isProduction: false,
routesDir: `${import.meta.dir}/routes`,
prefix: "/api/v1",
swagger: true, // -> GET /api/v1/openapi
});
Documenta un endpoint inline. detail mappa i metadati OpenAPI; gli schemi
documentano le forme:
- Native Elysia
- Class-based
import { Elysia, t } from "efesto/elysia";
export default new Elysia().post(
"/",
({ body }) => createUser(body),
{
body: t.Object({ name: t.String(), email: t.String({ format: "email" }) }),
response: { 201: t.Object({ id: t.Number() }) },
detail: { summary: "Create a user", tags: ["User"] },
}
);
import { BaseApiService, type Context, RouteValidation, SwaggerOptions, t } from "efesto/elysia";
export default class extends BaseApiService {
_postSwagger: SwaggerOptions = { operationId: "createUser", summary: "Create a user" }; // -> OpenAPI
_postValidation: RouteValidation = {
body: t.Object({ name: t.String(), email: t.String({ format: "email" }) }),
};
_post({ body }: Context) {
return createUser(body);
}
}
Per personalizzare il plugin OpenAPI (titolo, versione, percorso), passa un oggetto
come swagger invece di true: viene inoltrato a @elysiajs/openapi.