PostgreSQL
O Empilha aceita um runner compatível com PostgreSQL. O pacote @empilha/pg integra o driver pg e administra o pool no ciclo de vida da aplicação.
Instale
bun add @empilha/pg pg
bun add -d @types/pgConfigure
import { createApplication, defineModule } from "empilha";
import { postgres } from "@empilha/pg";
const database = postgres({
url: process.env.DATABASE_URL!,
sql: "./src/queries",
healthCheck: "database",
});
const AppModule = defineModule({
name: "app",
controllers: [TaskController],
plugins: [database],
});
const app = await createApplication(AppModule);
await app.run({ port: 4000 });O plugin:
- cria um
pg.Pool; - carrega os arquivos SQL quando
sqlaponta para um arquivo ou diretório; - habilita queries e transações;
- registra um check do banco;
- encerra o pool em
app.close().
Use um pool existente
Se a aplicação já cria o pool:
import { Pool } from "pg";
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
const AppModule = defineModule({
name: "app",
controllers: [TaskController],
});
const app = await createApplication(AppModule, {
configure: (app) => app.postgres(pool, {
sql: "./src/queries",
healthCheck: "database",
}),
});Por padrão, app.close() também chama pool.end(). Se o pool pertence a outro componente do processo, preserve-o com close: false:
app.postgres(pool, {
close: false,
healthCheck: false,
});Execute migrations
Para executar migrations pelo ciclo de inicialização da aplicação, configure o runner e registre o diretório:
app.migrations({ directory: "src/database" });O runtime usa checksum e advisory lock no PostgreSQL. A mesma operação também está disponível como runMigrations(runner, options) para scripts.
Adicione:
{
"scripts": {
"migrate": "bun node_modules/empilha/scripts/database/migrate.ts"
}
}Execute:
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/tasks \
bun run migrateArquivos em src/database rodam em ordem lexicográfica. O histórico fica em empilha_migrations; alterar o conteúdo de uma migration já aplicada causa erro de checksum.
O migrador usa um advisory lock do PostgreSQL para que apenas uma execução por banco aplique migrations por vez. Cada arquivo SQL e seu registro de checksum são confirmados na mesma transação: se o arquivo falhar, o histórico também não é alterado.
O script bun run dev do scaffold usa esse mesmo migrador versionado antes de iniciar a aplicação. Portanto, reiniciar o ambiente de desenvolvimento não executa novamente migrations já registradas.
Verifique a saúde
Com health check configurado:
curl http://localhost:4000/health/ready{
"status": "ok",
"checks": {
"database": "up"
}
}Uma falha em qualquer check responde 503 com status degraded.
Timeout
No plugin oficial, timeout configura o limite das operações do Empilha e, se statement_timeout não tiver sido informado, usa o mesmo valor no PostgreSQL:
postgres({
url: process.env.DATABASE_URL!,
timeout: 5_000,
lock_timeout: 2_000,
});app.postgres(pool, {
timeout: 5_000,
sql: "./src/queries",
});O timeout gera 504 e envia um AbortSignal ao runner. O plugin oficial @empilha/pg usa uma conexão separada para solicitar o cancelamento da query no PostgreSQL. Em um runner próprio, o método query() precisa observar options.signal; caso contrário, o framework encerra a requisição pelo timeout de parede, mas o driver pode continuar o trabalho até o próprio limite.
Configure também statement_timeout e lock_timeout no PostgreSQL; o limite da aplicação não substitui limites do banco.
Outro driver
Para integrar outro driver, implemente o contrato mínimo:
type PostgresQueryRunner = {
query(
sql: string,
params?: unknown[],
options?: { signal?: AbortSignal },
): Promise<{ rows: unknown[] }>;
connect?(): Promise<{
query: PostgresQueryRunner["query"];
release(): void;
}>;
};Passe o runner a app.postgres(runner). Transações exigem connect().
Banco é um recurso da aplicação
Deixe criação, health check e fechamento próximos do bootstrap. Controllers devem conhecer queries, não o ciclo de vida do pool.