Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

Temporal no Node.js: Workflows Duráveis

Atualizado em: 5 de setembro de 2026

Rack de servidores processando fluxos de dados no Node.js

Usar Temporal no Node.js permite implementar workflows duráveis que sobrevivem a reinícios, falhas de rede e indisponibilidade temporária. Em vez de controlar manualmente estados, timers, retries e compensações em tabelas e cron jobs, a plataforma registra o histórico da execução e retoma o código do ponto correto.

Temporal é especialmente útil para processos longos, como onboarding, pedidos, pagamentos, provisionamento, aprovação e integrações que podem durar minutos, dias ou meses. O modelo separa Workflows determinísticos de Activities que executam efeitos externos.

Neste guia, você aprenderá Workflows, Activities, Workers, Client, Task Queues, retries, timeouts, signals, queries, cancellation, versionamento, testes, segurança e observabilidade.

O que é Temporal?

Temporal é uma plataforma de execução durável. O guia oficial do SDK TypeScript apresenta Workflows, Activities, Workers, Client, cancellation, timers, versionamento e testes. A referência da API TypeScript detalha os pacotes e tipos.

Para processos distribuídos baseados em compensações, consulte Saga Pattern no Node.js. Para eventos persistidos junto ao banco, veja Outbox Pattern no Node.js.

Pacotes principais

npm install \
  @temporalio/client \
  @temporalio/worker \
  @temporalio/workflow \
  @temporalio/activity

Ferramentas de teste podem exigir pacotes adicionais da versão atual.

Conceitos

  • Workflow: lógica durável e determinística.
  • Activity: efeito externo que pode falhar.
  • Worker: processo que executa tasks.
  • Task Queue: fila lógica de distribuição.
  • Client: inicia e interage com workflows.
  • Temporal Service: mantém histórico e coordenação.

Activity

export async function chargePayment(input: {
  orderId: string;
  amountCents: number;
  idempotencyKey: string;
}): Promise<{ transactionId: string }> {
  return paymentProvider.charge(input);
}

Activities podem acessar banco, HTTP, filesystem e SDKs externos. Elas precisam ser idempotentes, pois podem executar novamente.

Workflow

import { proxyActivities } from '@temporalio/workflow';
import type * as activities from './activities';

const { chargePayment, reserveInventory, sendEmail } =
  proxyActivities<typeof activities>({
    startToCloseTimeout: '30 seconds',
    retry: {
      maximumAttempts: 5,
      initialInterval: '1 second',
      backoffCoefficient: 2
    }
  });

export async function processOrder(input: OrderInput) {
  const reservation = await reserveInventory(input);

  const payment = await chargePayment({
    orderId: input.orderId,
    amountCents: input.amountCents,
    idempotencyKey: `payment:${input.orderId}`
  });

  await sendEmail({
    orderId: input.orderId,
    template: 'order-approved'
  });

  return {
    reservationId: reservation.id,
    transactionId: payment.transactionId
  };
}

O Workflow parece código assíncrono comum, mas é reproduzido a partir do histórico.

Determinismo

O Workflow deve produzir as mesmas decisões ao ser reexecutado. Não faça diretamente:

  • fetch;
  • query ao banco;
  • leitura de arquivo;
  • Math.random;
  • new Date dependente do relógio real;
  • acesso arbitrário a process.env;
  • efeitos externos.

Use Activities e APIs do SDK para tempo, aleatoriedade e mensagens.

Por que o Workflow é reexecutado?

Temporal armazena eventos como ActivityScheduled, ActivityCompleted e TimerFired. O código é replayado para reconstruir o estado. Efeitos já registrados não são executados novamente durante replay.

Worker

import { Worker } from '@temporalio/worker';
import * as activities from './activities';

const worker = await Worker.create({
  workflowsPath: require.resolve('./workflows'),
  activities,
  taskQueue: 'orders'
});

await worker.run();

Vários workers podem ouvir a mesma Task Queue para escalar horizontalmente.

Client

import { Client, Connection } from '@temporalio/client';

const connection = await Connection.connect({
  address: process.env.TEMPORAL_ADDRESS
});

const client = new Client({
  connection,
  namespace: process.env.TEMPORAL_NAMESPACE
});

Iniciando Workflow

const handle = await client.workflow.start(
  processOrder,
  {
    taskQueue: 'orders',
    workflowId: `order:${orderId}`,
    args: [input]
  }
);

