Node-RED

Contexto no Node-RED: Armazenamento Flow, Global e Persistente

O contexto do Node-RED permite que os fluxos lembrem informações entre mensagens. Este guia explica os três escopos, memória versus armazenamento em arquivo, múltiplos stores, acesso assíncrono e um padrão prático para guardar o último valor MQTT de cada dispositivo.

Diagrama dos escopos de contexto do Node-RED com armazenamento node, flow e global guardando os últimos valores MQTT
Rode isso no seu próprio broker MQTTTenha um broker MQTT privado e gerenciado no plano Free e comece com exemplos de código prontos.
Teste grátis Exemplos de código Sem cartão de crédito

O contexto do Node-RED é o armazenamento chave-valor embutido que permite a um fluxo lembrar valores entre mensagens, como a última temperatura informada por um sensor ou a contagem de alarmes do dia. Este guia explica os três escopos de contexto, como fazer o contexto sobreviver a restarts, como usar vários stores ao mesmo tempo e um padrão limpo para guardar o último valor MQTT de cada dispositivo.

Por que os fluxos precisam de estado

As mensagens no Node-RED são independentes. Quando um node mqtt in emite uma leitura, o próximo node só conhece o que está dentro daquela mensagem. Muitas tarefas reais precisam de mais do que isso:

  • Comparar um valor com o anterior para detectar uma mudança ou tendência.
  • Responder a uma requisição HTTP com a leitura mais recente, mesmo que nenhuma mensagem MQTT esteja chegando naquele momento.
  • Contar eventos por turno ou lembrar quando um alarme começou.
  • Combinar valores de dois topics que chegam em momentos diferentes.

O próprio MQTT oferece uma forma de memória por meio das retained messages, em que o broker guarda a última mensagem de um topic e a entrega a novos assinantes. Isso é ótimo para estado compartilhado entre clientes, e você deve usar. O contexto é a ferramenta complementar para o estado que vive dentro do Node-RED. Se estiver em dúvida sobre de que lado fica cada responsabilidade, nosso guia sobre o que um broker MQTT faz traça essa linha.

Os três escopos de contexto do Node-RED

Escopo Visível para Uso típico Acesso em uma função
Node Apenas o node que o definiu Filtros de banda morta, contadores dentro de uma função context.get() / context.set()
Flow Todos os nodes da mesma aba (ou da mesma instância de subflow) Últimos valores usados por vários nodes de uma aba flow.get() / flow.set()
Global Todos os nodes da instância do Node-RED Configuração compartilhada, tabelas de consulta global.get() / global.set()

Escolha o escopo mais restrito que funcione. Um valor no contexto global pode ser alterado por qualquer node em qualquer aba, o que torna os bugs difíceis de rastrear. O contexto do node é privado, então nada mais pode quebrá-lo.

Você não precisa escrever código para usar contexto. O node Change consegue definir, ler, mover e apagar propriedades flow. e global., e os nodes Inject, Switch e Template conseguem lê-las. A aba Context Data na barra lateral do editor mostra os valores atuais de cada escopo e permite atualizar ou apagar entradas durante a depuração.

Os subflows têm um detalhe a mais: o contexto de flow dentro de um subflow pertence àquela instância. Um subflow pode acessar o contexto do fluxo pai com o prefixo $parent., como em flow.get("$parent.lastTemp").

Contexto em memória versus persistente

Por padrão, todo o contexto fica em memória. É rápido, mas desaparece quando o Node-RED reinicia, quando o contêiner é recriado ou quando o host é reinicializado. Para tudo o que você não consegue reconstruir com facilidade, ative o armazenamento em arquivo.

Abra o settings.js (em ~/.node-red/ numa instalação padrão, ou em /data/ na imagem Docker) e encontre a seção contextStorage:

// settings.js
module.exports = {
    // ...
    contextStorage: {
        default: { module: "memory" },
        file:    { module: "localfilesystem" }
    },
    // ...
};

Reinicie o Node-RED. Agora você tem dois stores: default, que continua em memória, e file, que grava arquivos JSON na pasta context/ do seu diretório de usuário. Os nodes escolhem o store explicitamente:

