NodeJSWhen to Use (and Not Use) Node.js

When to Use (and Not Use) Node.js

Every technology has a sweet spot. Choosing Node.js because it is trendy — or avoiding it because of a myth — both lead to pain. This page gives you a decision framework grounded in how Node actually works, plus the patterns that let you stretch it beyond its comfort zone.

The one-sentence rule
Rule of thumb
Node.js excels at **I/O-bound, high-concurrency** workloads and struggles with **CPU-bound** ones. If your app spends its time *waiting* (on databases, APIs, files, sockets), Node is excellent. If it spends its time *computing* on a single request, think carefully.

The reason traces directly back to the single-threaded, non-blocking model: waiting is delegated and essentially free, but computing happens on the one thread that also serves everyone else.

Great fits for Node.js

Use case

Why Node fits

REST / GraphQL APIs

Mostly I/O: receive request, query DB, serialize JSON, respond

Real-time apps (chat, presence, games)

Many mostly-idle WebSocket connections — cheap on the event loop

Microservices

Fast startup, low memory, easy to deploy many small services

Serverless functions

Quick cold starts on Lambda/Vercel/Cloudflare

API gateways & proxies

Concurrently fan out to many upstreams and aggregate

Streaming (data/media/uploads)

Process chunk-by-chunk without buffering everything in memory

CLIs & build tooling

Vite, webpack, ESLint, Prettier — fast startup, npm ecosystem

Server-side rendering (SSR)

Next.js, Nuxt, Remix render React/Vue on the server

Poor fits — and what to do instead

Workload

Verdict

Better approach

Video/image transcoding

Poor on the main thread

Offload to a worker, child process, or a dedicated service (ffmpeg)

Machine-learning training

Poor

Use Python/GPU; let Node call it as a service

Heavy scientific computation

Poor

Worker Threads, native add-ons, or another language

Hard real-time / low-latency control

Poor

GC pauses are non-deterministic — use C/C++/Rust

Large in-process data crunching

Risky

Stream it, batch it, or move it to a queue/worker

Caveat: poor fit is not a ban
CPU-bound work is not *impossible* in Node — it just must not run on the main thread. You can offload it to [Worker Threads](/nodejs/worker-threads), a [child process](/nodejs/child-processes), or an external job queue (BullMQ, a message broker). The point is that *naive* CPU work in a request handler kills concurrency. Architect around it and Node remains a fine orchestrator.
The offloading patterns

When you do have a CPU-heavy task, these are the standard escape hatches, from lightest to heaviest:

  • Worker Threads — run the computation on another thread within the same process, sharing memory via SharedArrayBuffer when needed. Best for CPU work that is part of the app. See Worker Threads.

  • Child processes — spawn a separate process (even a non-JS program like ffmpeg) and communicate over stdio/IPC. See Child Processes.

  • Job queues — push the task to a queue (Redis/BullMQ, a broker) and let dedicated worker processes handle it asynchronously, decoupled from the request.

  • A separate service — extract the heavy part into its own service in whatever language suits it, and have Node call it over HTTP/gRPC.

Offloading a CPU task to a worker thread

JS
import { Worker } from 'node:worker_threads'

function runHeavyTask(input) {
  return new Promise((resolve, reject) => {
    const worker = new Worker('./heavy-task.js', { workerData: input })
    worker.on('message', resolve)   // result comes back without blocking the loop
    worker.on('error', reject)
  })
}

// The main thread stays free to serve other requests.
const result = await runHeavyTask({ size: 10_000_000 })
Quick comparison

Scenario

Node.js fit

Why

JSON API over a database

Excellent

I/O-bound, high concurrency

Real-time chat / presence

Excellent

Event-driven, many idle connections

Image/video processing

Poor (offload it)

CPU-bound, blocks the loop

Machine-learning training

Poor

Heavy compute; use Python/GPU

CLI tool / bundler

Excellent

Fast startup, npm ecosystem

High-frequency trading core

Poor

GC pauses; needs deterministic latency

Myths to discard
  • "Node can not scale" — it scales superbly for I/O. You scale across CPU cores by running multiple processes (cluster / containers), which is normal for any runtime.

  • "Single-threaded means slow" — for I/O it is often faster than thread-per-request because it avoids context-switch and memory overhead.

  • "Node is only for small apps" — Netflix, PayPal, Uber, and Walmart run it at massive scale.

  • "You must rewrite everything in JS" — Node is a great orchestrator; it can shell out to other languages for the parts they do best.

A decision checklist
  • Is the work mostly waiting on I/O? → Node is a strong choice.

  • Are there many concurrent connections, many idle? → Node shines.

  • Do you value shared code/types with a JS front end? → Big win.

  • Is there a CPU-heavy core step? → Plan to offload it before committing.

  • Do you need deterministic, sub-millisecond latency? → Consider another runtime.

You have finished the introduction
Next, get a working setup: head to [Installing Node.js](/nodejs/install-nodejs) and run your first program.