Um workflowId de negócio impede iniciar acidentalmente dois processos para o mesmo pedido, conforme a política configurada.

Obtendo resultado

const result = await handle.result();

Não bloqueie uma requisição HTTP por horas. Muitas APIs retornam 202 e permitem consultar o status.

Workflow Handle

const handle = client.workflow.getHandle(
  `order:${orderId}`
);

const description = await handle.describe();

O handle também envia signals, executa queries e solicita cancelamento.

Timers duráveis

import { sleep } from '@temporalio/workflow';

await sleep('24 hours');
await sendReminder(input);

O worker não precisa ficar ativo por 24 horas. O timer é persistido pelo serviço.

Schedules versus timers

Timers pertencem à execução de um Workflow. Schedules iniciam workflows em horários recorrentes. Use schedules para tarefas periódicas e timers para espera dentro de um processo.

Activity timeouts

  • Schedule-to-Start: tempo aguardando worker.
  • Start-to-Close: duração de uma tentativa.
  • Schedule-to-Close: duração total incluindo retries.
  • Heartbeat: limite para activity longa informar progresso.

Configure pelo comportamento real.

Retries

retry: {
  initialInterval: '1 second',
  backoffCoefficient: 2,
  maximumInterval: '1 minute',
  maximumAttempts: 10,
  nonRetryableErrorTypes: [
    'InvalidPaymentMethod'
  ]
}

Erros permanentes não devem consumir dez tentativas.

ApplicationFailure

Activities podem lançar falhas classificadas:

throw ApplicationFailure.nonRetryable(
  'Cartão inválido',
  'InvalidPaymentMethod'
);

Não baseie política em mensagens livres.

Idempotência das Activities

Uma Activity pode completar no fornecedor e falhar antes de retornar ao Temporal. A tentativa seguinte precisa usar a mesma chave:

idempotencyKey: `order:${orderId}:charge`

Consulte Idempotência em APIs Node.js.

Compensações

let reservationId: string | undefined;

try {
  const reservation = await reserveInventory(input);
  reservationId = reservation.id;

  await chargePayment(paymentInput);
} catch (error) {
  if (reservationId) {
    await releaseInventory({ reservationId });
  }

  throw error;
}

Compensação também precisa de retry e idempotência.

Signals

Signals enviam mensagens assíncronas para um Workflow:

import {
  defineSignal,
  setHandler,
  condition
} from '@temporalio/workflow';

export const approveSignal = defineSignal('approve');

export async function approvalWorkflow() {
  let approved = false;

  setHandler(approveSignal, () => {
    approved = true;
  });

  await condition(() => approved);
  return 'approved';
}

Um sistema externo pode sinalizar aprovação humana.

Queries

const statusQuery = defineQuery<WorkflowStatus>('status');

setHandler(statusQuery, () => status);

Queries leem estado sem alterá-lo. Não executam Activities.

Updates

Workflow Updates permitem solicitar mudança com resposta e validação, conforme a versão do SDK. Use quando precisa de confirmação mais forte que um Signal.

Cancellation

await handle.cancel();

O Workflow precisa decidir o que cancelar e compensar. Activities podem receber cancelamento e devem limpar recursos.

Cancellation scopes

Scopes permitem proteger uma compensação do cancelamento principal. Um pedido cancelado ainda pode precisar liberar reserva.

Child Workflows

const result = await executeChild(
  provisionTenant,
  {
    workflowId: `tenant:${tenantId}`,
    args: [input]
  }
);

Use para processos com ciclo de vida próprio, histórico separado ou necessidade de escala.

Não fragmente demais

Cada child adiciona coordenação. Activities podem ser suficientes para passos simples.

Continue-As-New

Workflows com histórico muito longo podem reiniciar a execução preservando estado lógico:

return continueAsNew({
  remainingItems,
  processedCount
});

Isso limita o tamanho do histórico.

Versionamento de Workflow

Alterar código determinístico enquanto execuções antigas existem pode causar erro de nondeterminism. Estratégias incluem:

  • Worker Versioning;
  • patching APIs;
  • Task Queues versionadas;
  • manter código compatível;
  • esperar workflows curtos terminarem.

Siga a documentação da versão atual.

Deploy seguro

