Have you ever been in this situation: your Node.js server is getting slower and slower, users are complaining about page lag, but you don’t want to undertake a massive rewrite of your entire project?
Don’t panic! Today I’m sharing a “lazy but effective” optimization approach—middleware refactoring. Using this method, my server performance improved by 40%, and the code became cleaner too.
What is Middleware? Let’s Get This Concept Clear First
Simply put, middleware is like a restaurant server. You order a dish (make a request), the kitchen prepares it (processing logic), but before the dish reaches your table, the server has to do many things:
- Check if the dish is complete (data validation)
- Verify your identity (user authentication)
- Record this service (logging)
- Handle special requests (error handling)
In Node.js, middleware are these “servers,” handling various tasks between requests and final responses.
If you’ve used Express.js, you’re definitely already using middleware—you just might not have realized it.
What’s the Problem? The Middleware Mistake Most People Make
My previous code looked like this:
// Every route has to run a bunch of middleware
app.get('/users', logRequest, authCheck, loadUserData, getUsers);
app.get('/products', logRequest, authCheck, loadUserData, fetchProducts);
app.get('/public/info', logRequest, authCheck, loadUserData, getPublicInfo);
See the problem? It’s like making every customer go through VIP lounge security, even if they’re just going to the regular dining hall for fast food.
The results of doing this:
- Redundant execution: The same middleware runs on every route
- Unnecessary overhead: Public pages also have to load user data
- Debugging nightmare: When something breaks, you have no idea which step got stuck
The Solution: Layered Middleware Architecture
I reorganized middleware into a four-layer structure, just like airport security:
Global Middleware: Basic checks everyone has to pass (request parsing, logging) Route Group Middleware: Area-specific checks (like extra security only international flights need) Context Middleware: Situation-specific checks (business class has dedicated lanes) Handlers: Final business logic (boarding)
Here’s how it’s implemented:
// Global middleware - all requests need this
app.use(express.json());
app.use(logger);
// Public route group - simplest processing
const publicRouter = express.Router();
publicRouter.get('/info', getPublicInfo);
publicRouter.get('/docs', getDocs);
app.use('/public', publicRouter);
// Private route group - requires authentication
const privateRouter = express.Router();
privateRouter.use(authCheck); // Only this group needs authentication
privateRouter.use(loadUserData); // Only this group needs user data
privateRouter.get('/profile', getUserProfile);
privateRouter.get('/settings', getUserSettings);
app.use('/private', privateRouter);
Why Does This Make It 40% Faster?
Reduces unnecessary middleware execution: Public pages no longer need to load user data, directly saving database queries
Lowers I/O bottlenecks: Moves expensive operations (like database queries) to when they’re actually needed
Better cache utilization: Similar request types can share cache, avoiding duplicate computations
It’s like highway ETC lanes—different types of vehicles use different lanes, naturally improving overall traffic efficiency.
How Did I Measure This 40% Improvement?
Data doesn’t lie. I used these tools:
Apache Benchmark (ab): Simulating high-concurrency requests New Relic: Real-time performance monitoring
Before optimization:
- Average response time: 450ms
- Concurrent processing capacity: 200 req/s
- CPU usage: 75%
After optimization:
- Average response time: 270ms (40% improvement)
- Concurrent processing capacity: 350 req/s (75% improvement)
- CPU usage: 45% (40% reduction)
A Few Tips to Make Middleware Faster
Order matters: Put lightweight, frequently used middleware first, and heavy, rarely used middleware last
Avoid middleware bloat: If a function isn’t truly middleware (like just a utility function), don’t force it into the middleware chain
Use async wisely: Database queries and external API calls must use async/await—blocked event loops are performance killers
Smart caching: Use in-memory caches like Redis for repeated request caching
Final Thoughts
Middleware isn’t just “some functions that run before responses”—it’s actually a performance tuner.
By reorganizing into a global → route group → context → handler layered structure, my Node.js server went from struggling under high load to cruising at 40% faster speeds.
If your Node.js application is also hitting performance bottlenecks, try rethinking your middleware structure. It might just be the cheapest performance upgrade you ever make.
Have you tried similar middleware optimizations? Feel free to share your experiences in the comments—I’d love to hear your success stories (or horror stories).
Follow Dream Beast Programming WeChat official account to unlock more black technologies