guias
Projetos

Workers

Como fazer deploy de processos em background (filas, crons, consumers) na Veloz.

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 deploy

Quando 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 alive

Consumer 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   │
└─────────────┘                         └─────────────┘
  1. Next.js recebe request e enfileira job
  2. Redis armazena a fila
  3. Worker processa e salva resultado