Skip to content

Backend

Express

The minimal, unopinionated web framework that has quietly run the Node.js ecosystem for over a decade — and the discipline it demands from whoever builds on it.

Overview

Express is the de-facto-standard web framework for Node.js: a thin layer over the built-in HTTP server that gives you routing and a middleware pipeline, and almost nothing else. That minimalism is the whole point. Express does not ship an opinion about how to validate input, authenticate a request, structure your folders, talk to a database, or handle errors. It hands you a request, a response, and a way to chain functions between them, and it trusts you to assemble the rest. For a decade that trade — near-total flexibility in exchange for near-total responsibility — made it the default choice for anything from a five-line prototype to the API behind a large product.

The core abstraction is the middleware pipeline. A request enters, passes through an ordered chain of functions — each of which can read and modify the request and response, do work, and either pass control onward with next() or end the exchange — and eventually a response goes out. Routing is itself just middleware matched on method and path. Everything else you might want — body parsing, logging, sessions, CORS, rate limiting, authentication — is a middleware you add to the chain, drawn from an enormous ecosystem of packages that all speak the same simple interface. Once that model clicks, Express is remarkably easy to reason about: the behaviour of an endpoint is the sum of the middleware it passes through, in order.

We use Express where its ubiquity and stability are the deciding factors: it is boring in the best sense, the interface has barely changed in years, and every engineer who has touched Node.js knows it. But we are equally blunt about its cost. Because Express gives you no structure, an undisciplined Express codebase sprawls — validation scattered inline, error handling copy-pasted, business logic tangled into route handlers. The framework will not stop you, so the conventions have to come from the team. For a lot of greenfield work in 2026 we will recommend a more structured option instead, and we say so up front.

Best for — Focused Node.js APIs and services where a minimal, battle-tested HTTP layer and a senior team’s own conventions beat a heavier framework — not sprawling greenfield systems that would be better served by NestJS or a full-stack Next.js.

Why teams choose Express

  • A stable, universal foundation

    Express has barely changed in years and is the most widely used Node.js framework by a distance. That means a deep pool of engineers who already know it, a compatible middleware for almost any need, and a codebase that will not be broken by the framework churning underneath it. Boring and durable is a feature.

  • Total control over the request lifecycle

    Because Express imposes nothing, you decide exactly what happens to a request and in what order. When you need a service to behave in a specific, non-standard way — an unusual auth flow, a bespoke streaming response, a proxy with custom rules — there is no framework opinion to fight.

  • Minimal surface, minimal overhead

    A thin layer over Node’s HTTP server means little between your code and the runtime: fast to start, easy to profile, and simple to reason about under load. There is no hidden framework machinery to account for when something goes wrong at three in the morning.

Why businesses choose Express

  • You want a minimal, completely understood HTTP layer for a focused API or service, and you value control over convenience.
  • You are working in or extending an existing Node.js codebase where Express’s stability and ubiquity outweigh any newer framework’s features.
  • You want a foundation that will not churn underneath you — the interface has been stable for years, and that predictability has real value over a project’s life.
  • You have, or want us to provide, the senior judgement to impose the structure, validation, and error handling that Express deliberately leaves to you.

What we build with Express

The capabilities this technology is genuinely strong at — and what we most often build with it.

  • The middleware pipeline

    Express’s one big idea. A request flows through an ordered chain of functions, each able to inspect or change it, do work, and call next() or respond. We use it to express cross-cutting concerns — authentication, logging, rate limiting, request context — as composable, testable units rather than logic smeared through every handler.

  • Routing and routers

    Routing in Express is just middleware matched on method and path. We use Router instances to split a large API into mountable, self-contained modules — each resource owning its own routes and middleware — so the routing table stays legible as the surface grows rather than collapsing into one enormous file.

  • Centralised error handling

    Express recognises error-handling middleware by its four-argument signature and runs it when a handler forwards an error. We wire up a single error boundary that turns thrown errors into consistent responses, and — critically — wrap async handlers so rejected promises actually reach it, closing the framework’s most common footgun.

  • The compatible middleware ecosystem

    Because every middleware speaks the same simple interface, there is a well-worn package for almost anything: helmet for security headers, cors, express-rate-limit, compression, morgan for logging. We choose these deliberately and keep the list short — every package added to the chain is code you now depend on and must keep current.

  • Request and response extension

    Middleware can attach data to the request object as it passes — an authenticated user, a request id, a database transaction — so later handlers read it directly. Used with discipline this keeps handlers thin; used carelessly it becomes an untyped grab-bag, so we type these extensions and keep what we attach explicit.

  • Layered application structure

    The structure Express does not give you, we supply: a clear separation between routing, a service layer holding business logic, and a data-access layer, so route handlers stay thin and the logic that matters is testable in isolation. This convention is what stops an Express codebase drifting into sprawl.

Use cases

  • Focused REST and JSON APIs

    A standalone backend serving a defined set of resources to a front end or third parties — the workload Express was made for, where a thin, well-structured HTTP layer is exactly what is needed.

  • Webhook receivers and integration services

    Endpoints that receive events from Stripe, GitHub, payment providers or internal systems, verify signatures, and act on them — small, single-purpose services where Express’s minimalism is a virtue.

  • Backend-for-frontend and API gateways

    A thin Express layer that aggregates several downstream services, handles auth and rate limiting through middleware, and shapes responses for a specific client, without the weight of a full framework.

  • Extending an existing Node.js service

    Adding routes, middleware, or a new integration to a codebase already built on Express, where staying with the incumbent framework is worth far more than any greenfield alternative.

When Express is the right choice

  • Right when you want a small, well-understood HTTP layer you control completely — a focused API, a webhook receiver, a proxy, a service that does one thing — and you have the discipline to impose your own structure on it.
  • Right when you are adding an endpoint or two to an existing Node.js service, or working in a codebase that already uses Express, where its ubiquity and stability matter more than any newer framework’s features.
  • Right when middleware is genuinely the right shape for the problem — a request passing through an ordered chain of concerns is exactly how auth, logging, and rate limiting want to be expressed.
  • Wrong for a large greenfield application where you want the framework to enforce structure. Express gives you none, so a big team on a blank Express repo tends to reinvent conventions inconsistently; NestJS provides modules, dependency injection, and opinions out of the box and is usually the better call there.
  • Wrong when the product is really a full-stack web application with a UI. Bolting a React front end onto a hand-assembled Express API duplicates work that Next.js does in one framework with server rendering, routing, and data fetching already solved — reach for bare Express only when you specifically want a standalone backend.

Express: pros and cons

Strengths

  • Ubiquitous and stable: the de-facto standard for Node.js, with a huge hiring pool and a mature, compatible middleware for almost any requirement.
  • The middleware pipeline is a genuinely simple, composable mental model — an endpoint’s behaviour is just the ordered chain it passes through.
  • Unopinionated flexibility: it gets out of your way entirely, which is ideal for focused services and unusual requirements a heavier framework would resist.
  • Thin and low-overhead, sitting close to Node’s HTTP server, so it is fast to start, easy to profile, and cheap to understand end to end.

Trade-offs

  • No built-in structure or opinions. That freedom is a liability without strong conventions — undisciplined Express apps sprawl into inconsistent, hard-to-navigate codebases.
  • You must wire up validation, security, authentication, and error handling yourself from separate packages; nothing is safe by default, and it is easy to leave a gap.
  • Asynchronous error handling has sharp edges: a rejected promise or a throw inside an async handler will not reach Express’s error middleware unless you deliberately forward it, so unhandled errors are a classic Express footgun.
  • For greenfield work in 2026 it is often the wrong default: NestJS gives you structure and dependency injection, and Next.js gives you a full-stack framework — bare Express increasingly makes sense only for focused backends.

Architecture

Express gives you routing and a middleware pipeline and stops there, so the architecture is entirely ours to design — and that is where an Express project is won or lost. We impose a layered structure the framework declines to: routers that map HTTP to handlers and nothing more, a service layer that holds the actual business logic with no knowledge of HTTP, and a data-access layer that owns talking to the database. Route handlers become thin translators between the wire and the services, which keeps the logic that matters testable without spinning up a server.

