Express.js中Middleware与Controller的区别及适用场景咨询
Hey there! Let me break this down clearly since you're transitioning from raw SQL queries to an ORM in your Express API and are confused about when to use middleware vs controllers—totally get it when most resources fixate on middleware without clarifying the controller role.
Core Differences
First, let's get the basics straight:
- Middleware acts as a filter or pre-processor in the request-handling pipeline. It can modify the
req/resobjects, terminate the request early (like returning a 401 for unauthenticated users), or pass control to the next function in the chain. It’s horizontal—it applies to multiple routes or even all requests. - Controllers are dedicated to handling specific route business logic. They’re vertical—each controller method maps to a single route’s core functionality, like fetching a list of users or creating a new order. Their job is to tie your ORM models to the API response, keeping route definitions clean.
When to Use Middleware
Use middleware for cross-cutting concerns that apply to multiple routes or the entire app:
- Global/route-group utilities: Things like
express.json()to parse request bodies, CORS middleware, or logging middleware that records every request’s method, path, and response time. - Request validation & authentication: For example, a middleware that checks if a JWT token is present and valid before allowing access to protected routes, or one that verifies required query parameters exist.
- Error handling: A global error-handling middleware that catches errors thrown by controllers or other middleware, formats them into a consistent JSON response, and sends the appropriate status code.
When to Use Controllers
Controllers shine when you need to encapsulate route-specific business logic—especially now that you’re using an ORM:
- Clean route definitions: Instead of stuffing ORM queries and response logic directly into your route files, you can keep routes focused on mapping paths/methods to controller functions. For example:
// routes/users.js const userController = require('../controllers/userController'); router.get('/users', userController.getAllUsers); router.post('/users', userController.createUser); - Maintainable business logic: All code related to fetching, creating, or updating users lives in a single
userController.jsfile. If you need to modify how users are retrieved (like adding filters or pagination), you only need to update the controller method, not hunt through route definitions. - Testability: Controllers are standalone functions that are easier to unit test than inline route handlers. You can mock ORM model methods and test how the controller handles success/error cases without spinning up an entire Express server.
How They Work Together
Middleware and controllers aren’t mutually exclusive—they’re designed to work in tandem. A typical request flow might look like:
- Request hits the server
- Global middleware runs (e.g., parse JSON, log request)
- Route-specific middleware runs (e.g., authenticate user)
- Controller method executes (e.g., fetch users via ORM, send response)
- Error-handling middleware catches any errors thrown along the way
Example of this flow:
// routes/users.js const authMiddleware = require('../middleware/auth'); const userController = require('../controllers/userController'); // First authenticate, then run the controller logic router.get('/users/me', authMiddleware.protect, userController.getCurrentUser);
For Your ORM Transition
Since you’re moving to models and migrations, controllers will help you organize your new ORM-based logic. Instead of writing ORM queries directly in middleware/route handlers, wrap them in controller methods. This aligns with common Express best practices and keeps your code scalable as your API grows.
内容的提问来源于stack exchange,提问作者delavago1999