Não substitua workers sem considerar execuções antigas. Blue-green ou canary precisam preservar capacidade de processar históricos existentes.

Consulte Canary Deploy no Node.js.

Payloads

Argumentos e resultados entram no histórico. Não envie:

  • senhas;
  • tokens;
  • cartão;
  • documentos completos;
  • arquivos grandes;
  • objetos mutáveis gigantes.

Use IDs e busque dados em Activities.

Data converters

Converters customizados podem criptografar payloads. Gerencie chaves, rotação e compatibilidade.

Namespaces

Namespaces isolam workloads, retenção e configuração. Separe produção de desenvolvimento e defina políticas de acesso.

Task Queues

Uma Task Queue representa capacidade, não necessariamente uma fila de negócio. Workers fazem long polling e o serviço distribui tasks.

Escala de workers

Dimensione por:

  • workflow tasks;
  • activity tasks;
  • CPU;
  • pool de banco;
  • limites externos;
  • schedule-to-start latency.

Worker tuning

O SDK oferece controles de concorrência e tuning. Aumentar concorrência sem capacidade no banco apenas aumenta timeouts.

Observabilidade

Monitore:

  • workflow success e failure;
  • activity retries;
  • schedule-to-start;
  • task queue backlog;
  • timeout;
  • workflow history size;
  • workers ativos;
  • non-determinism;
  • compensações.

Search Attributes

Search Attributes permitem localizar workflows por campos controlados. Não use valores de cardinalidade ou sensibilidade inadequadas sem planejamento.

Tracing

Use interceptors e integração OpenTelemetry. O replay não deve criar spans duplicados como se fossem novas execuções.

Consulte OpenTelemetry no Node.js.

Logs em Workflow

O SDK oferece logger consciente de replay. Não use console comum para eventos críticos, pois replays podem duplicar mensagens.

Shutdown do Worker

Ao receber SIGTERM, deixe o Worker parar polling e concluir tasks dentro do prazo. Activities interrompidas serão reentregues conforme política.

Consulte Graceful Shutdown no Node.js.

Testes unitários

Teste Activities como funções comuns, usando fakes para fornecedores.

Testes de Workflow

O ambiente de teste do SDK pode avançar o relógio virtual, permitindo testar timers de dias em segundos.

const environment = await TestWorkflowEnvironment.createTimeSkipping();

Confirme a API exata da versão instalada.

Teste de retry

Faça uma Activity falhar duas vezes e ter sucesso na terceira. Confirme tentativas e resultado sem esperar o backoff real em ambiente de time skipping.

Teste de signal

Inicie o Workflow, espere chegar ao estado de espera, envie signal e confirme continuação.

Teste de replay

Reproduza históricos reais contra a nova versão antes do deploy para detectar nondeterminism.

Quando usar Temporal?

  • processos longos;
  • múltiplas dependências;
  • retries complexos;
  • timers duráveis;
  • aprovação humana;
  • compensações;
  • estado de workflow consultável;
  • necessidade de retomar após falha.

Quando não usar?

Uma operação CRUD simples ou job curto pode usar fila tradicional. Temporal adiciona serviço, workers e disciplina de determinismo.

Erros comuns

  • I/O no Workflow: replay deixa de ser determinístico.
  • Activity sem idempotência: retry duplica efeito.
  • Timeout ausente: execução fica presa.
  • Payload sensível: dado entra no histórico.
  • Deploy sem versionamento: ocorre nondeterminism.
  • Workflow ID aleatório: duplicidade de processo.
  • Histórico ilimitado: replay fica pesado.
  • Concorrência excessiva: dependências saturam.

Boas práticas

  • Mantenha Workflows determinísticos.
  • Coloque efeitos em Activities.
  • Use IDs de negócio.
  • Torne Activities idempotentes.
  • Defina timeouts e retries.
  • Classifique erros permanentes.
  • Use compensações seguras.
  • Versione deploys.
  • Proteja payloads.
  • Teste replay e timers.

Conclusão

Usar Temporal no Node.js transforma processos distribuídos em código durável. Workflows mantêm estado e timers, enquanto Activities isolam efeitos externos e retries.

O modelo exige determinismo, idempotência e versionamento cuidadoso. Com timeouts, compensações, signals, testes de replay e observabilidade, Temporal reduz a quantidade de estado manual necessário para processos longos e críticos.

10 melhores cursos de programação em 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita