How to Build a REST API with Express.js: Architecture Decisions That Matter
By SendBridge Team · Published Jun 30, 2026 · 6 min read · Technology
You can write and launch a basic HTTP server with Express.js in just five minutes and ten lines of code. It's precisely this ease that makes developers worldwide adore this framework. However, there's a huge difference between a server from a training tutorial and a production-ready API that serves hundreds of thousands of real users.
When a project starts to grow, the initial freedom of Express.js can reveal its downside. Without strict rules, a codebase can become a tangled monolith. One change can break functionality in some other places. Onboarding new developers can last for weeks. Finding the cause of a production outage becomes a detective investigation.
6 Core Principles for Designing Scalable Express.js APIs
The experience of any top express.js development company shows that the success of the backend is laid before the business logic is written. Architectural decisions play a key role in this success. In this article, we will explore the fundamental principles and patterns for designing REST APIs with Express.js in 2026, helping you create a scalable, secure, and maintainable system.
1. 3-Layer Architecture
The most common anti-pattern in Express.js is "fat controllers," where the developer places routing logic, business rules, and SQL queries directly in the app.post() handler. This makes the code untestable and non-reusable.
The correct architectural solution is a strict separation of concerns into three independent layers:
- Routing Layer: Responsible solely for receiving an HTTP request, invoking the appropriate controller, and sending a response to the client.
- Controller Layer: Acts as a manager. The controller extracts data from the request object (
req.body, req.params), passes it to the service layer, receives the result, and decides which HTTP status to return. For example, it can be 200 OK or 404 Not Found. - Service Layer: This is the heart of your application where all the business logic resides. The service layer knows nothing about HTTP requests,
req, orresobjects. It simply receives parameters, performs calculations, interacts with the database, and returns the result.
This approach allows you to easily cover business logic with unit tests and reuse the same services, such as an email sending service, across different parts of the API.
2. Feature-Based Folder Structure
Traditionally, developers group files by their technical type, placing them in separate controllers, models, and routes folders.
The modern standard is to group by functionality, using a feature-based or modular structure.
Instead of scattering user-related code throughout the project, you create a separate user folder that contains user.controller.js, user.service.js, user.routes.js, and user.model.js.
This makes your API modular. If the business decides in the future to move user-facing functionality into a separate microservice, you can simply copy one folder.
3. Global Error Handling
There's nothing worse for a client application or for security than an API that crashes with an unhandled exception. Worse, it can return to the user a stack trace containing fragments of an SQL query. That is why Express.js should implement centralized error handling from the very beginning. Here's what this looks like in practice:
- Create a custom
AppErrorclass, inherited from the standardErrorclass, adding HTTP status properties, e.g., 400 or 500. Include an indicator of whether the error is operational or a software bug. - When a problem occurs, the business logic doesn't send a response on its own. It propagates the error further by calling next
(new AppError(...)). - At the very end of the Express.js middleware chain, a single Global Error Handling Middleware is added. It intercepts all errors from the entire application, logs them for developers (for example, sending them to Sentry), and returns standardized JSON to the client with a clear description of the problem.
4. Strict Validation of Incoming Data
Rule #1 in web development tells you that you should never trust data received from the client. A lack of proper validation can lead to SQL injection, server crashes, or the storage of corrupted data in the database.
Validation shouldn't be embedded deep within business logic. The request should be checked at the point of input, before it reaches the controller. For this purpose, developers use powerful libraries such as Zod and Joi from the Node.js ecosystem.
You create a schema that strictly defines a password as a string of at least 8 characters and age as a number. This schema is included in the router as middleware. If the incoming JSON (payload) does not match the schema, the API immediately rejects the request with a 400 Bad Request status, without overloading the database with unnecessary work.
5. API Versioning from the Start of the Project
If your application is successful, it will evolve. You will change the database structure, rename fields, and modify endpoint logic. But if a client's mobile app is already using your API, you can't simply change the response format. This will break the app for users who haven't updated.
Therefore, it's important to build versioning into the design from the start of the project. URI-level routing is most commonly used. This allows you to develop a new, improved version of the API (v2) while keeping the old version (v1) functional until all clients have updated.
6. Basic Security and Rate Limiting
You need to release a REST API to production with at least basic security measures. Otherwise, it invites hackers and botnets. A good architecture always takes into account framework-level protection mechanisms:
- Helmet. One-click installation of this package protects your application against several known web vulnerabilities by automatically configuring the appropriate HTTP security headers.
- Rate Limiting. Your server should be able to protect against brute-force attacks and API abuse. Request-limiting middleware, such as
express-rate-limit, will block an IP address if it sends more than 100 requests per minute. This prevents your application from being overwhelmed by spam traffic.
Building REST APIs That Actually Last
A high-quality REST API is a well-thought-out, multi-layered system. It is resilient to load, secure, and ready for scaling. Implementing a three-tier architecture, strict validation, centralized error handling, and a proper folder structure requires a little more time at the start of a project. However, these architectural investments pay off many times after release. This saves the business budget on support and refactoring.
Entrust the design of mission-critical systems to professionals. If you need a powerful, easily scalable backend, a reliable Express.js development company will help you avoid code "teething problems". It will create a technological foundation that drives, not hinders, your business growth.