Skip to content
PRS India

Node 22 LTS · NestJS · Fastify

Node.js Development

Node is the right answer for live features, streaming and many users at once. It is the wrong answer for CPU-heavy work. We build TypeScript services that know the difference, and we will tell you which one you have.

Last updated

  • 10k+ concurrent sockets per node
  • Event-loop lag alarmed in production
  • Strict TypeScript, no escape hatches
  • BullMQ jobs with dead-letter replay
Node.js Development
Why Node

Brilliant at connections. Bad at arithmetic.

Node.js does one thing extremely well: it keeps a huge number of connections open while doing almost nothing on each of them. That is exactly what a live-tracking feed, a chat tier, an online-status service or a dashboard that pushes updates needs. This is why we use Node for those, and only those. The same single event loop that makes it excellent at I/O makes it weak under heavy computing. One blocking call and every open connection waits. We design around that with firm limits, worker threads and queues, and we set an alarm for event-loop lag in production. If your system is really an order flow with a database at the end, we will tell you that Laravel is the better answer and explain why.

Talk to a backend engineer
12k
Sockets per node

WebSocket connections open at the same time on a single process, in production, on modest hardware.

18ms
p95 API latency

Fastify with schema-based serialisation, in front of PostgreSQL with a connection pool that is sized properly.

<20
Event-loop lag

Our production alarm limit. Past it, someone gets a call — before a customer notices anything.

100%
Strict TypeScript

No loose any types, and no shortcuts merged to make a deadline. Zod checks everything that comes in from outside.

How we actually build it

Node is the right tool for a small but important set of problems: systems that keep a great many connections open, systems that pass data around more than they work on it, and systems whose team already writes TypeScript everywhere else. Outside that, we will usually point you to Laravel or Go and be honest about why.

The event loop is the whole design, and it is also the whole risk

Node handles ten thousand idle WebSocket connections on a single process with ease, because an idle connection costs almost nothing. That is excellent, and it is why we use Node for live features: live tracking, chat, online status, editing together, dashboards that push updates instead of asking again and again.

The same trait is also the weak spot. One JSON parse of a 40 MB payload, one tight loop over a large list, one badly written regex meeting bad input, and the event loop stops. Every one of those ten thousand connections is now waiting on it. We treat CPU-heavy work as a firm line: it goes to a worker thread, a BullMQ job, or a different service entirely. We track event-loop lag as a key production number and set an alarm on it, because by the time users notice, you are already several minutes into a problem.

NestJS when the system will outlive its first team

Express is fine for a service that fits in one engineer's head. For anything a team will look after for years, we use NestJS: modules, dependency injection, decorators, class-validator at the boundary, and a structure so the twelfth developer puts their code where the first one would have. It is shaped like Laravel on purpose. Most Node codebases that go bad go bad because no one enforced a structure, not because they lacked clever ideas.

Where raw speed is the need and the surface is small, we use Fastify instead. It is clearly faster than Express, has schema-based checks and serialisation built in, and does not pretend to be a framework for a large team.

Types all the way to the database

TypeScript in strict mode, no exceptions and no any smuggled in to make a deadline. Drizzle or Prisma for the data layer so the schema is typed where each query is written, and Zod at every outside boundary, because a type is a promise made while building and the network does not care about your promises. Where the client is also TypeScript, tRPC removes the hand-written API contract and the kind of bug where the frontend and backend disagree about a field name.

Running it

Stateless containers behind a load balancer, one process per container, managed by tools rather than joined by hand. Redis for shared state and pub/sub between instances, because the moment there is more than one Node process, state kept in memory is a bug waiting to happen. BullMQ for background jobs with retries, backoff and a dead-letter queue. A clean shutdown that finishes running requests on SIGTERM. OpenTelemetry traces, and event-loop lag on the same dashboard as p95 latency.

The honest part

When Node is the right call — and when it is not.

  Choose Node.js Choose something else
Real-time: chat, tracking, presence Yes. This is what the event loop was built for, and it holds tens of thousands of idle connections with ease. Go, if you need a hundred thousand connections per machine and have the team to run it.
Order, payment and inventory logic Only if your team uses TypeScript everywhere and that simple setup is worth more than what you lose in the framework. Laravel. The work waits on the database, so Node gives you no speed, and you get Horizon and a Filament admin for free.
Image, video or report processing Never inside the request itself. It must go to a worker thread or a queue, or it pulls the whole service down with it. A separate worker service — Go, Python or a managed pipeline. Keep the CPU-heavy work out of your socket tier completely.
Machine learning or data science No. The libraries are not there and you will build again, badly, what already exists elsewhere. Python, as a separate service. Have Node call it through a queue. This is dull and it works.
Sharing types with a React front end Strong. tRPC or shared Zod schemas remove the kind of bug where client and server disagree about a field. Nothing else matches it here. This is the most overlooked reason to pick Node, and the one teams mention least.
Small team, complex admin needs You will build the admin panel by hand, and it will take longer than you think. Plan for it honestly. Laravel with Filament. A full back office over your data in days. This is not a close contest.
What we build

