Validação
POST /tasks receberá JSON. O TypeScript protege seu código durante o build; um schema protege a aplicação durante a requisição.
Defina o contrato
Crie src/schemas/task.schema.ts:
import { t, type Infer } from "empilha";
export const CreateTask = t.Object({
title: t.String({ minLength: 1, maxLength: 120 }),
description: t.Optional(t.String({ maxLength: 2_000 })),
});
export type CreateTask = Infer<typeof CreateTask>;O schema existe em runtime. Infer extrai dele o tipo TypeScript.
Valide e injete o body
import { Body, Post } from "empilha";
import { CreateTask, type CreateTask as CreateTaskInput } from "../schemas/task.schema";
@Post("/")
create(@Body(CreateTask) input: CreateTaskInput) {
return {
id: 1,
...input,
done: false,
};
}@Body(CreateTask) faz duas coisas:
- valida o JSON;
- entrega o valor validado ao argumento.
Como este contrato espera um objeto JSON, envie o tipo de conteúdo correto:
Content-Type: application/jsonO leitor também aceita text/*, application/x-www-form-urlencoded e multipart/form-data. Cada formato é convertido antes da validação: texto vira string, formulários viram objetos e arquivos multipart permanecem como File. Um tipo não suportado, como application/octet-stream, recebe 415 Unsupported Media Type.
Um body inválido recebe 400 antes de o método executar:
{
"errors": [
{
"path": "/title",
"message": "Expected string"
}
]
}Se o body exceder http.maxBodyBytes, a requisição recebe 413 Payload Too Large. O limite é aplicado também quando o cliente envia o body em chunks ou omite Content-Length.
Valide a query como um objeto
Para paginação, valores relacionados formam um único contrato:
import { Get, QueryParams, Request, t, type RequestContext } from "empilha";
const TaskFilters = t.Object({
page: t.Integer({ minimum: 1 }),
limit: t.Integer({ minimum: 1, maximum: 100 }),
done: t.Optional(t.Boolean()),
});
@Get("/")
@QueryParams(TaskFilters, { page: 1, limit: 20 })
list(@Request() request: RequestContext) {
return {
filters: request.query,
items: [],
};
}@QueryParams converte números e booleanos, aplica defaults e valida o objeto completo.
Formatos de string nativos
O Empilha registra os formatos mais comuns usados por schemas TypeBox:
| Formato | Exemplo |
|---|---|
date | 2026-08-13 |
date-time | 2026-08-13T12:30:00Z |
email | ana@example.com |
uuid | 550e8400-e29b-41d4-a716-446655440000 |
uri | https://example.com/tasks/1 |
ipv4 | 192.0.2.1 |
hostname | api.example.com |
const TaskParams = t.Object({
id: t.String({ format: "uuid" }),
});Esses formatos funcionam na validação de request e resposta sem registro manual no FormatRegistry.
Body usado sem argumento
Uma rota SQL pode precisar validar o body sem recebê-lo no método:
import { queryArtifacts } from "../queries/query-artifacts";
@Post("/")
@Body(CreateTask)
@Sql(queryArtifacts.taskCreate)
create() {}Nesse formato, o body validado fica disponível para bindings como :body.title.
Schema e tipo devem vir juntos
Não escreva uma interface TypeScript separada que possa divergir do schema. Infira o tipo com Infer<typeof Schema>.