npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

node-red-contrib-timescaledb

v0.2.0

Published

Node-RED nodes for writing data to TimescaleDB without SQL queries. Supports MQTT topic mapping and automatic data type detection.

Readme

node-red-contrib-timescaledb

Node-RED nodes for writing data to TimescaleDB (PostgreSQL) using a unified schema for both industrial and home use. No manual SQL required.


Nodes in this package

1. MQTT to TimescaleDB

  • Maps MQTT topic levels to database columns or JSONB tags according to a mapping string.
  • Designed for scenarios where data arrives via MQTT or structured topic.
  • Supports topic mapping via the msg.mapping or ignore msg.topic with fixed config, extra tags to JSONB column.

2. Payload to TimescaleDB

  • Writes naked or JSON object payloads directly to TimescaleDB.
  • Use when your data is already normalized and you want to map fields and tags directly from the message or node configuration.

Node Configuration (shared)

  • Server: Connection to PostgreSQL/TimescaleDB (host, port, database, user, password, SSL)
  • Unit: Optional unit for the value.
  • Fixed Tags: Optional JSON object with fixed tags (can be overridden in msg.tags).

MQTT to TimescaleDB (additional)

  • Topic mapping: String defining how topic levels map to columns/keys (see below)
  • Ignore msg.topic: If checked, msg.topic is ignored and only fixed tags or msg.tags are used.
  • Payload Type: "naked" or "JSON object"

Payload to TimescaleDB (additional)

  • Payload Type: "naked" or "JSON object"
  • Measurement: Name of the measurement (can be overridden in msg)
  • Field: Name of the field (can be overridden in msg)

Features

  • Supports database schema for "industry" and "home" use (inspired by ISA-95 convention)
  • Automatic data type detection (boolean, integer, double, text)
  • Supports both "naked" and JSON object payloads
  • Flexible topic mapping to DB columns and JSONB tags
  • Option to ignore msg.topic and use only fixed tags
  • Support for custom unit and timestamp
  • Errors are reported to the debug window and msg.result

Input parameters in msg

| Parameter | Description | Overrides/priority | Note | |------------------|-----------------------------------------------------------------------------|------------------------------|------------------------------------------| | msg.payload | Value(s) to write (naked or JSON) | - | Required | | msg.topic | Topic to map to columns/keys according to mapping | everything else (unless ignoreTopic) | If used, has highest priority | | msg.mapping | Mapping string (same format as in node) | mapping from node | Optional, has priority over node | | msg.tags | JSON object with tags | fixedTags from node | Ignored if topic is used | | msg.measurement| Overrides measurement from node | measurement from node | Ignored if topic is used | | msg.field | Overrides field from node | field from node | Ignored if topic is used | | msg.unit | Overrides unit from node | unit from node | | | msg.timestamp | Overrides write time | current time | | | msg.jsonb | Additional tags for JSONB | - | Merged with extra tags from topic |


Value priority (what overrides what)

  1. If topic is used (and not ignored):
    • All tags, measurement and field are taken only from topic according to mapping.
    • Other tags from msg or node config are ignored (except JSONB, which can be merged).
  2. If msg.mapping is present, it is used instead of node mapping.
  3. If topic is not used:
    • Values from msg (msg.tags, msg.measurement, msg.field) are used, or from node config.
  4. msg.unit and msg.timestamp always have priority over node config or current time.

Topic mapping logic

  • Mapping string (e.g. org/location/building/area/device/measurement/field) defines how each topic level is mapped.
  • - means skip this level.
  • Custom key (e.g. foo) stores the value in JSONB under this key.
  • If topic is longer than mapping, the extra parts are stored in JSONB as tag1, tag2, ... (always starting from 1).
  • If mapping is longer than topic, missing values are null.

Example:

  • Topic: ACME/PvPlant/Hall/Meters/Inverter1/Live/ActivePower
  • Mapping: org/location/building/area/device/measurement/field
  • Result:
    • org = ACME
    • location = PvPlant
    • building = Hall
    • area = Meters
    • device = Inverter1
    • measurement = Live
    • field = ActivePower

Example with custom keys and overflow:

  • Topic: A/B/C/D/E/F/G/H/I
  • Mapping: org/location/-/foo/device/-/bar/measurement/field
  • Result:
    • org = A
    • location = B
    • foo (JSONB) = D
    • device = E
    • bar (JSONB) = G
    • measurement = H
    • field = I
    • C and F are skipped, no overflow

