Passa al contenuto principale

Validazione delle richieste

Come funziona la validazione dipende dallo stack, e la differenza è importante:

  • Express: la validazione è esplicita. Colleghi catene di express-validator a un metodo tramite _<verb>Validation. Gli schemi swaggerModel guidano documentazione e generazione dei tipi, non la validazione: dichiarare uno schema non valida la richiesta.
  • Bun/Elysia: la validazione è guidata dagli schemi. Gli schemi TypeBox che metti su body/query/params di una rotta vengono compilati e applicati automaticamente da Elysia.
Express: gli schemi non validano

Un equivoco comune: sullo stack Express, swaggerModel / Magic Types descrivono l'API per OpenAPI e i tipi generati. Non controllano le richieste in arrivo. Se devi rifiutare input errati, dichiara una catena _<verb>Validation.

Express

Dichiarare la validazione

Aggiungi un array _<verb>Validation al servizio. Ogni elemento è una catena express-validator. Efesto le esegue prima del tuo handler:

import { BaseApiService } from "efesto";
import { body, query, param } from "express-validator";

class Users extends BaseApiService {
constructor() {
super(__filename);
}

_getValidation = [
query("page").optional().isInt({ min: 1 }),
query("limit").optional().isInt({ min: 1, max: 100 }),
];
async _get(req, res) {
return res.json(await listUsers(req.query));
}

_postValidation = [
body("name").isString().isLength({ min: 2, max: 50 }),
body("email").isEmail(),
body("age").optional().isInt({ min: 0, max: 150 }),
];
async _post(req, res) {
return res.status(201).json(await createUser(req.body));
}
}
export default Users;

Gli array di validazione esistono per ogni verbo: _getValidation, _postValidation, _putValidation, _patchValidation, _deleteValidation, _headValidation, _optionsValidation, _traceValidation, _connectValidation.

Gestire gli errori di validazione

Efesto non impone una forma per gli errori. I risultati di express-validator sono disponibili tramite validationResult(req); formattali nel tuo errorMiddleware (il secondo hook passato a efesto()), che viene eseguito dopo le catene di validazione:

import { validationResult } from "express-validator";

const errorMiddleware = (req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
}
next();
};

Validazione personalizzata e tra campi

Tutto ciò che express-validator può fare funziona qui, perché queste sono catene express-validator. Per esempio, una regola asincrona personalizzata e un controllo tra campi:

_postValidation = [
body("email").isEmail().custom(async (email) => {
if (await emailExists(email)) throw new Error("Email already in use");
return true;
}),
body("confirmPassword").custom((value, { req }) => {
if (value !== req.body.password) throw new Error("Passwords do not match");
return true;
}),
];

Consulta la documentazione di express-validator per l'API completa (sanitizer, oneOf, sintassi a schema e altro).

Bun/Elysia

Sullo stack Elysia, la validazione è lo schema. Collega schemi TypeBox a body, query o params di una rotta; Elysia li compila, valida ogni richiesta e restituisce un 422 con un errore descrittivo quando una richiesta non corrisponde. Lo stesso schema documenta anche l'endpoint e tipizza l'handler.

import { BaseApiService, type Context, RouteValidation, t } from "efesto/elysia";

export default class extends BaseApiService {
_postValidation: RouteValidation = {
body: t.Object({
name: t.String({ minLength: 2, maxLength: 50 }),
email: t.String({ format: "email" }),
age: t.Optional(t.Number({ minimum: 0, maximum: 150 })),
}),
query: t.Object({ ref: t.Optional(t.String()) }),
};
_post({ body }: Context) {
return createUser(body); // validato rispetto allo schema
}
}

TypeBox copre i vincoli a cui ricorreresti: minLength/maxLength, minimum/maximum, format, pattern, enum (via t.Union/t.Literal), array e oggetti annidati. Vedi la guida alla validazione di Elysia.

Campi file

  • Express: valida i campi non-file con _<verb>Validation come al solito; abilita l'upload con il flag _<verb>Multer (vedi Upload di file). Il file stesso (req.file) viene controllato nel tuo handler.
  • Elysia (nativo): descrivi un campo file con t.File() nello schema del corpo; Elysia valida tipo e dimensione dallo schema.
  • Elysia (classe): abilita _<verb>Multer / _<verb>MultipleMulter; il file arriva in ctx.body (vedi Upload di file).