The middleware chain is the other load-bearing decision. We settle early what runs globally and in what order — security headers, CORS, body parsing, request logging and request context first, then route-specific auth and validation, then the handler, then a single error-handling boundary at the very end. Getting that order right matters, because middleware executes top to bottom and a security or parsing step in the wrong place silently does nothing. We keep the chain short and explicit rather than accreting packages until nobody can say what a request actually passes through.

Performance

Express itself adds little overhead — it is a thin layer over Node’s HTTP server — so performance problems are rarely the framework’s fault. They come from the same places they always do in Node: blocking the single event loop with synchronous work, slow or unindexed database queries, chatty calls to downstream services, and unbounded payloads. We profile the event loop and the queries rather than blaming Express, and we keep handlers non-blocking, pushing genuinely CPU-heavy work off to worker threads or a separate job process.

The wins that matter are the usual Node ones applied with care: connection pooling to the database, a caching layer such as Redis in front of expensive reads, response compression, and pagination so an endpoint never tries to serialise an unbounded result set. Because Express does nothing automatically, each of these is a deliberate choice — which is a strength, since there is no hidden framework machinery quietly doing the wrong thing, and every millisecond in the request path is code we put there and can account for.

Security

Express is unopinionated about security, which means nothing is safe by default and every protection is something you must add. We treat that as a checklist, not an afterthought: helmet for sensible security headers, a strict CORS policy rather than a wildcard, rate limiting on public endpoints, and hardened cookie and session handling. Input validation sits at the edge of the pipeline — every request body, query and parameter is validated and typed before it reaches any handler — because Express will happily pass whatever the client sent straight through to your code.

The subtler risk is error handling. Because a thrown error or rejected promise in an async handler does not reach Express’s error middleware unless it is forwarded, a naive app can leak stack traces or crash on inputs an attacker controls. We wrap async handlers so every error lands in one boundary that logs the detail and returns a safe, generic response. Authentication and authorisation are enforced in middleware on the server and never merely implied by the UI, and we keep the dependency tree small and current, because every middleware in the chain is attack surface we have chosen to take on.

Scalability

An Express service scales the way any Node.js process does. A single process runs on one CPU core via the event loop, so we scale out rather than up: multiple stateless instances behind a load balancer, using the whole machine through clustering or, more commonly now, several containers. The precondition is statelessness — no session or request state held in process memory, with anything shared pushed to Redis or the database — so any instance can serve any request and instances can be added or removed freely.

The harder kind of scale is the codebase and the team, and here Express’s lack of structure is the risk. Without conventions, a growing Express app fragments as each engineer solves routing, validation and error handling their own way. We hold the line with the layered structure, mountable routers per resource, shared middleware, and TypeScript contracts throughout, so new features slot into a known shape. When a single service genuinely outgrows this, the clean separation between routing and business logic is what lets us carve a bounded piece out into its own service without unpicking the whole thing.

Express integrations & ecosystem

The technologies we most often pair with it — each links to how we work with it.

How we work

With Express the first job is to decide the things the framework will not decide for you: the layered structure, the middleware order, the validation and error-handling strategy, and the small, deliberate set of packages we will depend on. We settle these before the code volume grows, because retrofitting structure onto a sprawling Express app is far more expensive than establishing it on day one. From there we build in thin vertical slices — a real endpoint, validated, authenticated, tested and in production — rather than a scaffold that looks finished but does nothing.

The engineers who design the service are the ones who write it and keep it running, which is what we mean by operating what we build. That shapes the choices: async errors caught properly, a dependency list short enough to keep current, TypeScript on the request and response contracts, and boring, durable decisions over whatever is fashionable. And if, having heard the requirements, we think NestJS or Next.js would serve you better than bare Express, we will tell you before you commit — that judgement is part of the work.

The service behind it

Delivered throughCustom Software Development

What we build with Express

The disciplines this technology most often shows up in — from a first build to taking over and stabilising an existing one.

How we deliver

  1. 01

    Discover

    We map the system, the constraints and the business it serves — including the parts nobody documented.

    Architecture brief

  2. 02

    Architect

    Decisions get made, written down and defended before a line of production code exists.

    Decision records

  3. 03

    Build

    Short cycles against working software. You see progress in the product, not in a status deck.

    Shipping increments

  4. 04

    Operate

    Monitoring, incident response and iteration. The system is alive, so the engagement is too.

    Runbooks & SLOs

