Passa al contenuto principale

Stack Bun / Elysia

Efesto gira sullo stack Bun / Elysia tramite un entrypoint separato: efesto/elysia. Su questo stack il compito di Efesto è ristretto: fornisce il routing da filesystem e monta i tuoi moduli. Tutto il resto — validazione, OpenAPI, hook — è Elysia idiomatico e nativo.

Modello diverso da Express

L'OpenAPI è prodotto nativamente da @elysiajs/openapi, non da YAML generati. Le rotte sono normale Elysia, oppure la classe BaseApiService di Efesto, che rispecchia la convenzione per metodo di Express (_<verb>, _<verb>Swagger, _<verb>Validation, _<verb>Multer). La validazione a runtime proviene da TypeBox; non c'è auto-generazione di swaggerModel né generazione di tipi .d.ts (TypeScript inferisce i tipi degli handler dai tuoi schemi TypeBox).

Installazione

Elysia e il plugin OpenAPI sono peer dependency opzionali:

bun add efesto elysia @elysiajs/openapi

Crea il server

import efesto from "efesto/elysia";

const app = efesto({
isProduction: process.env.NODE_ENV === "production",
routesDir: `${import.meta.dir}/routes`,
prefix: "/api/v1",
swagger: true, // OpenAPI nativo su /api/v1/openapi
}).listen(2014);

console.log(`http://localhost:${app.server?.port}/api/v1`);

efesto(...) restituisce un'istanza Elysia nativa, quindi puoi anche montarla come plugin: new Elysia().use(efesto(...)). L'oggetto di configurazione è documentato per intero in Configurazione.

Scrivere le rotte

Ogni file di rotta fa export default di una di due forme. Possono essere mescolate liberamente nello stesso progetto, ed entrambe compilano in rotte Elysia native, quindi hanno prestazioni identiche.

Una semplice istanza Elysia che usa percorsi relativi. Efesto la monta al prefisso ricavato dal file (routes/user/index.ts/user):

import { Elysia, t } from "efesto/elysia";

const users = [{ id: 1, name: "Ada", surname: "Lovelace" }];

export default new Elysia()
.get("/", () => users, {
detail: { summary: "List users", tags: ["User"] },
})
.put(
"/",
({ body }) => {
const user = { id: Math.floor(Math.random() * 1000), ...body };
users.push(user);
return users;
},
{
body: t.Object({ name: t.String(), surname: t.String() }),
detail: { summary: "Create user", tags: ["User"] },
}
);

Annota _<verb>Swagger: SwaggerOptions e _<verb>Validation: RouteValidation come nell'esempio: ciò mantiene permission inferito come tupla senza cast. Il ctx dell'handler è il contesto Elysia nativo, quindi leggi l'input validato da ctx.body/ctx.query/ctx.params (fai il cast o annota la forma che hai dichiarato in _<verb>Validation).

Modelli condivisi

Dichiara schemi riutilizzabili una volta tramite l'opzione models; i riferimenti @Model nel _<verb>Swagger di qualsiasi rotta si risolvono poi rispetto a essi (riscritti in ref nativi #/components/schemas/Model e registrati come componenti OpenAPI). I valori possono usare il DSL di Efesto o essere schemi semplici/TypeBox:

efesto({
isProduction: false,
routesDir: `${import.meta.dir}/routes`,
models: {
User: { properties: { name: "string", email: "string::email", friend: "@User?" } },
},
// swaggerConfig: { customTypes, customFormats } estende il DSL con i tuoi tipi
});

Aspetti trasversali

Componi auth, ABAC, caching e CORS nativamente tramite setup, che viene eseguito sull'istanza root prima del mount delle rotte, così si applica a ogni rotta. Per le regole permission basate su classe, fornisci qui l'ability CASL nel contesto:

import { defineAbility } from "@casl/ability";

efesto({
isProduction: false,
routesDir: `${import.meta.dir}/routes`,
setup: (root) => {
root
.derive(() => ({ ability: defineAbility((can) => can("readAll", "User")) }))
.onBeforeHandle(({ ability }) => {
// autentica / verifica i permessi, o lancia un'eccezione per rifiutare
});
},
});

Vedi Autenticazione e Permessi ABAC per i dettagli: entrambi hanno una tab Bun/Elysia.

Differenze rispetto allo stack Express

AspettoExpress (efesto)Bun/Elysia (efesto/elysia)
File di rottaclass extends BaseApiServicenew Elysia()... nativo oppure class extends BaseApiService
Handler_get(req, res, next) + res.json(...)_get(ctx) { return value } nativo
Validazione_<verb>Validation (express-validator)_<verb>Validation (schema TypeBox per rotta)
OpenAPI.yaml generato da _<verb>Swagger@elysiajs/openapi nativo, da TypeBox + il DSL Magic Types di _<verb>Swagger
Upload_<verb>Multer / _<verb>MultipleMulteruguale: _<verb>Multer / _<verb>MultipleMulter
AuthauthMiddleware globale + _<verb>OverrideAuthnativo, via setup
CachingRedis integrato (cache/purgeKey)plugin Elysia nativi
Tipi.d.ts generato (+ modifica magica)inferiti nativamente da TypeScript