Ciclo de vida
Uma aplicação não termina quando responde HTTP. Ela também precisa drenar requisições, concluir tarefas aceitas e fechar recursos.
run() para o caso comum
await app.run({ port: 4000 });run():
- inicia o servidor;
- registra shutdown para
SIGINTeSIGTERM; - imprime URLs da API, docs e health check quando disponíveis.
Use listen(port) quando outro código controlar sinais e encerramento.
Trabalho depois da resposta
Para uma notificação que não precisa atrasar o cliente:
@Post("/:id/notify")
@AfterResponse()
async notify(@Param("id", Number) id: number) {
await mailer.sendTaskReminder(id);
}A rota responde 202 e o método continua com o mesmo contexto de requisição.
Configure limites:
app.backgroundJobs({
concurrency: 8,
queueLimit: 100,
});Com worker e fila ocupados, uma nova tarefa recebe 503.
Para observar rejeições por fila cheia sem depender do log HTTP:
app.events.on("background.rejected", ({ requestId, route, reason }) => {
metrics.increment("background_rejected", { route, reason });
logger.warn({ requestId, route, reason });
});O evento tem reason: "queue-full" e não interfere na resposta original.
Capture falhas:
app.onBackgroundError((error, route) => {
logger.error({ error, route });
});Se esse callback de observabilidade também falhar, o scheduler continua processando tarefas. A falha do callback é registrada no logger da aplicação com o erro original e os metadados da tarefa.
Esse scheduler é em memória. Não oferece persistência, retry ou recuperação depois que o processo morre.
Health checks próprios
app.healthCheck("mailer", async () => {
return mailer.ping();
});Ao menos um check registra dois endpoints:
GET /health/liveconfirma que o processo HTTP está vivo. Ele não consulta dependências e deve ser usado pela sonda de liveness.GET /health/readyverifica todas as dependências registradas e deve ser usado pela sonda de readiness ou pelo load balancer.
Os checks de readiness são executados em paralelo. Se qualquer um falhar ou exceder o timeout, /health/ready responde 503 com status degraded.
Configure o prazo individual dos checks e quantas requisições de readiness podem executar ao mesmo tempo:
app.configureHealthChecks({
timeout: 2_000,
maxConcurrentRequests: 8,
});Use null em qualquer opção para remover o respectivo limite.
Registre recursos próprios
app.onClose(async () => {
await client.disconnect();
});Providers também podem declarar descarte:
app.provide("mailer", {
useFactory: () => createMailer(),
onDispose: (mailer) => mailer.close(),
});Shutdown ordenado
app.close():
- para de aceitar novas conexões;
- aguarda requisições e tarefas aceitas;
- descarta scopes de requisição;
- executa hooks e fecha providers raiz.
shutdownTimeout limita a espera. Quando vence, os AbortSignal ativos são abortados e conexões restantes são fechadas.
disposalTimeout limita o descarte de providers e hooks onClose(). Se um recurso não terminar nesse prazo, app.close() rejeita em vez de permanecer bloqueado indefinidamente.
Promessas que ignoram o signal não podem ser interrompidas à força pelo JavaScript.
Cancelar I/O em timeout e shutdown
Na maioria das rotas, não é preciso fazer nada. Um caso comum é consultar a API de uma transportadora para acompanhar uma entrega. Passe context.signal ao fetch para cancelar essa chamada se a rota exceder o timeout ou a aplicação receber um sinal de encerramento:
@Get("/orders/:id/tracking")
async tracking(
@Param("id") orderId: string,
@Context() context: RequestScope
) {
const response = await fetch(
`https://api.carrier.example/orders/${orderId}/tracking`,
{
headers: {
Authorization: `Bearer ${process.env.CARRIER_TOKEN}`
},
signal: context.signal,
},
);
return response.json();
}Se a operação não aceitar AbortSignal, ela pode continuar até terminar mesmo depois de o cliente receber 504. Nesse caso, o framework não consegue parar o trabalho por conta própria.
Se o shutdown exceder shutdownTimeout, app.close() rejeita e os recursos são fechados quando as requisições em andamento terminarem.
Ciclo de vida declarativo
Plugins e integrações participam do ciclo por meio do contexto declarativo:
const LifecycleModule = defineModule({
name: "app",
});
const app = await createApplication(LifecycleModule, {
configure: (app) => {
app.onStart(async () => {});
app.onClose(async () => {});
},
});Registre o plugin no campo plugins do módulo. onStart() executa quando o servidor inicia e onClose() durante o encerramento.
Na maioria das aplicações, defineModule → createApplication → run é suficiente.
Você chegou ao fim da trilha
Você começou com uma rota e terminou com contratos validados, DI, SQL, autenticação, testes e shutdown. As páginas de referência abaixo servem para consulta, não como continuação obrigatória.