Workers são processos que rodam em background sem expor HTTP — ideais para filas de jobs, cron jobs e consumidores de mensagens.
Quando usar Workers
| Caso de uso | Tipo |
|---|---|
| Fila de processamento | Worker |
| Cron jobs | Worker |
| Kafka/RabbitMQ consumer | Worker |
| API REST | Web |
| Site/Dashboard | Web ou Static |
Criando um Worker
1. Código do worker
// worker.ts
import { Worker } from "bullmq";
import { connection } from "./redis";
const worker = new Worker(
"emails",
async (job) => {
console.log(`Processing ${job.id}...`);
await sendEmail(job.data);
console.log(`Done: ${job.id}`);
},
{ connection },
);
// Graceful shutdown
process.on("SIGTERM", async () => {
await worker.close();
process.exit(0);
});2. Deploy via CLI
veloz deployQuando perguntado sobre o tipo:
? Tipo de serviço:
Web (HTTP server)
Static (site estático)
❯ Worker (background process)
3. Ou configure no veloz.json
{
"services": {
".": {
"name": "email-worker",
"type": "worker",
"runtime": {
"command": "npx tsx worker.ts"
}
}
}
}Exemplos
Fila com BullMQ
import { Worker } from "bullmq";
import sharp from "sharp";
const worker = new Worker(
"images",
async (job) => {
const { url, sizes } = job.data;
for (const size of sizes) {
const buffer = await fetch(url).then((r) => r.arrayBuffer());
const thumb = await sharp(buffer).resize(size.width, size.height).toBuffer();
await uploadToS3(`thumb-${size.name}.jpg`, thumb);
}
},
{ connection: redis },
);Cron com node-cron
import cron from "node-cron";
// Todo dia às 3am
cron.schedule("0 3 * * *", async () => {
console.log("Running cleanup...");
await cleanupOldData();
});
console.log("Cron started");
setInterval(() => {}, 60000); // Keep aliveConsumer Kafka
import { Kafka } from "kafkajs";
const kafka = new Kafka({ brokers: [process.env.KAFKA_BROKER!] });
const consumer = kafka.consumer({ groupId: "my-group" });
await consumer.connect();
await consumer.subscribe({ topic: "events" });
await consumer.run({
eachMessage: async ({ message }) => {
const event = JSON.parse(message.value!.toString());
await processEvent(event);
},
});Diferenças do tipo Web
| Característica | Web | Worker |
|---|---|---|
| Porta HTTP | ✅ | ❌ |
| Domínio automático | ✅ | ❌ |
| Health check HTTP | ✅ | ❌ |
| Restart automático | ✅ | ✅ |
| Métricas CPU/Mem | ✅ | ✅ |
| Logs | ✅ | ✅ |
| Scaling horizontal | ✅ | ✅ |
Graceful Shutdown
Sempre trate SIGTERM para finalizar jobs em andamento:
process.on("SIGTERM", async () => {
console.log("Shutting down...");
// Para de aceitar novos jobs
await worker.close();
// Fecha conexões
await redis.quit();
process.exit(0);
});O Kubernetes espera 30 segundos antes de forçar o kill.
Keep Alive
Workers precisam manter o processo vivo:
// Opção 1: setInterval
setInterval(() => {}, 60000);
// Opção 2: Promise que nunca resolve
await new Promise(() => {});Variáveis de Ambiente
Configure via CLI ou dashboard:
veloz env set REDIS_URL "redis://..."
veloz env set KAFKA_BROKER "kafka.example.com:9092"Monitoramento
Workers aparecem no dashboard com:
- CPU — Uso de processador
- Memory — Uso de memória
- Logs — stdout/stderr em tempo real
Como não têm HTTP, não mostram requests/min ou latência.
Arquitetura típica
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Next.js │────▶│ Redis │────▶│ Worker │
│ (WEB) │ │ (Queue) │ │ (WORKER) │
└─────────────┘ └─────────────┘ └─────────────┘
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Database │◀────────────────────────│ Storage │
└─────────────┘ └─────────────┘
- Next.js recebe request e enfileira job
- Redis armazena a fila
- Worker processa e salva resultado