Skip to content

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:

ts
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

ts
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çãoProtege contraPadrão
maxBodyBytesbody excessivo em memória; responde 413 quando excedido1 MiB
maxHeaderCountquantidade excessiva de campos de header; responde 431100
requestIdadiciona X-Request-Id às respostastrue
bodyTimeoutcliente lento enviando bodydesativado
handlerTimeouthandler que não termina30 s
maxConcurrentRequestssaturação por concorrênciailimitado
shutdownTimeoutdrenagem que não termina15 s
disposalTimeoutfechamento de recursos que não termina15 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çãoProtege contraPadrão
health.timeoutdependência travada durante um check2 s
health.maxConcurrentRequestsexcesso de probes consultando dependências8

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:

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

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

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

ts
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.

Feito para APIs que continuam simples quando crescem.