{
  "schema_version": 2,
  "id": "develop/clients/nodejs/transpipe",
  "title": "Pipelines and transactions",
  "url": "https://redis.io/docs/latest/develop/clients/nodejs/transpipe/",
  "summary": "Learn how to use Redis pipelines and transactions",
  "tags": [
    "docs",
    "develop",
    "stack",
    "oss",
    "rs",
    "rc",
    "oss",
    "kubernetes",
    "clients"
  ],
  "last_updated": "2026-09-08T15:03:39+01:00",
  "page_type": "content",
  "content_hash": "52b9ac09b652a6bd78f97eafd7e9d4fea63c9456357aa122cd247d1db24b7973",
  "sections": [
    {
      "id": "overview",
      "title": "Overview",
      "role": "overview",
      "text": "Redis lets you send a sequence of commands to the server together in a batch.\nThere are two types of batch that you can use:\n\n-   **Pipelines** avoid network and processing overhead by sending several commands\n    to the server together in a single communication. The server then sends back\n    a single communication with all the responses. See the\n    [Pipelining](https://redis.io/docs/latest/develop/using-commands/pipelining) page for more\n    information.\n-   **Transactions** guarantee that all the included commands will execute\n    to completion without being interrupted by commands from other clients.\n    See the [Transactions](https://redis.io/docs/latest/develop/using-commands/transactions)\n    page for more information."
    },
    {
      "id": "execute-a-pipeline",
      "title": "Execute a pipeline",
      "role": "content",
      "text": "There are two ways to execute commands in a pipeline. Firstly, `node-redis` will\nautomatically pipeline commands that execute within the same \"tick\" of the\n[event loop](https://nodejs.org/en/learn/asynchronous-work/event-loop-timers-and-nexttick#what-is-the-event-loop).\nYou can ensure that commands happen in the same tick very easily by including them in a\n[`Promise.all()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all)\ncall, as shown in the following example. The chained `then(...)` callback is optional\nand you can often omit it for commands that write data and only return a\nstatus result.\n\n[code example]\n\nYou can also create a pipeline object using the\n[`multi()`](https://redis.io/docs/latest/commands/multi) method\nand then add commands to it using methods that resemble the standard\ncommand methods (for example, `set()` and `get()`). The commands are\nbuffered in the pipeline and only execute when you call the\n`execAsPipeline()` method on the pipeline object. Again, the\n`then(...)` callback is optional.\n\n[code example]\n\nThe two approaches are almost equivalent, but they have different behavior\nwhen the connection is lost during the execution of the pipeline. After\nthe connection is re-established, a `Promise.all()` pipeline will\ncontinue execution from the point where the interruption happened,\nbut a `multi()` pipeline will discard any remaining commands that\ndidn't execute."
    },
    {
      "id": "execute-a-transaction",
      "title": "Execute a transaction",
      "role": "content",
      "text": "A transaction works in a similar way to a pipeline. Create a\ntransaction object with the `multi()` command, call command methods\non that object, and then call the transaction object's \n`exec()` method to execute it.\n\n[code example]"
    },
    {
      "id": "watch-keys-for-changes",
      "title": "Watch keys for changes",
      "role": "content",
      "text": "Redis supports *optimistic locking* to avoid inconsistent updates\nto different keys. The basic idea is to watch for changes to any\nkeys that you use in a transaction while you are processing the\nupdates. If the watched keys do change, you must restart the updates\nwith the latest data from the keys. See\n[Transactions](https://redis.io/docs/latest/develop/using-commands/transactions)\nfor more information about optimistic locking.\n\nThe code below reads a string\nthat represents a `PATH` variable for a command shell, then appends a new\ncommand path to the string before attempting to write it back. If the watched\nkey is modified by another client before writing, the transaction aborts.\nNote that you should call read-only commands for the watched keys synchronously on\nthe usual `client` object but you still call commands for the transaction on the\ntransaction object created with `multi()`.\n\nFor production usage, you would generally call code like the following in\na loop to retry it until it succeeds or else report or log the failure.\n\n[code example]\n\nIn an environment where multiple concurrent requests are sharing a connection\n(such as a web server), you must use a connection pool to get an isolated connection,\nas shown below:\n\n[code example]\n\nThis is important because the server tracks the state of the WATCH on a\nper-connection basis, and concurrent WATCH and MULTI/EXEC calls on the same\nconnection will interfere with one another. See\n[`RedisClientPool`](https://github.com/redis/node-redis/blob/master/docs/pool.md)\nfor more information."
    }
  ],
  "examples": [
    {
      "id": "execute-a-pipeline-ex0",
      "language": "js",
      "code": "await Promise.all([\n  client.set('seat:0', '#0'),\n  client.set('seat:1', '#1'),\n  client.set('seat:2', '#2'),\n]).then((results) =>{\n    console.log(results);\n    // >>> ['OK', 'OK', 'OK']\n});\n\nawait Promise.all([\n    client.get('seat:0'),\n    client.get('seat:1'),\n    client.get('seat:2'),\n]).then((results) =>{\n    console.log(results);\n    // >>> ['#0', '#1', '#2']\n});",
      "section_id": "execute-a-pipeline"
    },
    {
      "id": "execute-a-pipeline-ex1",
      "language": "js",
      "code": "await client.multi()\n    .set('seat:3', '#3')\n    .set('seat:4', '#4')\n    .set('seat:5', '#5')\n    .execAsPipeline()\n    .then((results) => {\n        console.log(results);\n        // >>> ['OK', 'OK', 'OK']\n    });",
      "section_id": "execute-a-pipeline"
    },
    {
      "id": "execute-a-transaction-ex0",
      "language": "js",
      "code": "const [res1, res2, res3] = await client.multi()\n    .incrBy(\"counter:1\", 1)\n    .incrBy(\"counter:2\", 2)\n    .incrBy(\"counter:3\", 3)\n    .exec();\n\nconsole.log(res1); // >>> 1\nconsole.log(res2); // >>> 2\nconsole.log(res3); // >>> 3",
      "section_id": "execute-a-transaction"
    },
    {
      "id": "watch-keys-for-changes-ex0",
      "language": "js",
      "code": "// Set initial value of `shellpath`.\nclient.set('shellpath', '/usr/syscmds/');\n\n// Watch the key we are about to update.\nawait client.watch('shellpath');\n\nconst currentPath = await client.get('shellpath');\nconst newPath = currentPath + ':/usr/mycmds/';\n\n// Attempt to write the watched key.\nawait client.multi()    \n    .set('shellpath', newPath)\n    .exec()\n    .then((result) => {\n        // This is called when the pipeline executes\n        // successfully.\n        console.log(result);\n    }, (err) => {\n        // This is called when a watched key was changed.\n        // Handle the error here.\n        console.log(err);\n    });\n\nconst updatedPath = await client.get('shellpath');\nconsole.log(updatedPath);\n// >>> /usr/syscmds/:/usr/mycmds/",
      "section_id": "watch-keys-for-changes"
    },
    {
      "id": "watch-keys-for-changes-ex1",
      "language": "js",
      "code": "import { createClientPool } from 'redis';\n\nconst pool = await createClientPool()\n  .on('error', err => console.error('Redis Client Pool Error', err));\n\ntry {\n  await pool.execute(async client => {\n    await client.watch('key');\n\n    const multi = client.multi()\n      .ping()\n      .get('key');\n\n    if (Math.random() > 0.5) {\n      await client.watch('another-key');\n      multi.set('another-key', await client.get('another-key') / 2);\n    }\n\n    return multi.exec();\n  });\n} catch (err) {\n  if (err instanceof WatchError) {\n    // the transaction aborted\n  }\n}",
      "section_id": "watch-keys-for-changes"
    }
  ]
}
