Configuração
O bootstrap já acumulou banco, autenticação e políticas HTTP. Mova as opções operacionais para um arquivo tipado e mantenha os plugins no módulo da aplicação.
Crie empilha.config.ts
Na raiz do projeto:
import { defineConfig } from "empilha";
export default defineConfig({
server: {
port: Number(process.env.PORT) || 4000,
},
http: {
cors: process.env.CORS_ORIGIN || false,
requestId: true,
maxBodyBytes: 1024 * 1024,
maxHeaderCount: 100,
bodyTimeout: 10_000,
handlerTimeout: 30_000,
maxConcurrentRequests: 500,
shutdownTimeout: 15_000,
},
health: {
timeout: 2_000,
maxConcurrentRequests: 8,
},
openapi: {
title: "Tasks API",
version: "1.0.0",
},
logging: {
requests: true,
},
migrations: {
directory: "src/database",
},
});defineConfig() mantém autocomplete e verificação de tipos. Segredos continuam no ambiente.
No projeto criado pelo scaffold, database é uma seção de configuração consumida pelo src/app.ts para criar o pool e chamar app.postgres(). O .configure() aplica as opções operacionais, mas não cria uma conexão com o banco automaticamente.
Crie a aplicação
import { createApplication, defineModule } from "empilha";
import config from "../empilha.config";
import { TaskController } from "./controllers/task.controller";
import { postgres } from "@empilha/pg";
import { jwt } from "@empilha/jwt";
const access = jwt({
name: "access",
secret: process.env.JWT_SECRET!,
});
const database = postgres({
url: process.env.DATABASE_URL!,
sql: "./src/queries",
});
const AppModule = defineModule({
name: "app",
controllers: [TaskController],
plugins: [database, access, access.auth()],
});
const app = await createApplication(AppModule, { runtime: config });
await app.run();server.port permite chamar run() sem argumento. Plugins pertencem ao defineModule(); defineConfig() contém apenas configuração de runtime. Com migrations, os arquivos SQL são aplicados depois que o runner PostgreSQL é configurado e antes do readiness final. Use false para desativá-las em um ambiente específico.
Limites HTTP
| Opção | Protege contra | Padrão |
|---|---|---|
maxBodyBytes | body excessivo em memória; responde 413 quando excedido | 1 MiB |
maxHeaderCount | quantidade excessiva de campos de header; responde 431 | 100 |
requestId | adiciona X-Request-Id às respostas | true |
bodyTimeout | cliente lento enviando body | desativado |
handlerTimeout | handler que não termina | 30 s |
maxConcurrentRequests | saturação por concorrência | ilimitado |
shutdownTimeout | drenagem que não termina | 15 s |
disposalTimeout | fechamento de recursos que não termina | 15 s |
Passe null aos timeouts que aceitam essa opção para desabilitá-los.
Health checks
Ao registrar ao menos um health check, a aplicação expõe /health/live e /health/ready. O primeiro verifica somente se o processo está respondendo; o segundo verifica se as dependências estão prontas para atender tráfego.
| Opção | Protege contra | Padrão |
|---|---|---|
health.timeout | dependência travada durante um check | 2 s |
health.maxConcurrentRequests | excesso de probes consultando dependências | 8 |
Os checks de readiness são executados em paralelo. Use null em timeout ou maxConcurrentRequests para remover o limite correspondente.
CORS
CORS começa desativado:
http: {
cors: "https://app.example.com",
}Use uma origem explícita em produção. false mantém CORS desligado.
Para credenciais, cache de preflight e métodos/headers explícitos:
http: {
cors: {
origin: "https://app.example.com",
methods: "GET, POST, HEAD",
headers: "Content-Type, Authorization",
credentials: true,
maxAge: 600,
},
}O framework valida o preflight e envia Vary: Origin. Com credentials: true, a origem não pode ser *.
Ajustes específicos
O objeto centralizado é conveniente, não obrigatório:
const AppModule = defineModule({
name: "app",
controllers: [TaskController],
plugins: [access, access.auth()],
});
const app = await createApplication(AppModule, {
configure: (app) => {
app.configureHttp({ handlerTimeout: 20_000 });
app.openapi({ title: "Tasks API", version: "1.0.0" });
},
});Escolha um estilo principal para o projeto. Evite espalhar a mesma política entre vários arquivos.
Validação de respostas
Schemas de @Returns validam em runtime quando NODE_ENV !== "production". Para controlar explicitamente:
app.validateResponseSchemas(true);Mesmo com validação desligada, a serialização pelo schema continua removendo campos não declarados.
Configure durante a criação
Depois da compilação do módulo, a aplicação não aceita mudanças que alterariam o contrato das rotas.