Automatic value type detection

  • Decimal number → value_double
  • Boolean → value_bool
  • Integer:
    • home: value_int
    • industrial: value_bigint
  • Text → value_text

Error handling

  • Errors are reported to the debug window and to msg.result with details.

Usage examples

1. Write with topic and mapping

{
  "topic": "ACMPE/PvPlabt/Hall/Meters/Inverter1/Live/ActivePower",
  "payload": 1275
}

Mapping: org/location/building/area/device/measurement/field

2. Write without topic, only with tags in msg

{
  "payload": 42.5,
  "tags": { "org": "ACME", "location": "Plant1" },
  "measurement": "temperature",
  "field": "value"
}

3. Write with unit and timestamp override

{
  "payload": 100,
  "unit": "kWh",
  "timestamp": "2025-05-19T10:00:00Z"
}

4. Write with mapping in msg

{
  "topic": "A/B/C/D/E/F",
  "mapping": "org/location/building/area/device/field",
  "payload": 1
}

Notes

  • If payload is JSON, each key is a field, value is the measured value.
  • If payload is "naked", field is determined by mapping or node config.
  • If topic has more levels than mapping, the overflow goes to JSONB as tag1, tag2, ...

Database schema

See database-schema.sql for details.

Example flow

[
    {
        "id": "example-timescaledb-node",
        "type": "timescaledb",
        "z": "flow-id",
        "name": "Write to TimescaleDB",
        "server": "timescaledb-config",
        "schema": "industrial",
        "payloadType": "json",
        "measurement": "temperature",
        "field": "value",
        "tags": {
            "org": "ACME",
            "location": "Plant1",
            "building": "A",
            "area": "Zone1",
            "device": "Sensor42"
        },
        "x": 300,
        "y": 200,
        "wires": [[]]
    }
]

License

MIT

Example Flow

[
  {
    "id": "inject1",
    "type": "inject",
    "name": "MQTT: topic mapping",
    "props": [
      { "p": "payload" },
      { "p": "topic" }
    ],
    "payload": "42.5",
    "payloadType": "num",
    "topic": "ACME/PvPlant/Hall/Meters/Inverter1/Live/ActivePower",
    "x": 200,
    "y": 80,
    "wires": [["mqtt2tsdb"]]
  },
  {
    "id": "mqtt2tsdb",
    "type": "mqtt-to-timescaledb",
    "name": "MQTT to TimescaleDB",
    "server": "config1",
    "topicMapping": "org/location/building/area/device/measurement/field",
    "ignoreTopic": false,
    "payloadType": "naked",
    "unit": "",
    "fixedTags": "{}",
    "x": 500,
    "y": 80,
    "wires": [[]]
  },
  {
    "id": "inject2",
    "type": "inject",
    "name": "Payload: direct write",
    "props": [
      { "p": "payload" },
      { "p": "tags" },
      { "p": "measurement" },
      { "p": "field" }
    ],
    "payload": "55.2",
    "payloadType": "num",
    "tags": "{\"org\":\"ACME\",\"location\":\"Plant1\"}",
    "measurement": "temperature",
    "field": "value",
    "x": 200,
    "y": 180,
    "wires": [["payload2tsdb"]]
  },
  {
    "id": "payload2tsdb",
    "type": "timescaledb",
    "name": "Payload to TimescaleDB",
    "server": "config1",
    "payloadType": "naked",
    "measurement": "temperature",
    "field": "value",
    "unit": "",
    "fixedTags": "{\"org\":\"ACME\",\"location\":\"Plant1\"}",
    "x": 500,
    "y": 180,
    "wires": [[]]
  },
  {
    "id": "config1",
    "type": "timescaledb-config",
    "host": "localhost",
    "port": 5432,
    "database": "postgres",
    "user": "postgres",
    "password": "yourpassword",
    "ssl": true
  }
]

Usage

  • Use MQTT to TimescaleDB when you want to map topic levels to DB columns/tags.
  • Use Payload to TimescaleDB when your data is already normalized and you want to write it directly.

See detailed documentation in DOC_DETAILS.md for all options, mapping logic, and advanced scenarios.