flow.set("lastReading", msg.payload, "file");   // persistido
flow.set("scratch", 42);                        // memória (store padrão)
const last = flow.get("lastReading", "file");

No node Change e em outros editores, um pequeno seletor de store aparece ao lado dos campos flow. e global. assim que você tem mais de um store.

Se preferir tudo persistente, defina default: { module: "localfilesystem" }. Tenha em mente como o store em arquivo funciona: os valores ficam em cache na memória e são gravados em disco em intervalos, a cada 30 segundos por padrão, controlados pela opção flushInterval. Uma queda abrupta pode perder os últimos segundos de alterações. Isso não é problema para últimos valores e contadores, mas contexto não é banco de dados. Para dados históricos, grave em um banco de séries temporais como o InfluxDB, como mostrado no nosso guia de Node-RED com InfluxDB.

Observe também que apenas valores serializáveis em JSON persistem de forma confiável. Funções, timers e instâncias de classes não voltam intactos depois de um restart, então guarde objetos simples, números e strings.

Acesso síncrono e assíncrono

Com os stores memory e localfilesystem, get e set funcionam de forma síncrona, como mostrado acima. Alguns stores de terceiros, como módulos baseados em Redis, são assíncronos e exigem callbacks:

flow.get("count", "redis", (err, count) => {
    if (err) { node.error(err, msg); return; }
    count = (count || 0) + 1;
    flow.set("count", count, "redis", (err) => {
        if (err) { node.error(err, msg); return; }
        msg.payload = count;
        node.send(msg);
        node.done();
    });
});

Escrever código com callbacks desde o início custa pouco e permite trocar o store depois sem reescrever todas as funções. Saiba que um get seguido de um set não é atômico. Com taxas de mensagens muito altas em um store assíncrono, duas mensagens podem ler a mesma contagem antiga. Mantenha os contadores no contexto do node, onde só um node escreve, ou agrupe as atualizações.

Você também pode ler e gravar várias chaves de uma vez passando arrays: flow.get(["a", "b"]) retorna um array de valores.

Padrão: guardar o último valor MQTT de cada dispositivo

Uma tarefa comum do contexto no Node-RED é uma tabela ao vivo de "último valor conhecido": uma entrada por dispositivo, atualizada a cada mensagem e legível de qualquer ponto do fluxo. Imagine a caldeira de uma planta de alimentos no interior paulista. Publique alguns dados de teste no servidor MQTT público (gratuito) do ConnectMQ, que serve para testes e protótipos e não para dados de produção, substituindo c8d1 pela sua própria sequência aleatória:

mosquitto_pub -h broker.connectmq.com -p 1883 -i demo-ctx-c8d1 \
  -t "connectmq/demo/c8d1/boiler/tank1/telemetry" -m '{"level": 71.5, "temp": 63.2}'
mosquitto_pub -h broker.connectmq.com -p 1883 -i demo-ctx-c8d1 \
  -t "connectmq/demo/c8d1/boiler/pump2/telemetry" -m '{"rpm": 1450, "temp": 41.0}'

Assine no Node-RED com um node mqtt in em connectmq/demo/c8d1/+/+/telemetry, com a saída configurada como JSON convertido, e ligue-o a esta função:

// Function "store latest"
const parts = msg.topic.split("/");
const deviceId = parts[3] + "/" + parts[4];       // ex.: "boiler/tank1"

const latest = flow.get("latest", "file") || {};
latest[deviceId] = {
    values: msg.payload,
    ts: Date.now()
};
flow.set("latest", latest, "file");

node.status({ text: Object.keys(latest).length + " devices" });
return null;

Agora qualquer node da aba pode ler a tabela. Um node http in em GET /latest, seguido desta função e de um node http response, entrega uma pequena API:

// Function "serve latest"
const latest = flow.get("latest", "file") || {};
const staleMs = 5 * 60 * 1000;
const now = Date.now();

