Use Case
Ingest, store, and stream sensor data in one place
IoT and metrics pipelines always fragment into three separate systems. Lux collapses the entire stack into native time-series commands with built-in aggregation and live streaming. One binary, one connection, zero glue code.
The Problem
IoT and metrics pipelines always end up as three separate systems: something to ingest the data (a custom service or Kafka), something to store and query it (InfluxDB or TimescaleDB), and something to stream live updates to dashboards (a WebSocket server or Grafana with polling). That is three services to deploy, three to monitor, three that can fail independently. And when you need to correlate time-series data with application state, you are writing cross-service queries that are brittle and slow. Every new sensor type means touching the ingestion layer, the storage schema, and the streaming config. Teams spend more time plumbing infrastructure than building the features their data should power.
Without Lux
Kafka / Custom Service
Data ingestion
InfluxDB / TimescaleDB
Time series storage
WebSocket Server
Live data streaming
Grafana
Visualization + polling
4 services to maintain, deploy, and monitor.
With Lux
Just Lux
Ingest + store + stream + aggregate
One binary. One connection string.
Getting Started
import { Lux } from "@luxdb/sdk" const db = new Lux(process.env.LUX_DIRECT_URL ?? "lux://localhost:6379") await db.tsadd("sensor:temp:floor1", "*", 22.5, { labels: { building: "hq", floor: "1", type: "temperature" } }) await db.tsadd("sensor:temp:floor1", "*", 23.1) const hourlyAvg = await db.tsrange("sensor:temp:floor1", "-", "+", { aggregation: { type: "avg", bucketSize: 3600000 } })
Ingest with labels, query with server-side aggregation. Three lines.
How It Works
Lux's time-series engine (TSADD/TSRANGE) handles ingestion and historical queries natively. Every data point gets a timestamp, either auto-generated with * or explicit, and is stored in a memory-efficient append-only structure optimized for range scans. There is no separate ingestion layer because the write path is the storage engine.
Labels let you tag data points for cross-series filtering. TSMRANGE queries across multiple time series by label, so you can ask "give me the average temperature for all sensors on floor 3" without knowing the key names ahead of time. Labels are indexed at write time, not scanned at query time.
Aggregation functions (avg, sum, min, max, count, std) run server-side so you are not pulling raw data over the wire. A query for hourly averages over a week of per-second data returns 168 data points instead of 604,800. Your dashboard stays fast and your network stays quiet.
Because KSUB fires events on every write, you can stream new readings to dashboards in real time without a separate service. Subscribe to sensor:* and every new data point triggers a callback. Time-series storage + realtime streaming + application state in one binary.
Full Pipeline
import { Lux } from "@luxdb/sdk" const db = new Lux(process.env.LUX_DIRECT_URL ?? "lux://localhost:6379") async function ingestSensor(sensorId, building, floor, type, value) { await db.tsadd(`sensor:${type}:${sensorId}`, "*", value, { labels: { building, floor, type, sensor: sensorId } }) } await ingestSensor("t-001", "hq", "1", "temperature", 22.5) await ingestSensor("t-002", "hq", "2", "temperature", 24.1) await ingestSensor("h-001", "hq", "1", "humidity", 45.2) const allTemps = await db.tsmrange("-", "+", { filter: { type: "temperature", building: "hq" }, aggregation: { type: "avg", bucketSize: 3600000 } }) const floor1Humidity = await db.tsmrange("-", "+", { filter: { type: "humidity", floor: "1" }, aggregation: { type: "max", bucketSize: 60000 } }) const sub = db.ksub(["sensor:*"], (event) => { pushToDashboard(event.key, event.operation) })
Ingest from multiple sensors, query across series by label, stream live updates via KSUB.
Feature Deep-Dive
Native Time Series
TSADD and TSRANGE are first-class commands in Lux, not a plugin or module. Ingest timestamped data with automatic or explicit timestamps and query any range with sub-millisecond latency. The storage engine is optimized for append-heavy workloads and sequential range scans, exactly how sensor data behaves.
Label-Based Filtering
Tag every data point with arbitrary key-value labels at write time. TSMRANGE queries across all series matching a label filter, so you can slice data by building, floor, sensor type, region, or any dimension without restructuring keys. Labels are indexed, not scanned, so filtering stays fast at scale.
Realtime Dashboard Streaming
KSUB fires a callback every time a matching key is written. Subscribe to sensor:* and every new reading triggers your handler instantly. No polling, no separate WebSocket service, no Grafana refresh intervals. Your dashboards update the moment data lands.
Performance
18M
ops/sec on TSGET
6
built-in aggregation functions
avg / sum / min / max / count / std
KSUB
live streaming on every write