{
  "id": "stream_triggers",
  "title": "Stream triggers",
  "url": "https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/triggers/stream_triggers/",
  "summary": "Execute a JavaScript function when an item is added to a stream",
  "tags": [
    "docs",
    "develop",
    "stack",
    "oss",
    "rs",
    "rc",
    "oss",
    "kubernetes",
    "clients"
  ],
  "last_updated": "2026-04-01T08:10:08-05:00",
  "page_type": "content",
  "content_hash": "607b19f4b79046b341e91ac67eb04f0fbcd3975ffe033005adb24ee4c4010ca7",
  "sections": [
    {
      "id": "overview",
      "title": "Overview",
      "role": "overview",
      "text": "Redis Stack's triggers and functions feature comes with a full stream API to process data from [Redis streams](). Unlike RedisGears v1 that provided a micro batching API, the new triggers and functions feature provides a **real streaming** API, which means that the data will be processed as soon as it enters the stream."
    },
    {
      "id": "register-a-stream-consumer",
      "title": "Register a stream consumer",
      "role": "content",
      "text": "Triggers and functions provide an API that allows to register a stream trigger. Do not get confused with [Redis streams consumer groups](), triggers and functions uses the Redis Module API to efficiently read the stream and manage its consumers. This approach gives a much better performance as there is no need to invoke any Redis commands in order to read from the stream. Lets see a simple example:\n\n[code example]\n\nArgument Description:\n\n* consumer - the consumer name.\n* stream - streams name prefix on which to trigger the callback.\n* callback - the callback to invoke on each element in the stream. Following the same rules of [sync and async invocation](). The callback will be invoke only on primary shard.\n\nIf we register this library (see the [quick start]() section to learn how to Register a RedisGears function) and run the following command on our Redis:\n\n[code example]\n\nWe will see the following line on the Redis log file:\n\n[code example]\n\nThe `data` argument which pass to the stream consumer callback are in the following format:\n\n[code example]\n\nThe reason why the record is a list of touples and not an object is because the Redis Stream specifications allows duplicate keys.\n\nNotice that `stream_name` and `record` fields might contains `null`'s if the data can not be decoded as string. the `*_raw` fields will always be provided and will contains the data as `JS` `ArrayBuffer`.\n\nWe can observe the streams which are tracked by our registered consumer using `TFUNCTION LIST` command:\n\n[code example]"
    },
    {
      "id": "enable-trimming-and-set-window",
      "title": "Enable trimming and set window",
      "role": "content",
      "text": "We can enable stream trimming by adding `isStreamTrimmed` optional argument after the trigger callback, we can also set the `window` argument that controls how many elements can be processed simultaneously. example:\n\n[code example]\n\nThe default values are:\n* `isStreamTrimmed` - `false`\n* `window` - 1\n\nIt is enough that a single consumer will enable trimming so that the stream will be trimmed. The stream will be trim according to the slowest consumer that consume the stream at a given time (even if this is not the consumer that enabled the trimming). Raising exception during the callback invocation will **not prevent the trimming**. The callback should decide how to handle failures by invoke a retry or write some error log. The error will be added to the `last_error` field on `TFUNCTION LIST` command."
    },
    {
      "id": "data-processing-guarantees",
      "title": "Data processing guarantees",
      "role": "content",
      "text": "As long as the primary shard is up and running we guarantee exactly once property (the callback will be triggered exactly one time on each element in the stream). In case of failure such as shard crashing, we guarantee at least once property (the callback will be triggered at least one time on each element in the stream)"
    },
    {
      "id": "upgrades",
      "title": "Upgrades",
      "role": "content",
      "text": "When upgrading the consumer code (using the `REPLACE` option of `TFUNCTION LOAD` command) the following consumer parameters can be updated:\n\n* Window\n* Trimming\n\nAny attempt to update any other parameter will result in an error when loading the library."
    }
  ],
  "examples": [
    {
      "id": "register-a-stream-consumer-ex0",
      "language": "js",
      "code": "#!js api_version=1.0 name=myFirstLibrary\n\nredis.registerStreamTrigger(\n    \"consumer\", // consumer name\n    \"stream\", // streams prefix\n    function(c, data) {\n        // callback to run on each element added to the stream\n        redis.log(JSON.stringify(data, (key, value) =>\n            typeof value === 'bigint'\n                ? value.toString()\n                : value // return everything else unchanged\n        ));\n    }\n);",
      "section_id": "register-a-stream-consumer"
    },
    {
      "id": "register-a-stream-consumer-ex1",
      "language": "plaintext",
      "code": "XADD stream:1 * foo1 bar1\nXADD stream:1 * foo2 bar2\nXADD stream:2 * foo1 bar1\nXADD stream:2 * foo2 bar2",
      "section_id": "register-a-stream-consumer"
    },
    {
      "id": "register-a-stream-consumer-ex2",
      "language": "plaintext",
      "code": "2630021:M 05 Jul 2022 17:13:22.506 * <redisgears_2> {\"id\":[\"1657030402506\",\"0\"],\"stream_name\":\"stream:1\",\"record\":[[\"foo1\",\"bar1\"]]}\n2630021:M 05 Jul 2022 17:13:25.323 * <redisgears_2> {\"id\":[\"1657030405323\",\"0\"],\"stream_name\":\"stream:1\",\"record\":[[\"foo2\",\"bar2\"]]}\n2630021:M 05 Jul 2022 17:13:29.475 * <redisgears_2> {\"id\":[\"1657030409475\",\"0\"],\"stream_name\":\"stream:2\",\"record\":[[\"foo1\",\"bar1\"]]}\n2630021:M 05 Jul 2022 17:13:32.715 * <redisgears_2> {\"id\":[\"1657030412715\",\"0\"],\"stream_name\":\"stream:2\",\"record\":[[\"foo2\",\"bar2\"]]}",
      "section_id": "register-a-stream-consumer"
    },
    {
      "id": "register-a-stream-consumer-ex3",
      "language": "json",
      "code": "{\n    \"id\": [\"<ms>\", \"<seq>\"],\n    \"stream_name\": \"<stream name>\",\n    \"stream_name_raw\": \"<stream name as ArrayBuffer>\",\n    \"record\":[\n        [\"<key>\", \"<value>\"],\n        .\n        .\n        [\"<key>\", \"<value>\"]\n    ],\n    \"record_raw\":[\n        [\"<key_raw>\", \"<value_raw>\"],\n        .\n        .\n        [\"<key_raw>\", \"<value_raw>\"]\n    ],\n}",
      "section_id": "register-a-stream-consumer"
    },
    {
      "id": "register-a-stream-consumer-ex4",
      "language": "plaintext",
      "code": "127.0.0.1:6379> TFUNCTION LIST LIBRARY lib vvv\n1)  1) \"engine\"\n    1) \"js\"\n    2) \"api_version\"\n    3) \"1.0\"\n    4) \"name\"\n    5) \"lib\"\n    6) \"pending_jobs\"\n    7) (integer) 0\n    8) \"user\"\n    9)  \"default\"\n    10) \"functions\"\n   1)  (empty array)\n   2)  \"stream_triggers\"\n   3)  1)  1) \"name\"\n           1) \"consumer\"\n           2) \"prefix\"\n           3) \"stream\"\n           4) \"window\"\n           5) (integer) 1\n           6) \"trim\"\n           7) \"disabled\"\n           8) \"num_streams\"\n          1)  (integer) 2\n          2)  \"streams\"\n          3)  1)  1) \"name\"\n                  1) \"stream:2\"\n                  2) \"last_processed_time\"\n                  3) (integer) 0\n                  4) \"avg_processed_time\"\n                  5) \"0\"\n                  6) \"last_lag\"\n                  7) (integer) 0\n                  8) \"avg_lag\"\n                 1)  \"0\"\n                 2)  \"total_record_processed\"\n                 3)  (integer) 2\n                 4)  \"id_to_read_from\"\n                 5)  \"1657030412715-0\"\n                 6)  \"last_error\"\n                 7)  \"None\"\n                 17) \"pending_ids\"\n                 18) (empty array)\n              1)  1) \"name\"\n                  1) \"stream:1\"\n                  2) \"last_processed_time\"\n                  3) (integer) 1\n                  4) \"avg_processed_time\"\n                  5) \"0.5\"\n                  6) \"last_lag\"\n                  7) (integer) 1\n                  8) \"avg_lag\"\n                 1)  \"0.5\"\n                 2)  \"total_record_processed\"\n                 3)  (integer) 2\n                 4)  \"id_to_read_from\"\n                 5)  \"1657030405323-0\"\n                 6)  \"last_error\"\n                 7)  \"None\"\n                 8)  \"pending_ids\"\n                 9)  (empty array)\n   4)  \"keyspace_triggers\"\n   5)  (empty array)",
      "section_id": "register-a-stream-consumer"
    },
    {
      "id": "enable-trimming-and-set-window-ex0",
      "language": "js",
      "code": "#!js api_version=1.0 name=myFirstLibrary\n\nredis.registerStreamTrigger(\n    \"consumer\", // consumer name\n    \"stream\", // streams prefix\n    function(c, data) {\n        // callback to run on each element added to the stream\n        redis.log(JSON.stringify(data, (key, value) =>\n            typeof value === 'bigint'\n                ? value.toString()\n                : value // return everything else unchanged\n        ));\n    }, \n    {\n        isStreamTrimmed: true,\n        window: 3   \n    }\n);",
      "section_id": "enable-trimming-and-set-window"
    }
  ]
}