Industries we use Express in

Domain knowledge changes what gets built. A few of the sectors we know before the first meeting.

Also in Backend

Why teams choose us for Express

  • Senior engineers only

    Express rewards discipline and punishes its absence — its freedom is exactly where inexperienced teams create sprawl and security gaps. The people designing your service have imposed structure on Express before and have seen how the shortcuts age. No juniors learning on your project.

  • We operate what we build

    We run the services we ship, so we optimise for the maintenance reality, not the demo: async errors caught properly, a short dependency list we keep current, and code your team can actually pick up. Express hides nothing, and neither do we.

  • Honest about when not to use Express

    For a lot of greenfield work in 2026, NestJS or Next.js is the better choice, and we will say so before you spend money. We would rather steer you to the right framework than build you a bare Express app you will outgrow.

Typical timeline

  1. 01

    Discovery and architecture

    One to two weeks agreeing the layered structure, middleware order, validation and error-handling strategy, and the deliberate set of dependencies — plus an honest call on whether Express is even the right framework.

  2. 02

    First vertical slice

    Two to three weeks delivering one real endpoint end to end in production — validated, authenticated, error-handled and tested — proving the conventions against reality before the surface grows.

  3. 03

    Iterative build

    Resource-by-resource delivery in short cycles, each endpoint shippable, with security and error handling applied as we go rather than bolted on at the end.

  4. 04

    Hardening and handover

    Load and event-loop profiling, security review of the middleware chain, test coverage on the load-bearing paths, and documentation so your team can own and extend the service.

How pricing works

  • Fixed-scope builds for well-defined services — an API, a webhook receiver, a backend-for-frontend — quoted once the endpoints, integrations and data model are clear.
  • Monthly senior engagement for ongoing backend work, where scope evolves and you want continuity and someone who keeps the dependency tree and error handling honest over time.
  • Focused audits and rescues for existing Express codebases — structure, security gaps, async error handling, or a service that has sprawled — priced by the assessment.

Hire Express engineers

Need Express capacity on your own team? We embed named senior engineers into your existing team — reporting to your leads, working in your rituals — so you add capacity without a hiring cycle.

Hire Express engineers

Common questions

Is Express still a good choice in 2026?

For the right job, yes — and for others, no. Its ubiquity and stability make it a safe default for a focused API, a webhook receiver, or extending an existing Node.js service. But for a large greenfield application we usually prefer NestJS for its structure, and for a full-stack web product Next.js. Express is a good choice specifically when you want a minimal HTTP layer you control and have the discipline to structure it yourself.

What is the difference between Express and NestJS?

Express is minimal and unopinionated: routing, a middleware pipeline, and nothing else — you supply all the structure. NestJS is built on top of Express (or Fastify) and adds an opinionated architecture: modules, dependency injection, and clear conventions out of the box. For a small focused service Express’s simplicity wins; for a large application with a big team, NestJS’s structure usually pays for itself by keeping everyone consistent.

Should we use Express or Next.js for a full-stack app?

If the product has a UI, Next.js is usually the better fit — it solves routing, server rendering, and data fetching for both the front end and the back end in one framework, so you are not hand-assembling an Express API and a separate React app. Reach for bare Express when you specifically want a standalone backend — an API consumed by other services, a mobile app, or third parties — rather than a web application with pages.

Why does Express need so many extra packages?

By design. Express deliberately ships almost nothing beyond routing and middleware, so validation, security headers, CORS, rate limiting and logging each come from a separate compatible package you add to the chain. That is the trade for its flexibility. We keep the list short and deliberate, because every package is code you now depend on and must keep patched — the goal is the minimum that does the job, not the maximum the ecosystem offers.

What is the most common way Express applications go wrong?

Two things. First, sprawl: because Express imposes no structure, validation, error handling and business logic drift into route handlers until the codebase is inconsistent and hard to change. Second, async error handling — a rejected promise in an async handler does not reach Express’s error middleware unless you forward it, so errors get swallowed or crash the process. We fix both with a layered structure and a wrapper that routes every async error to one boundary.

Building on Express?

A technical conversation with the engineers who would do the work. If we are not the right fit, we will say so on the call.

Two fields required. We reply to real enquiries — no list, no sequence.