Express.js Secrets That Senior Developers Don’t Share
Express.js is often the go-to framework for building web applications in Node.js. It’s lightweight, unopinionated, and incredibly flexible, making it a favorite among both beginners and experienced developers. However, there’s a gap between writing basic Express apps and building scalable, maintainable, and high-performance applications.
Senior developers, especially those who’ve spent years in production environments, have hard-earned secrets that make their Express applications faster, safer, and easier to maintain. These insights rarely make it into tutorials or official documentation.
1. Using Middleware Wisely: The Hidden Performance Cost
Middleware is at the heart of Express. However, misusing middleware can slow down your app and cause bottlenecks. Here’s what most people get wrong:
Common Mistake: Using Too Many Global Middleware
Many developers add middleware at the top level, affecting every single request, even if it’s not needed:
app.use((req, res, next) => {
console.log('Request received');
next();
});
app.use(someHeavyMiddleware);
app.get('/simple-route', (req, res) => {
res.send('Hello, world!');
});
Here, even a simple route like /simple-route
has to go through someHeavyMiddleware
, slowing things down.
The Senior Dev Fix: Apply Middleware Selectively
Instead, attach middleware only where necessary:
app.get('/simple-route', (req, res) => {
res.send('Hello, world!');
});
app.use('/heavy-api', someHeavyMiddleware);
app.get('/heavy-api/data', (req, res) => {
res.send('This route actually needs heavy middleware');
});
Pro Tip: Use router.use()
Instead of app.use()
Instead of attaching middleware globally, bind it only to relevant routers:
const userRouter = express.Router();
userRouter.use(authMiddleware);
userRouter.get('/profile', (req, res) => {
res.send('User profile');
});
app.use('/user', userRouter);
Now, authMiddleware
only affects /user
routes.
2. Secret Sauce: Async Error Handling Like a Pro
Handling errors in async functions can get messy and repetitive. Most developers write error-prone code like this:
app.get('/data', async (req, res) => {
try {
let data = await fetchData();
res.send(data);
} catch (err) {
res.status(500).send('Something went wrong');
}
});
What’s wrong? Every route needs the same try/catch
pattern, leading to bloated code.
The Senior Dev Fix: Use a Universal Async Error Handler
Wrap your async routes with a helper function:
const asyncHandler = fn => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
app.get('/data', asyncHandler(async (req, res) => {
let data = await fetchData();
res.send(data);
}));
Now, errors are automatically passed to the Express error handler, keeping your code clean and DRY.
3. Mastering Express Router: The Scalability Trick
If your server.js
file has 100+ routes, you’re doing it wrong. A senior developer knows that breaking routes into modular files is crucial.
The Senior Dev Fix: Organize Your Routes Properly
- Create a
/routes
directory - Move each feature into its own file
For example, instead of dumping everything into server.js
, create a clean structure:
/routes
├── users.js
├── products.js
├── orders.js
Then in users.js
:
const express = require('express');
const router = express.Router();
router.get('/profile', (req, res) => {
res.send('User profile page');
});
module.exports = router;
And in server.js
:
const express = require('express');
const app = express();
app.use('/users', require('./routes/users'));
app.use('/products', require('./routes/products'));
app.listen(3000, () => console.log('Server running on port 3000'));
Now, your routes are modular, scalable, and easy to maintain.
4. Using Environment Variables the Right Way
Many junior developers hardcode secrets in their code:
const dbPassword = 'supersecret123';
Big mistake! Never expose secrets in your source code.
The Senior Dev Fix: Use .env
Properly
- Install
dotenv
:
npm install dotenv
- Create a
.env
file:
DB_PASSWORD=supersecret123
- Use it in your app:
require('dotenv').config();
const dbPassword = process.env.DB_PASSWORD;
Now, your credentials are safe and easy to change.
5. The res.locals
Trick: Avoiding Unnecessary Database Calls
Many developers fetch user data in every request, slowing down performance:
app.use(async (req, res, next) => {
const user = await fetchUser(req.userId);
req.user = user;
next();
});
The Senior Dev Fix: Store Data in res.locals
Use res.locals
to store request-specific data without bloating req
:
app.use(async (req, res, next) => {
res.locals.user = await fetchUser(req.userId);
next();
});
app.get('/dashboard', (req, res) => {
res.send(`Welcome, ${res.locals.user.name}`);
});
This keeps req
lightweight and your middleware efficient.
6. The Lesser-Known next('route')
Trick
Sometimes, you want to skip the current middleware and jump to the next route handler. Most developers don’t realize next('route')
exists!
The Senior Dev Fix: Conditional Route Skipping
app.get('/admin', checkAuth, (req, res, next) => {
if (!req.user.isAdmin) return next('route'); // Skip this handler
res.send('Welcome, Admin!');
});
app.get('/admin', (req, res) => {
res.send('You are not an admin');
});
Now, non-admin users skip the first handler and get a friendly message instead.
Final Thoughts: Write Express Like a Senior Developer
Express is simple to learn, but hard to master. By applying these hidden techniques, you’ll write applications that are:
→ Faster — By reducing unnecessary middleware and optimizing route handling
→ Cleaner — Using modular routing and async error handling
→ More secure — By using .env
and avoiding secret leaks
→ More maintainable – By keeping request-specific data lightweight
The difference between a junior and a senior developer is not knowledge of the framework, but how they use it wisely.
You may also like:
1) 5 Common Mistakes in Backend Optimization
2) 7 Tips for Boosting Your API Performance
3) How to Identify Bottlenecks in Your Backend
4) 8 Tools for Developing Scalable Backend Solutions
5) 5 Key Components of a Scalable Backend System
6) 6 Common Mistakes in Backend Architecture Design
7) 7 Essential Tips for Scalable Backend Architecture
8) Token-Based Authentication: Choosing Between JWT and Paseto for Modern Applications
9) API Rate Limiting and Abuse Prevention Strategies in Node.js for High-Traffic APIs
10) Can You Answer This Senior-Level JavaScript Promise Interview Question?
11) 5 Reasons JWT May Not Be the Best Choice
12) 7 Productivity Hacks I Stole From a Principal Software Engineer
13) 7 Common Mistakes in package.json Configuration
Read more blogs from Here
Share your experiences in the comments, and let’s discuss how to tackle them!
Follow me on Linkedin