When is Express.js the right choice, and when is it not?
Express is a deliberately small layer on top of the Node.js HTTP server: routing, a middleware chain, and request and response helpers, and very little else. That is its strength when you want a thin, predictable API - a backend for a web or mobile app, a webhook receiver, a backend-for-frontend or a Socket.io server - built by a team that already knows the pattern. Most Node middleware on npm is written for it.
The same minimalism is the cost. Express ships no ORM, no authentication, no job queue and no project layout, so each of those is a decision someone has to make and maintain. For a marketplace or delivery MVP, that plumbing is why a ready-made platform, which already has it, usually reaches market first. If you are still deciding on the runtime rather than the framework, our Node.js development service covers that choice.
Express vs Fastify vs NestJS: which one fits your project?
All three run on Node.js and TypeScript, so the choice is about structure and overhead, not language. Fastify validates and serializes with JSON Schema and keeps plugins encapsulated, which lowers the framework cost per request - worth it for high-throughput JSON APIs where that cost is a measurable share of each response. NestJS adds modules, dependency injection, decorators and guards; it uses Express as its default HTTP adapter and can switch to Fastify, and its enforced structure pays off once several teams share one codebase.
Express sits between them: fewer rules than NestJS, the widest middleware ecosystem, and the largest pool of developers who can read it on day one. We recommend Express for small and mid-sized APIs, NestJS for large multi-team backends, and Fastify where throughput has been measured and matters - and we say which in writing before the quote.
What changed in Express 5, and should you upgrade an Express 4 app?
Express 5 has been the current major version since late 2024 and needs Node.js 18 or newer. The change that matters most: an error thrown or a promise rejected inside an async handler or middleware now reaches your error-handling middleware automatically. On Express 4 the same error escapes Express and the request hangs or the process stops, which is why Express 4 codebases wrap every handler.
Express 4 is still supported, so the upgrade is not an emergency. We run the official codemods, fix the routes the new matcher rejects, and let the tests prove nothing else moved. Other changes:
- Route patterns: wildcards must be named (/*splat), optional segments use braces, and regex characters in string paths are rejected
- Removed APIs such as app.del(), req.param() and res.sendfile()
- req.body stays undefined until a body parser runs, and req.query can no longer be reassigned
- express.urlencoded() now defaults to the simple query parser
How should a large Express codebase be structured?
Express will not stop a codebase from growing into one enormous app.js, so the structure has to be agreed and enforced. We organize by feature, not by file type: each module - orders, payments, users - owns its router, controllers, services, Zod schemas and repository, and the app only mounts routers. Services never see req or res, which keeps business logic testable without HTTP and portable if you later move to NestJS or Fastify.
Cross-cutting concerns are middleware in a fixed order: request ID and logging, helmet, CORS, body parsing with a size limit, rate limiting, authentication, then the routers, then one error handler last. Configuration is parsed and validated once at boot, so a missing environment variable stops the deploy instead of failing the first real request.
How does TypeScript work with Express?
Express itself is written in JavaScript; its types come from the @types/express package, which has a major version matching Express 5. TypeScript checks your code at build time, but the types vanish at runtime, and req.body arrives as whatever the client sent. So we define each body, query and params shape as a Zod schema, parse it in a validation middleware, and infer the TypeScript type from that same schema - one definition, enforced at both stages.
Custom properties such as req.user are added through declaration merging on the Express Request interface instead of casting to any, as the middleware sample further down this page shows. With a TypeScript front end, the schemas live in a shared package, so the API and the client change together and a renamed field fails the build rather than production.
How do you secure an Express.js API?
Express applies almost no security defaults of its own, so each protection is something we add and configure. Around the middleware: parameterized queries through Prisma or pg, secrets in environment config, npm audit in CI, and an error handler that logs stack traces but never returns them. We build to the risks the OWASP API Security Top 10 describes; an independent penetration test remains your decision.
The middleware every Miracuves Express.js API ships with:
- helmet for security headers, which also removes the X-Powered-By header
- express-rate-limit on login and password-reset routes, with a Redis store across processes
- A body size limit on express.json(), and Zod validation of every input before it reaches a query
- CORS restricted to your own front-end origins, never a wildcard on authenticated routes
- trust proxy set correctly behind a load balancer, so rate limits and logs see the real client IP
Should you hire Express.js developers or an Express.js development company?
Hiring Express developers directly works when you already have a backend lead who sets the structure, reviews every pull request and owns deployments. Because Express decides so little for you, two contractors without that lead will solve validation, errors and auth in two different ways, and the codebase shows it within months.
With Miracuves you work with the company, not individual freelancers: a Node.js engineer, DevOps, QA and a project manager build to a written scope and route map, with weekly demos and 100% source code ownership at handoff. Custom Express.js backend work starts from $3,699, depending on the work. A custom Express.js MVP typically runs $8,000-$25,000 over 2-8 weeks, and the Ongoing Express.js Development retainer from $2,299/month keeps the same team on new endpoints, upgrades and maintenance.