From fb58cd2da044a901751acfc7f307f9e422a08f24 Mon Sep 17 00:00:00 2001 From: Murat Kirazkaya Date: Sun, 2 Aug 2026 11:54:43 +0300 Subject: [PATCH 1/8] fix: enhance remark-code-tabs plugin to handle MDX files and ensure no-op for plain markdown --- src/plugins/remark-code-tabs.mjs | 13 ++++++++++++- tests/unit/remark-code-tabs.test.mjs | 29 ++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/plugins/remark-code-tabs.mjs b/src/plugins/remark-code-tabs.mjs index fd6e59a740..400012cfa1 100644 --- a/src/plugins/remark-code-tabs.mjs +++ b/src/plugins/remark-code-tabs.mjs @@ -36,6 +36,12 @@ * and leave the `code` nodes intact, so each block still gets full * expressive-code rendering (highlighting, copy button, …). * + * MDX only. The plugin emits MDX constructs (an `mdxjsEsm` import and a JSX + * `` element), so it is a no-op outside the MDX pipeline. In plain + * `.md` files those nodes would be serialized as literal text (the import + * would leak into the page) and the JSX wrapper cannot render, so the file is + * left untouched there. + * * The `tab="..."` token is stripped from each block's meta so expressive-code * never sees an unknown meta attribute. Labels are passed to the component as a * plain comma-separated string attribute (`tabs="CommonJS,ESM"`) to avoid @@ -225,7 +231,12 @@ function hasComponentImport(tree) { } export default function remarkCodeTabs() { - return (tree) => { + return (tree, file) => { + const isMdxFile = + file?.extname === '.mdx' || + (typeof file?.path === 'string' && file.path.toLowerCase().endsWith('.mdx')); + if (!isMdxFile) return; + const grouped = walk(tree); if (grouped && !hasComponentImport(tree)) { tree.children.unshift(buildImport()); diff --git a/tests/unit/remark-code-tabs.test.mjs b/tests/unit/remark-code-tabs.test.mjs index 1792d9a614..4359925f1f 100644 --- a/tests/unit/remark-code-tabs.test.mjs +++ b/tests/unit/remark-code-tabs.test.mjs @@ -7,9 +7,9 @@ function code(lang, meta = null, value = '') { return { type: 'code', lang, meta, value }; } -function run(children) { +function run(children, file) { const tree = { type: 'root', children }; - remarkCodeTabs()(tree); + remarkCodeTabs()(tree, file ?? { extname: '.mdx' }); return tree; } @@ -69,6 +69,31 @@ test('does not inject the import when nothing is grouped', () => { assert.equal(findTabs(tree), undefined); }); +test('is a no-op for plain markdown files (no import, no grouping, langs untouched)', () => { + const tree = run([code('cjs', null, "require('x')"), code('mjs', null, "import x from 'x'")], { + extname: '.md', + }); + assert.equal(hasImport(tree), false); + assert.equal(findTabs(tree), undefined); + assert.deepEqual( + tree.children.map((n) => n.lang), + ['cjs', 'mjs'] + ); +}); + +test('treats a missing file handle as non-MDX and stays a no-op', () => { + const tree = { type: 'root', children: [code('cjs'), code('mjs')] }; + remarkCodeTabs()(tree); + assert.equal(findTabs(tree), undefined); + assert.equal(hasImport(tree), false); +}); + +test('detects MDX via a file path ending in .mdx', () => { + const tree = run([code('cjs'), code('mjs')], { path: 'src/content/pages/en/x.mdx' }); + assert.ok(findTabs(tree)); + assert.ok(hasImport(tree)); +}); + test('does NOT group two standalone ts blocks (ts needs a cjs/mjs trigger)', () => { const tree = run([code('ts', null, 'const a = 1'), code('ts', null, 'const b = 2')]); assert.equal(findTabs(tree), undefined); From 6c847a2b7dc33b3b6347782edf4c13531573a723 Mon Sep 17 00:00:00 2001 From: Murat Kirazkaya Date: Sun, 2 Aug 2026 12:10:24 +0300 Subject: [PATCH 2/8] docs: enhance best practices for performance by adding worker threads and Node.js version recommendations --- .../en/advanced/best-practice-performance.mdx | 60 +++++++++++++++++-- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/src/content/pages/en/advanced/best-practice-performance.mdx b/src/content/pages/en/advanced/best-practice-performance.mdx index 62980393ca..70fde948f5 100644 --- a/src/content/pages/en/advanced/best-practice-performance.mdx +++ b/src/content/pages/en/advanced/best-practice-performance.mdx @@ -12,8 +12,10 @@ This topic clearly falls into the "devops" world, spanning both traditional deve - [Don't use synchronous functions](#dont-use-synchronous-functions) - [Do logging correctly](#do-logging-correctly) - [Handle exceptions properly](#handle-exceptions-properly) + - [Use worker threads for CPU-intensive tasks](#use-worker-threads-for-cpu-intensive-tasks) - Things to do in your environment / setup (the ops part): - [Set NODE_ENV to "production"](#set-node_env-to-production) + - [Use Node.js 24 or newer](#use-nodejs-24-or-newer) - [Ensure your app automatically restarts](#ensure-your-app-automatically-restarts) - [Run your app in a cluster](#run-your-app-in-a-cluster) - [Cache request results](#cache-request-results) @@ -28,6 +30,7 @@ Here are some things you can do in your code to improve your application's perfo - [Don't use synchronous functions](#dont-use-synchronous-functions) - [Do logging correctly](#do-logging-correctly) - [Handle exceptions properly](#handle-exceptions-properly) +- [Use worker threads for CPU-intensive tasks](#use-worker-threads-for-cpu-intensive-tasks) ### Use gzip compression @@ -147,16 +150,55 @@ Additionally, using `uncaughtException` is officially recognized as [crude](http We also don't recommend using [domains](https://nodejs.org/api/domain.html). It generally doesn't solve the problem and is a deprecated module. +### Use worker threads for CPU-intensive tasks + +Node.js runs your JavaScript on a single thread, so a CPU-intensive operation - such as image processing, parsing or transforming large payloads, or cryptographic operations - can block the event loop and slow down every request your Express app is handling. If your app does CPU-bound work, you can run it in [worker threads](https://nodejs.org/api/worker_threads.html), which execute JavaScript in parallel within the same process, keeping the main thread free to serve requests. + +Worker threads share memory with the main thread and are a good fit for offloading CPU-intensive work. They are not a replacement for the [cluster module](#run-your-app-in-a-cluster), which runs multiple instances of your app in separate processes to scale across CPU cores. For scaling your app itself, see [Run your app in a cluster](#run-your-app-in-a-cluster). + +Creating a worker thread is relatively expensive, so avoid creating a new one for every request. Instead, use a fixed pool of workers that you reuse. + +```js +const { Worker } = require('node:worker_threads'); + +app.post('/resize', (req, res, next) => { + const worker = new Worker('./resize-worker.js', { workerData: req.body.image }); + + worker.once('message', (result) => res.send(result)); + worker.once('error', next); +}); +``` + +For more information, see the [worker_threads documentation](https://nodejs.org/api/worker_threads.html). + ## Things to do in your environment / setup Here are some things you can do in your system environment to improve your app's performance: -- [Set NODE_ENV to "production"](#set-node_env-to-production) -- [Ensure your app automatically restarts](#ensure-your-app-automatically-restarts) -- [Run your app in a cluster](#run-your-app-in-a-cluster) -- [Cache request results](#cache-request-results) -- [Use a load balancer](#use-a-load-balancer) -- [Use a reverse proxy](#use-a-reverse-proxy) +- [Things to do in your code](#things-to-do-in-your-code) + - [Use gzip compression](#use-gzip-compression) + - [Don't use synchronous functions](#dont-use-synchronous-functions) + - [Do logging correctly](#do-logging-correctly) + - [For debugging](#for-debugging) + - [For app activity](#for-app-activity) + - [Handle exceptions properly](#handle-exceptions-properly) + - [Use try-catch](#use-try-catch) + - [Use promises](#use-promises) + - [What not to do](#what-not-to-do) + - [Use worker threads for CPU-intensive tasks](#use-worker-threads-for-cpu-intensive-tasks) +- [Things to do in your environment / setup](#things-to-do-in-your-environment--setup) + - [Set NODE\_ENV to "production"](#set-node_env-to-production) + - [Use Node.js 24 or newer](#use-nodejs-24-or-newer) + - [Ensure your app automatically restarts](#ensure-your-app-automatically-restarts) + - [Use a process manager](#use-a-process-manager) + - [Use an init system](#use-an-init-system) + - [Systemd](#systemd) + - [Run your app in a cluster](#run-your-app-in-a-cluster) + - [Using Node's cluster module](#using-nodes-cluster-module) + - [Using PM2](#using-pm2) + - [Cache request results](#cache-request-results) + - [Use a load balancer](#use-a-load-balancer) + - [Use a reverse proxy](#use-a-reverse-proxy) ### Set NODE_ENV to "production" @@ -183,6 +225,12 @@ Environment=NODE_ENV=production For more information, see [Using Environment Variables In systemd Units](https://www.flatcar.org/docs/latest/setup/systemd/environment-variables/). +### Use Node.js 24 or newer + +Running your app on a recent Node.js release is one of the easiest ways to improve performance. Benchmarks show that running Express on Node.js 24 can be roughly two times faster than on Node.js 22. The gains come from the cumulative improvements in the V8 JavaScript engine shipped with newer Node.js versions: a faster JIT compiler, more efficient garbage collection, and faster property access and regular-expression matching, all of which Express relies on heavily when processing requests and matching routes. + +Supporting old Node.js versions also holds Express back from adopting performance improvements, which is why Express 5 dropped support for versions before v18. See the [Node.js releases](https://nodejs.org/en/about/previous-releases) page for the current supported versions. + ### Ensure your app automatically restarts In production, you don't want your application to be offline, ever. This means you need to make sure it restarts both if the app crashes and if the server itself crashes. Although you hope that neither of those events occurs, realistically you must account for both eventualities by: From 5b0a373159adfdce91c71f1ca9bb389c787e9094 Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Sun, 2 Aug 2026 12:36:25 -0500 Subject: [PATCH 3/8] Revert "fix: enhance remark-code-tabs plugin to handle MDX files and ensure no-op for plain markdown" This reverts commit fb58cd2da044a901751acfc7f307f9e422a08f24. --- src/plugins/remark-code-tabs.mjs | 13 +------------ tests/unit/remark-code-tabs.test.mjs | 29 ++-------------------------- 2 files changed, 3 insertions(+), 39 deletions(-) diff --git a/src/plugins/remark-code-tabs.mjs b/src/plugins/remark-code-tabs.mjs index 400012cfa1..fd6e59a740 100644 --- a/src/plugins/remark-code-tabs.mjs +++ b/src/plugins/remark-code-tabs.mjs @@ -36,12 +36,6 @@ * and leave the `code` nodes intact, so each block still gets full * expressive-code rendering (highlighting, copy button, …). * - * MDX only. The plugin emits MDX constructs (an `mdxjsEsm` import and a JSX - * `` element), so it is a no-op outside the MDX pipeline. In plain - * `.md` files those nodes would be serialized as literal text (the import - * would leak into the page) and the JSX wrapper cannot render, so the file is - * left untouched there. - * * The `tab="..."` token is stripped from each block's meta so expressive-code * never sees an unknown meta attribute. Labels are passed to the component as a * plain comma-separated string attribute (`tabs="CommonJS,ESM"`) to avoid @@ -231,12 +225,7 @@ function hasComponentImport(tree) { } export default function remarkCodeTabs() { - return (tree, file) => { - const isMdxFile = - file?.extname === '.mdx' || - (typeof file?.path === 'string' && file.path.toLowerCase().endsWith('.mdx')); - if (!isMdxFile) return; - + return (tree) => { const grouped = walk(tree); if (grouped && !hasComponentImport(tree)) { tree.children.unshift(buildImport()); diff --git a/tests/unit/remark-code-tabs.test.mjs b/tests/unit/remark-code-tabs.test.mjs index 4359925f1f..1792d9a614 100644 --- a/tests/unit/remark-code-tabs.test.mjs +++ b/tests/unit/remark-code-tabs.test.mjs @@ -7,9 +7,9 @@ function code(lang, meta = null, value = '') { return { type: 'code', lang, meta, value }; } -function run(children, file) { +function run(children) { const tree = { type: 'root', children }; - remarkCodeTabs()(tree, file ?? { extname: '.mdx' }); + remarkCodeTabs()(tree); return tree; } @@ -69,31 +69,6 @@ test('does not inject the import when nothing is grouped', () => { assert.equal(findTabs(tree), undefined); }); -test('is a no-op for plain markdown files (no import, no grouping, langs untouched)', () => { - const tree = run([code('cjs', null, "require('x')"), code('mjs', null, "import x from 'x'")], { - extname: '.md', - }); - assert.equal(hasImport(tree), false); - assert.equal(findTabs(tree), undefined); - assert.deepEqual( - tree.children.map((n) => n.lang), - ['cjs', 'mjs'] - ); -}); - -test('treats a missing file handle as non-MDX and stays a no-op', () => { - const tree = { type: 'root', children: [code('cjs'), code('mjs')] }; - remarkCodeTabs()(tree); - assert.equal(findTabs(tree), undefined); - assert.equal(hasImport(tree), false); -}); - -test('detects MDX via a file path ending in .mdx', () => { - const tree = run([code('cjs'), code('mjs')], { path: 'src/content/pages/en/x.mdx' }); - assert.ok(findTabs(tree)); - assert.ok(hasImport(tree)); -}); - test('does NOT group two standalone ts blocks (ts needs a cjs/mjs trigger)', () => { const tree = run([code('ts', null, 'const a = 1'), code('ts', null, 'const b = 2')]); assert.equal(findTabs(tree), undefined); From d0ceedbcc78ea5b48910e8b28de80f1f76dca5db Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Sun, 2 Aug 2026 12:43:25 -0500 Subject: [PATCH 4/8] docs: update Node.js version recommendation to use the latest LTS release for improved performance --- .../pages/en/advanced/best-practice-performance.mdx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/content/pages/en/advanced/best-practice-performance.mdx b/src/content/pages/en/advanced/best-practice-performance.mdx index 70fde948f5..da414f543e 100644 --- a/src/content/pages/en/advanced/best-practice-performance.mdx +++ b/src/content/pages/en/advanced/best-practice-performance.mdx @@ -15,7 +15,7 @@ This topic clearly falls into the "devops" world, spanning both traditional deve - [Use worker threads for CPU-intensive tasks](#use-worker-threads-for-cpu-intensive-tasks) - Things to do in your environment / setup (the ops part): - [Set NODE_ENV to "production"](#set-node_env-to-production) - - [Use Node.js 24 or newer](#use-nodejs-24-or-newer) + - [Use the latest LTS version of Node.js](#use-the-latest-lts-version-of-nodejs) - [Ensure your app automatically restarts](#ensure-your-app-automatically-restarts) - [Run your app in a cluster](#run-your-app-in-a-cluster) - [Cache request results](#cache-request-results) @@ -188,7 +188,7 @@ Here are some things you can do in your system environment to improve your app's - [Use worker threads for CPU-intensive tasks](#use-worker-threads-for-cpu-intensive-tasks) - [Things to do in your environment / setup](#things-to-do-in-your-environment--setup) - [Set NODE\_ENV to "production"](#set-node_env-to-production) - - [Use Node.js 24 or newer](#use-nodejs-24-or-newer) + - [Use the latest LTS version of Node.js](#use-the-latest-lts-version-of-nodejs) - [Ensure your app automatically restarts](#ensure-your-app-automatically-restarts) - [Use a process manager](#use-a-process-manager) - [Use an init system](#use-an-init-system) @@ -225,11 +225,9 @@ Environment=NODE_ENV=production For more information, see [Using Environment Variables In systemd Units](https://www.flatcar.org/docs/latest/setup/systemd/environment-variables/). -### Use Node.js 24 or newer +### Use the latest LTS version of Node.js -Running your app on a recent Node.js release is one of the easiest ways to improve performance. Benchmarks show that running Express on Node.js 24 can be roughly two times faster than on Node.js 22. The gains come from the cumulative improvements in the V8 JavaScript engine shipped with newer Node.js versions: a faster JIT compiler, more efficient garbage collection, and faster property access and regular-expression matching, all of which Express relies on heavily when processing requests and matching routes. - -Supporting old Node.js versions also holds Express back from adopting performance improvements, which is why Express 5 dropped support for versions before v18. See the [Node.js releases](https://nodejs.org/en/about/previous-releases) page for the current supported versions. +Running your app on a recent Node.js release is one of the easiest ways to improve performance. Each new version of Node.js ships with cumulative improvements to the V8 JavaScript engine and the runtime itself, so the same Express app can handle significantly more requests simply by upgrading. Benchmarks such as NodeSource's [Node.js Performance Report](https://nodesource.com/pages/content-node-performance-report-wb.html) show how much throughput can improve from one Node.js version to the next. For production, use the latest [LTS release](https://nodejs.org/en/about/previous-releases) of Node.js, and keep it up to date as new versions come out. ### Ensure your app automatically restarts From 6b8b2b86ecf1e8c540b2de0c2b3aa9d9a291df76 Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Sun, 2 Aug 2026 12:46:11 -0500 Subject: [PATCH 5/8] docs: streamline performance best practices section by removing redundant lists --- .../en/advanced/best-practice-performance.mdx | 35 ++----------------- 1 file changed, 2 insertions(+), 33 deletions(-) diff --git a/src/content/pages/en/advanced/best-practice-performance.mdx b/src/content/pages/en/advanced/best-practice-performance.mdx index da414f543e..59e409d3ae 100644 --- a/src/content/pages/en/advanced/best-practice-performance.mdx +++ b/src/content/pages/en/advanced/best-practice-performance.mdx @@ -24,13 +24,7 @@ This topic clearly falls into the "devops" world, spanning both traditional deve ## Things to do in your code -Here are some things you can do in your code to improve your application's performance: - -- [Use gzip compression](#use-gzip-compression) -- [Don't use synchronous functions](#dont-use-synchronous-functions) -- [Do logging correctly](#do-logging-correctly) -- [Handle exceptions properly](#handle-exceptions-properly) -- [Use worker threads for CPU-intensive tasks](#use-worker-threads-for-cpu-intensive-tasks) +Here are some things you can do in your code to improve your application's performance. ### Use gzip compression @@ -173,32 +167,7 @@ For more information, see the [worker_threads documentation](https://nodejs.org/ ## Things to do in your environment / setup -Here are some things you can do in your system environment to improve your app's performance: - -- [Things to do in your code](#things-to-do-in-your-code) - - [Use gzip compression](#use-gzip-compression) - - [Don't use synchronous functions](#dont-use-synchronous-functions) - - [Do logging correctly](#do-logging-correctly) - - [For debugging](#for-debugging) - - [For app activity](#for-app-activity) - - [Handle exceptions properly](#handle-exceptions-properly) - - [Use try-catch](#use-try-catch) - - [Use promises](#use-promises) - - [What not to do](#what-not-to-do) - - [Use worker threads for CPU-intensive tasks](#use-worker-threads-for-cpu-intensive-tasks) -- [Things to do in your environment / setup](#things-to-do-in-your-environment--setup) - - [Set NODE\_ENV to "production"](#set-node_env-to-production) - - [Use the latest LTS version of Node.js](#use-the-latest-lts-version-of-nodejs) - - [Ensure your app automatically restarts](#ensure-your-app-automatically-restarts) - - [Use a process manager](#use-a-process-manager) - - [Use an init system](#use-an-init-system) - - [Systemd](#systemd) - - [Run your app in a cluster](#run-your-app-in-a-cluster) - - [Using Node's cluster module](#using-nodes-cluster-module) - - [Using PM2](#using-pm2) - - [Cache request results](#cache-request-results) - - [Use a load balancer](#use-a-load-balancer) - - [Use a reverse proxy](#use-a-reverse-proxy) +Here are some things you can do in your system environment to improve your app's performance. ### Set NODE_ENV to "production" From 3ab126f0b9b187a335347f28175ba7fc9e536176 Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Sun, 2 Aug 2026 12:49:32 -0500 Subject: [PATCH 6/8] docs: refine introduction of performance best practices by removing redundant list items --- .../en/advanced/best-practice-performance.mdx | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/content/pages/en/advanced/best-practice-performance.mdx b/src/content/pages/en/advanced/best-practice-performance.mdx index 59e409d3ae..5f8f9c294a 100644 --- a/src/content/pages/en/advanced/best-practice-performance.mdx +++ b/src/content/pages/en/advanced/best-practice-performance.mdx @@ -5,22 +5,7 @@ description: Discover performance and reliability best practices for Express app This article discusses performance and reliability best practices for Express applications deployed to production. -This topic clearly falls into the "devops" world, spanning both traditional development and operations. Accordingly, the information is divided into two parts: - -- Things to do in your code (the dev part): - - [Use gzip compression](#use-gzip-compression) - - [Don't use synchronous functions](#dont-use-synchronous-functions) - - [Do logging correctly](#do-logging-correctly) - - [Handle exceptions properly](#handle-exceptions-properly) - - [Use worker threads for CPU-intensive tasks](#use-worker-threads-for-cpu-intensive-tasks) -- Things to do in your environment / setup (the ops part): - - [Set NODE_ENV to "production"](#set-node_env-to-production) - - [Use the latest LTS version of Node.js](#use-the-latest-lts-version-of-nodejs) - - [Ensure your app automatically restarts](#ensure-your-app-automatically-restarts) - - [Run your app in a cluster](#run-your-app-in-a-cluster) - - [Cache request results](#cache-request-results) - - [Use a load balancer](#use-a-load-balancer) - - [Use a reverse proxy](#use-a-reverse-proxy) +This topic clearly falls into the "devops" world, spanning both traditional development and operations. Accordingly, the information is divided into two parts: [things to do in your code](#things-to-do-in-your-code) (the dev part), and [things to do in your environment / setup](#things-to-do-in-your-environment--setup) (the ops part). ## Things to do in your code From 4b60e71cc1cb79075828077fe3b64475a23afcda Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Sun, 2 Aug 2026 12:55:10 -0500 Subject: [PATCH 7/8] docs: clarify data-sharing semantics in worker threads section Data passed to a worker via workerData or postMessage is copied, not shared; sharing requires transferring objects or SharedArrayBuffer. Also remove a duplicated worker_threads link and a redundant cluster cross-reference. --- src/content/pages/en/advanced/best-practice-performance.mdx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/content/pages/en/advanced/best-practice-performance.mdx b/src/content/pages/en/advanced/best-practice-performance.mdx index 5f8f9c294a..2d42fdea8a 100644 --- a/src/content/pages/en/advanced/best-practice-performance.mdx +++ b/src/content/pages/en/advanced/best-practice-performance.mdx @@ -131,9 +131,9 @@ We also don't recommend using [domains](https://nodejs.org/api/domain.html). It ### Use worker threads for CPU-intensive tasks -Node.js runs your JavaScript on a single thread, so a CPU-intensive operation - such as image processing, parsing or transforming large payloads, or cryptographic operations - can block the event loop and slow down every request your Express app is handling. If your app does CPU-bound work, you can run it in [worker threads](https://nodejs.org/api/worker_threads.html), which execute JavaScript in parallel within the same process, keeping the main thread free to serve requests. +Node.js runs your JavaScript on a single thread, so a CPU-intensive operation (such as image processing, parsing or transforming large payloads, or cryptographic operations) can block the event loop and slow down every request your Express app is handling. If your app does CPU-bound work, you can run it in [worker threads](https://nodejs.org/api/worker_threads.html), which execute JavaScript in parallel within the same process, keeping the main thread free to serve requests. -Worker threads share memory with the main thread and are a good fit for offloading CPU-intensive work. They are not a replacement for the [cluster module](#run-your-app-in-a-cluster), which runs multiple instances of your app in separate processes to scale across CPU cores. For scaling your app itself, see [Run your app in a cluster](#run-your-app-in-a-cluster). +Each worker thread runs in its own JavaScript engine instance, so the data you pass to it through `workerData` or `postMessage` is copied, not shared. Copying large payloads has a cost of its own, which you can avoid by transferring objects such as `ArrayBuffer`s or by sharing memory explicitly with `SharedArrayBuffer`. Worker threads are not a replacement for [running your app in a cluster](#run-your-app-in-a-cluster), which scales your app across CPU cores by running multiple instances of it in separate processes. Creating a worker thread is relatively expensive, so avoid creating a new one for every request. Instead, use a fixed pool of workers that you reuse. @@ -148,8 +148,6 @@ app.post('/resize', (req, res, next) => { }); ``` -For more information, see the [worker_threads documentation](https://nodejs.org/api/worker_threads.html). - ## Things to do in your environment / setup Here are some things you can do in your system environment to improve your app's performance. From 1985c1be4cd148aa8196e6f1235a860df1da655e Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Sun, 2 Aug 2026 13:12:07 -0500 Subject: [PATCH 8/8] docs: expand worker threads example with a production-ready pool Frame the raw worker_threads snippet as an illustration and add a worker pool example using piscina, since creating a worker per request is expensive and the Node.js docs recommend pooling instead. Link Node's "Don't Block the Event Loop" guide for further guidance. --- .../en/advanced/best-practice-performance.mdx | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/content/pages/en/advanced/best-practice-performance.mdx b/src/content/pages/en/advanced/best-practice-performance.mdx index 2d42fdea8a..a7b712f812 100644 --- a/src/content/pages/en/advanced/best-practice-performance.mdx +++ b/src/content/pages/en/advanced/best-practice-performance.mdx @@ -135,7 +135,7 @@ Node.js runs your JavaScript on a single thread, so a CPU-intensive operation (s Each worker thread runs in its own JavaScript engine instance, so the data you pass to it through `workerData` or `postMessage` is copied, not shared. Copying large payloads has a cost of its own, which you can avoid by transferring objects such as `ArrayBuffer`s or by sharing memory explicitly with `SharedArrayBuffer`. Worker threads are not a replacement for [running your app in a cluster](#run-your-app-in-a-cluster), which scales your app across CPU cores by running multiple instances of it in separate processes. -Creating a worker thread is relatively expensive, so avoid creating a new one for every request. Instead, use a fixed pool of workers that you reuse. +This example offloads an image-resizing task to a worker, where `resize-worker.js` receives the image via `workerData` and posts the result back when it's done: ```js const { Worker } = require('node:worker_threads'); @@ -148,6 +148,27 @@ app.post('/resize', (req, res, next) => { }); ``` +Note that this example is kept simple for illustration: creating a worker thread is relatively expensive, so [the Node.js documentation recommends using a pool of workers](https://nodejs.org/api/worker_threads.html#worker-threads) instead of spawning a new one for every request. Create a fixed pool of workers at startup and dispatch tasks to them as requests come in, for example with a library like [piscina](https://www.npmjs.com/package/piscina): + +```js +const path = require('node:path'); +const Piscina = require('piscina'); + +const pool = new Piscina({ filename: path.resolve(__dirname, 'resize-worker.js') }); + +app.post('/resize', async (req, res, next) => { + try { + res.send(await pool.run(req.body.image)); + } catch (err) { + next(err); + } +}); +``` + +Here `resize-worker.js` simply exports the function to run in the pool, and piscina takes care of creating the workers, queueing tasks, and reusing threads across requests. + +For more guidance on keeping the event loop responsive, including when offloading work is worth its communication costs, see [Don't Block the Event Loop](https://nodejs.org/en/learn/asynchronous-work/dont-block-the-event-loop) in the Node.js documentation. + ## Things to do in your environment / setup Here are some things you can do in your system environment to improve your app's performance.