The main reason is one language across the full stack. Writing the API, the workers, and the UI in TypeScript lets the same team share types, validation, and tests on both sides of the network, which removes context switching and a class of integration bugs. For AI or computation-heavy work we still reach for Python, but for most web backends Node is the pragmatic default.
← Our stack
API routes, server-side rendering, build tooling, and backend services. One language across the entire stack.
nodejs.org ↗How we use Node.js
Node.js is the runtime for everything we build in JavaScript and TypeScript. Next.js API routes, SvelteKit server endpoints, Express services, and build tooling all run on Node. We use it for server-side rendering, background jobs, webhook handlers, email sending, and PDF generation. The single-language advantage is real: our team writes TypeScript on both sides of the network boundary, shares validation logic between client and server, and uses the same testing tools everywhere. We run Node 24 LTS in production.
One language. One toolchain. No context switching.
Why Node.js
Node.js means one language for the full stack. A developer working on a React component can also write the API route that feeds it data, the webhook that processes a Stripe payment, and the email template that confirms the purchase. No context switching between languages, no separate toolchains, no impedance mismatch. The npm ecosystem provides battle-tested solutions for nearly every backend need, and the async I/O model handles concurrent requests efficiently without threading complexity.
Node.js API development for production
Most of the backends we ship run on Node.js because one language covers the full stack. A developer writing a React component can also write its API route, the webhook that processes a payment, and the job that sends the confirmation email without switching languages or toolchains. Shared validation logic and types let the compiler enforce the contract between client and server. We build APIs as Next.js route handlers and SvelteKit endpoints for app-embedded logic, and as standalone services when the backend needs its own deployment. The async I/O model handles many concurrent requests without threading complexity, and the npm ecosystem covers common infrastructure we do not need to rebuild. We run the current Node LTS release in production.
Node services beyond the request cycle
Not every backend job fits neatly into a request that answers and exits. Plenty of the work we build in Node lives outside that cycle: webhook handlers that process events from Stripe or a CRM, background workers draining a job queue, scheduled tasks, email and PDF generation, and real-time servers holding open connections. We write these as Node services and then place them where they belong. Short, stateless request-response logic stays in serverless functions on the edge, close to the user. Anything that needs to stay alive, hold a connection, or run continuously moves to a persistent container on Railway, where a long-lived Node process is not fighting cold starts or execution-time limits. The single-language advantage carries through here too: the same team, types, and tests cover the API and the workers behind it, so the backend is one coherent system rather than two disconnected halves.
How we do it
How we build a Node.js backend
-
Shared types and contracts
We define the API contract in TypeScript and share it across client and server, so the shape of every request and response is enforced by the compiler. The front end and backend cannot drift apart without the build catching it first.
-
Route handlers and services
We implement backend logic as framework route handlers where it belongs to an app, and as standalone Node services where it needs its own lifecycle. Each integration (payments, email, third-party APIs) sits behind a small, testable module rather than inline.
-
Async work and jobs
Work that should not block a response (webhooks, background jobs, scheduled tasks) is moved off the request path into queues and workers. This keeps user-facing endpoints fast and gives slow or unreliable operations room to retry safely.
-
Deploy by workload
Stateless request-response code deploys to serverless on the edge, while long-running processes go to a persistent container. Matching each service to the right runtime is what keeps the backend both fast under load and affordable at idle.
Where we use it
API Development
Server-side API routes in Next.js and SvelteKit for authentication, payment processing, and data access.
Backend Services
Express and standalone Node services for webhook processing, background jobs, and real-time communication.
Build & Tooling
Custom build scripts, code generation, development servers, and testing infrastructure. All running on Node.
Systems we pair Node.js with
TypeScript
We write Node in strict TypeScript, sharing validation and types across the client-server boundary. One language and one type system for the whole stack removes a whole category of integration bugs before they ship.
Next.js
Next.js route handlers run on Node, so backend logic lives beside the front end that calls it. For most apps this means no separate server to provision, with the API and UI shipping as one deploy.
Railway
Where our long-running Node services live: WebSocket servers, job processors, and webhook handlers that need to stay alive between requests. Railway builds them straight from the repository as persistent containers.
Vercel
Hosts our stateless Node functions on the edge, close to users. The split is simple: short request-response work runs serverless on Vercel, and persistent processes run on Railway.
Powers these services
Questions