Internet das Coisas (IoT) com JavaScript
Um caminho técnico e prático para você modelar dispositivos, coletar dados, transportar eventos e processar telemetria com segurança
— usando JavaScript no edge e no backend.
1) Arquitetura de IoT: do sensor ao dashboard
Em IoT, o desenho do sistema define o que vai falhar (e como você vai descobrir). Uma arquitetura típica com JavaScript costuma
separar responsabilidades:
No “edge”, o dispositivo lê sensores (temperatura, umidade, vibração), normaliza valores e cria eventos (ex.: “porta aberta”).
Também é onde você controla ritmo, buffer local e regras simples para reduzir tráfego.
O backend recebe telemetria, valida formato, aplica regras e persiste em banco/stream. Aqui ficam serviços como autenticação,
rate limit e trilhas de auditoria.
Um barramento (ex.: MQTT) desacopla dispositivos de consumidores. Assim, você pode ter:
processamento em tempo real, armazenamento e notificações sem travar o envio do sensor.
O ponto-chave é definir contratos (schema) e estratégia de entrega (ex.: QoS, idempotência).
- Contratos de dados: você precisa de campos estáveis (ex.: deviceId, ts, metric, value, unit).
- Latência vs. custo: nem todo dado precisa ir a cada milissegundo; faça amostragem e agregação.
- Tolerância a falhas: perda de conexão é normal — trate reenvio e duplicidade.
2) Modelando dispositivos e telemetria (sem virar bagunça)
O erro comum é começar com “payload solto” e evoluir sem disciplina. Quando você faz IoT com JavaScript,
vale adotar um modelo de dados consistente desde o início.
Use um deviceId estável e gere credenciais (chave/token/cert) por dispositivo.
Isso permite autenticar, revogar e auditar.
Em vez de mandar “estado inteiro” o tempo todo, trate “mudanças” e “medições” como eventos.
Isso reduz payload e facilita processamento incremental.
contrato • schema • idempotência
{
"deviceId": "sensor-42",
"ts": "2026-04-25T12:34:56.789Z",
"eventId": "sensor-42-20260425T123456-001",
"type": "metric",
"metric": {
"name": "temperature",
"value": 23.7,
"unit": "C"
},
"meta": {
"battery": 3.92,
"rssi": -61
}
}
Regras que te poupam horas de debug:
- eventId: use para idempotência (evita duplicar no backend ao reenvios).
- ts em ISO 8601: facilita ordenação, janelas e queries.
- unidade explícita: evita “°C” virar “número sem contexto”.
- meta opcional: você adiciona sem quebrar consumidores.
3) Transporte com JavaScript: MQTT/HTTP e estratégias de envio
Para IoT, transporte importa tanto quanto o sensor. Em geral, você escolhe entre:
- MQTT: leve, eficiente para muitos dispositivos, com pub/sub e suporte a QoS.
- HTTP(S): simples de integrar, ótimo para baixa frequência ou poucas fontes, mas menos eficiente em cenários massivos.
Quando o link cai, como o dispositivo se comporta? E quando ele volta? Na prática, você precisa de:
- Buffer local: manter eventos enquanto está offline (limite por memória e tempo).
- Backoff exponencial: ao falhar envio, você reduz carga e evita “tempestade” de reconexão.
- Reenvio com idempotência: use eventId para que o backend ignore duplicados.
- Controle de ritmo: taxa máxima por device para proteger o sistema.
4) Ingestão no backend: validação, idempotência e observabilidade
O backend é onde você transforma “mensagens” em “verdade operacional”. Em IoT, o essencial é:
validar payload, garantir consistência e deixar claro o que está acontecendo.
Mesmo em JavaScript, trate validação de tipos e campos obrigatórios.
Se não validar, você vai descobrir o problema quando o dado já estiver espalhado.
Ao receber eventos com eventId, você evita duplicidade.
Uma estratégia comum: armazenar eventId recente por device (ou uma tabela com índice único).
Para IoT, métricas e logs precisam ser “operacionais”. Registre:
contagem por device, taxa de erro por rota/topico, latência de processamento, e motivos de rejeição.
Assim você evita ficar “no escuro” quando um lote inteiro começa a falhar.
express • schema • dedupe
import express from "express";
const app = express();
app.use(express.json({ limit: "256kb" }));
// Exemplo simples de dedupe em memória.
// Em produção, use banco/Redis com índice único para persistir.
const seenEventIds = new Set();
function parseIsoDate(ts) {
const d = new Date(ts);
return Number.isFinite(d.getTime()) ? d.toISOString() : null;
}
function validatePayload(payload) {
if (!payload || typeof payload !== "object") return { ok: false, error: "Payload inválido" };
const { deviceId, ts, eventId, type, metric } = payload;
if (typeof deviceId !== "string" || deviceId.length < 3) return { ok: false, error: "deviceId ausente/invalid" };
const normalizedTs = parseIsoDate(ts);
if (!normalizedTs) return { ok: false, error: "ts inválido (use ISO 8601)" };
if (typeof eventId !== "string" || eventId.length < 8) return { ok: false, error: "eventId ausente/invalid" };
if (type !== "metric" && type !== "alert") return { ok: false, error: "type inválido" };
if (!metric || typeof metric !== "object") return { ok: false, error: "metric ausente" };
if (typeof metric.name !== "string") return { ok: false, error: "metric.name inválido" };
if (typeof metric.value !== "number" || !Number.isFinite(metric.value)) return { ok: false, error: "metric.value inválido" };
if (typeof metric.unit !== "string") return { ok: false, error: "metric.unit inválido" };
return { ok: true, normalizedTs };
}
app.post("/ingest", (req, res) => {
const v = validatePayload(req.body);
if (!v.ok) return res.status(400).json({ ok: false, error: v.error });
const { deviceId, eventId, type, metric } = req.body;
// Idempotência: ignora duplicados
if (seenEventIds.has(eventId)) {
return res.status(200).json({ ok: true, deduped: true });
}
seenEventIds.add(eventId);
// Aqui você persiste ou envia para um pipeline/stream
// Ex.: saveToDB({ deviceId, ts: v.normalizedTs, type, metric })
return res.status(202).json({
ok: true,
deduped: false,
deviceId,
eventId,
accepted: true
});
});
app.listen(3000, () => console.log("Ingestão rodando em :3000"));
Observação prática: o exemplo usa dedupe em memória só para demonstrar a ideia.
Na prática, você vai preferir uma camada persistente com índice único (ou cache com expiração) para lidar com reinícios.
Quer aprofundar ainda mais?
Se você gostou do nível técnico e quer construir IoT com mais segurança e consistência, recomendo ler os outros posts do
yurideveloper.com sobre backend, mensageria, arquitetura de sistemas e boas práticas de integração.
Assim você estrutura seu projeto desde a ingestão até o processamento dos eventos.
Sou Apaixonado pela programação e estou trilhando o caminho de ter cada diz mais conhecimento e trazer toda minha experiência vinda do Design para a programação resultando em layouts incríveis e idéias inovadoras! Conecte-se Comigo!