Node.js work we take on.

Real-time tiers

Live order and vehicle tracking, online status, chat, editing together. Socket.IO or plain WebSockets with Redis pub/sub across instances.

API gateways and BFFs

A backend-for-frontend that gathers a mess of inside services into one clean, typed set of rules the client can easily use.

Event-driven services

Kafka or Redis Streams consumers, BullMQ workers, handlers that are safe to repeat, and dead-letter queues with a way to replay that an operator can use.

tRPC monorepos

Type safety from the database schema all the way to the React component, with no hand-written API rules to go out of date.

Streaming and ingestion

High-volume data intake pipelines where data is moved and changed rather than heavily computed on, and slowing down under load is planned, not hoped for.

Node rescue

An Express codebase that grew past its structure. We reorganise it into NestJS modules, type it strictly, and add tracking so you can see how it runs.

Characteristics

What you get, and what you must design around.

  • Concurrency is nearly free

    An idle connection costs a file handle and a little memory. Twelve thousand of them on one process is nothing special.

  • Event-loop lag is the metric

    We set the alarm above 20ms. It is the earliest sign that something is blocking, and it fires long before p95 latency notices.

  • CPU work leaves the request path

    Worker threads or BullMQ jobs. This is a firm line in our Node codebases, checked in every code review.

  • Types reach the database

    Strict TypeScript with Drizzle or Prisma, and Zod at every outside boundary. A type is a promise; the network has not read it.

  • Stateless containers, Redis for shared state

    The moment there is a second Node process, state kept in memory is a bug waiting to happen. We do not keep any.

  • Graceful shutdown, drained

    SIGTERM finishes running requests and closes sockets cleanly. Putting a new version live should not be a small outage you have learned to ignore.

The stack

What ships with a Node build from us.

Node.js 22 LTS
TypeScript
NestJS
Fastify
tRPC

Not sure Node is the right answer? Good.

Tell us what the system really does and we will tell you honestly if Node is the tool for it. Sometimes the answer is Laravel, Go or Python. That conversation is free and it takes twenty minutes.

FAQ

The questions you were going to ask on the call.

Laravel, for most business systems — orders, payments, inventory, daily steps, reports, admin. It gets you there sooner and the code stays easy to read. Node, when the system is truly live or streaming: live tracking, chat, presence, collaborative editing, or tens of thousands of open connections. Also Node when your team uses TypeScript everywhere and a second language would cost real effort to run. Anyone who answers this question without asking what the system does is guessing.

It is very fast at I/O and quite bad at heavy computing, and mixing up the two is where teams get hurt. Ten thousand idle WebSocket connections on one process is easy, because idle connections cost almost nothing. One parse of a large payload blocks all ten thousand of them, because there is a single event loop and you just stopped it. Node is fast at exactly the thing it was designed for, and you must design your system to keep CPU-heavy work out of the request itself.

NestJS for anything a team will look after for years. It gives you modules, dependency injection and a set structure, which stops the twelfth developer from inventing a thirteenth way to organise a controller. Express for a small service that fits in one engineer's head. Fastify when raw speed matters and the API is small — it is faster than Express and has schema checks and serialisation built in.

We treat CPU-heavy work as a firm line. Image work, report making, large parses, security maths and anything with a loop that never ends go to a worker thread, a BullMQ job or a separate service — never inside a request handler. Then we track event-loop lag as a production number with an alarm on it, so a slip shows on a dashboard, not as a customer complaint. Most Node outages we are called in to review are one blocking call that nobody was measuring.

It can, and we have built exactly that. But be honest about why you are choosing it. If the reason is "our team writes TypeScript", that is a legitimate reason and we will build it well in NestJS with Drizzle and strict types. If the reason is "Node is faster", that is not true for this workload — an order flow waits on the database, and Laravel would get you there sooner with a better admin panel for free. We would rather have that argument before the build than during it.

A live service or API tier is usually 8 to 14 weeks from around ₹8,00,000. A full platform with a live layer, background jobs, connections to other tools and an admin area is 16 to 26 weeks and starts around ₹20,00,000. If you already have a backend and need a live tier added to it cleanly, that is often 4 to 6 weeks of work and we are happy to plan it that way.

Let's talk node.js development

Bring us the hard version of the problem.

A senior engineer reads every enquiry. You'll get a real answer — scope, risk and a number — within one business day.