msg.payload = Object.entries(latest).map(([id, v]) => ({
    device: id,
    values: v.values,
    ageSeconds: Math.round((now - v.ts) / 1000),
    stale: now - v.ts > staleMs
}));
return msg;

O indicador stale é o detalhe importante. Uma tabela de últimos valores que nunca expira mostra tranquilamente uma leitura de um dispositivo que parou ontem. Sempre guarde um timestamp junto com o valor.

Checklist de boas práticas de contexto no Node-RED

  1. Use o escopo mais restrito: node, depois flow, depois global.
  2. Habilite um store em arquivo e escolha-o explicitamente para valores que você não consegue reconstruir.
  3. Guarde timestamps junto com os valores para detectar dados desatualizados.
  4. Evite objetos enormes; entradas de contexto grandes deixam lentos cada flush e cada atualização da barra lateral.
  5. Use prefixos nas chaves globais conforme a finalidade, por exemplo cfg_ para configuração e cache_ para dados de consulta.
  6. Não guarde segredos no contexto. Credenciais pertencem aos nodes de configuração e às variáveis de ambiente.
  7. Use retained messages MQTT para estado de que outros clientes precisam e contexto para estado de que só o Node-RED precisa.
  8. Faça backup da pasta context/ junto com seus fluxos. Roda o Node-RED em contêineres? Nosso guia de Node-RED com Docker mostra como manter o /data em um volume persistente.

A documentação oficial de contexto cobre por completo a API dos stores e as opções de configuração. Para o lado do código desses padrões, veja nosso guia do function node do Node-RED e, para a visão geral de como conectar fluxos a brokers, o tutorial de MQTT no Node-RED.

Combine o contexto do Node-RED com o ConnectMQ

O contexto guarda a memória do próprio Node-RED; o broker guarda a de todos os demais. O ConnectMQ é um broker MQTT gerenciado com retained messages, session expiry do MQTT 5 e TLS, para que dispositivos, dashboards e o Node-RED vejam os mesmos últimos valores conhecidos. O portal acrescenta um explorador de topics ao vivo e regras de alerta por limiar para verificações rápidas sem montar um fluxo. Comece no plano Serverless gratuito em app.connectmq.com, sem cartão de crédito, e conecte o Node-RED com o username, a senha e o client ID gerados.

Perguntas frequentes

Qual a diferença entre contexto flow e global no Node-RED?

O contexto flow é compartilhado apenas pelos nodes da mesma aba, enquanto o contexto global é compartilhado por todos os nodes da instância do Node-RED. Use o contexto flow, a menos que várias abas realmente precisem do mesmo valor.

Como faço o contexto do Node-RED sobreviver a um restart?

Adicione um store localfilesystem em contextStorage no settings.js, reinicie o Node-RED e grave nesse store, por exemplo flow.set("key", value, "file").

Onde o Node-RED guarda os arquivos de contexto persistente?

Em uma pasta context dentro do diretório de usuário do Node-RED, geralmente ~/.node-red/context ou /data/context no Docker, organizada por escopo.

Devo usar contexto ou retained messages MQTT para os últimos valores?

Use retained messages quando outros clientes MQTT precisarem do último valor e contexto quando só o Node-RED precisar dele, por exemplo para servir uma API ou comparar leituras. Muitos fluxos usam os dois.

Artigos relacionados

Node-RED Dashboard 2.0 com MQTT: gauges, gráficos e controles

Um tutorial prático do Node-RED Dashboard 2.0: assine telemetria MQTT ao vivo, exiba-a em gauges e em um gráfico de linha e controle um dispositivo com um switch que publica comandos e reflete o estado real.

9 min de leitura

Cliente OPC UA no Node-RED para MQTT com topics ISA-95

Use o node-red-contrib-opcua para assinar variáveis OPC UA em um CLP ou servidor de simulação, mapear node IDs para topics MQTT no estilo ISA-95 e publicar os valores com qualidade e timestamps.

10 min de leitura

Todos os artigos →

Seu broker MQTT privado está a poucos cliques de distância

Crie uma conta, receba as credenciais do seu broker e conecte seu primeiro dispositivo — sem servidores para instalar, atualizar ou escalar.