Passa al contenuto principale

Documentazione OpenAPI / Swagger

Entrambi gli stack producono un documento OpenAPI dagli schemi che già scrivi, ma con meccanismi molto diversi:

  • Express: Efesto scrive file .yaml su disco dalle tue dichiarazioni swaggerModel e _<verb>Swagger. Li unisci in un unico documento e servi una UI.
  • Bun/Elysia: Efesto non scrive nulla. @elysiajs/openapi costruisce il documento in memoria dai tuoi schemi TypeBox e dal detail di 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 suoi schemas condivisi. 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 breveSignificato
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 .yaml per ogni modelName (es. user.yaml) con gli schemi e i percorsi di quella rotta;
  • un index.yaml che unisce il tuo file di base con $ref a 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));
Imposta modelName

Gli 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:

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"] },
}
);

Per personalizzare il plugin OpenAPI (titolo, versione, percorso), passa un oggetto come swagger invece di true: viene inoltrato a @elysiajs/openapi.