Express.js: Building Web Applications
Express.js is a minimal and flexible Node.js web application framework. This article covers Express.js fundamentals.
Introduction
Express.js provides:
- Routing system
- Middleware support
- Request/response handling
- Template engines
- Static file serving
Understanding Express.js helps you:
- Build web applications
- Create REST APIs
- Handle HTTP requests
- Manage middleware
- Scale applications
Express.js Setup
Installation and Basic Server
// โ
Good: Install Express
// npm install express
// โ
Good: Create basic server
const express = require('express');
const app = express();
// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Routes
app.get('/', (req, res) => {
res.send('Hello, World!');
});
// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
// โ
Good: Project structure
// my-app/
// โโโ routes/
// โ โโโ users.js
// โ โโโ posts.js
// โโโ middleware/
// โ โโโ auth.js
// โ โโโ errorHandler.js
// โโโ controllers/
// โ โโโ userController.js
// โ โโโ postController.js
// โโโ models/
// โ โโโ User.js
// โ โโโ Post.js
// โโโ app.js
// โโโ server.js
// โโโ package.json
Routing
Basic Routing
const express = require('express');
const app = express();
// โ
Good: GET request
app.get('/', (req, res) => {
res.send('Home page');
});
// โ
Good: POST request
app.post('/users', (req, res) => {
const user = req.body;
res.status(201).json({ id: 1, ...user });
});
// โ
Good: PUT request
app.put('/users/:id', (req, res) => {
const userId = req.params.id;
const updatedUser = req.body;
res.json({ id: userId, ...updatedUser });
});
// โ
Good: DELETE request
app.delete('/users/:id', (req, res) => {
const userId = req.params.id;
res.json({ message: `User ${userId} deleted` });
});
// โ
Good: Route parameters
app.get('/users/:id', (req, res) => {
const userId = req.params.id;
res.json({ id: userId, name: 'John' });
});
// โ
Good: Query parameters
app.get('/search', (req, res) => {
const query = req.query.q;
const page = req.query.page || 1;
res.json({ query, page });
});
// โ
Good: Route groups
const userRoutes = express.Router();
userRoutes.get('/', (req, res) => {
res.json([{ id: 1, name: 'John' }]);
});
userRoutes.post('/', (req, res) => {
res.status(201).json(req.body);
});
app.use('/users', userRoutes);
Route Organization
// routes/users.js
const express = require('express');
const router = express.Router();
router.get('/', (req, res) => {
res.json([{ id: 1, name: 'John' }]);
});
router.get('/:id', (req, res) => {
res.json({ id: req.params.id, name: 'John' });
});
router.post('/', (req, res) => {
res.status(201).json(req.body);
});
router.put('/:id', (req, res) => {
res.json({ id: req.params.id, ...req.body });
});
router.delete('/:id', (req, res) => {
res.json({ message: `User ${req.params.id} deleted` });
});
module.exports = router;
// app.js
const express = require('express');
const userRoutes = require('./routes/users');
const app = express();
app.use(express.json());
app.use('/users', userRoutes);
app.listen(3000, () => {
console.log('Server running on port 3000');
});
Middleware
Understanding Middleware
const express = require('express');
const app = express();
// โ
Good: Application-level middleware
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
next();
});
// โ
Good: Middleware with conditions
app.use('/api', (req, res, next) => {
console.log('API request');
next();
});
// โ
Good: Error handling middleware
app.use((err, req, res, next) => {
console.error('Error:', err);
res.status(500).json({ error: 'Internal server error' });
});
// โ
Good: Route-specific middleware
const authenticate = (req, res, next) => {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({ error: 'Unauthorized' });
}
next();
};
app.get('/protected', authenticate, (req, res) => {
res.json({ message: 'Protected route' });
});
// โ
Good: Multiple middleware
app.get(
'/admin',
authenticate,
(req, res, next) => {
console.log('Admin route');
next();
},
(req, res) => {
res.json({ message: 'Admin panel' });
}
);
Custom Middleware
// middleware/auth.js
function authenticate(req, res, next) {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
try {
const decoded = verifyToken(token);
req.user = decoded;
next();
} catch (err) {
res.status(401).json({ error: 'Invalid token' });
}
}
module.exports = authenticate;
// middleware/errorHandler.js
function errorHandler(err, req, res, next) {
console.error('Error:', err);
const status = err.status || 500;
const message = err.message || 'Internal server error';
res.status(status).json({ error: message });
}
module.exports = errorHandler;
// app.js
const express = require('express');
const authenticate = require('./middleware/auth');
const errorHandler = require('./middleware/errorHandler');
const app = express();
app.use(express.json());
app.use(authenticate);
app.get('/protected', (req, res) => {
res.json({ user: req.user });
});
app.use(errorHandler);
app.listen(3000);
Request and Response Handling
Working with Requests and Responses
const express = require('express');
const app = express();
app.use(express.json());
// โ
Good: Access request data
app.post('/users', (req, res) => {
const { name, email } = req.body;
const userId = req.params.id;
const query = req.query.filter;
const token = req.headers.authorization;
console.log('Body:', req.body);
console.log('Params:', req.params);
console.log('Query:', req.query);
console.log('Headers:', req.headers);
res.json({ name, email });
});
// โ
Good: Send different response types
app.get('/json', (req, res) => {
res.json({ message: 'JSON response' });
});
app.get('/text', (req, res) => {
res.send('Text response');
});
app.get('/html', (req, res) => {
res.send('<h1>HTML response</h1>');
});
app.get('/file', (req, res) => {
res.download('file.pdf');
});
app.get('/redirect', (req, res) => {
res.redirect('/');
});
// โ
Good: Set status codes
app.post('/users', (req, res) => {
res.status(201).json({ id: 1, ...req.body });
});
app.get('/not-found', (req, res) => {
res.status(404).json({ error: 'Not found' });
});
app.get('/error', (req, res) => {
res.status(500).json({ error: 'Server error' });
});
// โ
Good: Set headers
app.get('/custom-header', (req, res) => {
res.set('X-Custom-Header', 'value');
res.json({ message: 'Response with custom header' });
});
Static Files and Views
Serving Static Files
const express = require('express');
const path = require('path');
const app = express();
// โ
Good: Serve static files
app.use(express.static('public'));
app.use(express.static(path.join(__dirname, 'public')));
// โ
Good: Serve static files with prefix
app.use('/static', express.static('public'));
// โ
Good: Set view engine
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
// โ
Good: Render views
app.get('/', (req, res) => {
res.render('index', { title: 'Home', user: 'John' });
});
app.listen(3000);
Error Handling
Comprehensive Error Handling
const express = require('express');
const app = express();
// โ
Good: Try-catch in routes
app.get('/users/:id', async (req, res, next) => {
try {
const user = await getUser(req.params.id);
res.json(user);
} catch (err) {
next(err);
}
});
// โ
Good: Error handling middleware
app.use((err, req, res, next) => {
console.error('Error:', err);
if (err.status === 404) {
return res.status(404).json({ error: 'Not found' });
}
if (err.status === 401) {
return res.status(401).json({ error: 'Unauthorized' });
}
res.status(500).json({ error: 'Internal server error' });
});
// โ
Good: 404 handler
app.use((req, res) => {
res.status(404).json({ error: 'Route not found' });
});
app.listen(3000);
Best Practices
-
Use environment variables:
// โ Good: Environment variables const PORT = process.env.PORT || 3000; const NODE_ENV = process.env.NODE_ENV || 'development'; // โ Bad: Hardcoded values const PORT = 3000; -
Organize routes:
// โ Good: Separate route files const userRoutes = require('./routes/users'); app.use('/users', userRoutes); // โ Bad: All routes in one file app.get('/users', ...); app.post('/users', ...); -
Use middleware properly:
// โ Good: Middleware order app.use(express.json()); app.use(authenticate); app.use(errorHandler); // โ Bad: Wrong order app.use(errorHandler); app.use(authenticate);
Summary
Express.js is powerful. Key takeaways:
- Set up Express servers
- Create routes for different HTTP methods
- Use middleware for cross-cutting concerns
- Handle requests and responses
- Serve static files
- Implement error handling
- Organize code properly
- Build scalable APIs
Related Resources
- Express.js Documentation
- Express Routing
- Express Middleware
- Express Error Handling
- Express Best Practices
Next Steps
- Learn about Routing and Middleware
- Explore Database Integration
- Study Building REST APIs
- Practice Express.js
- Build Express applications
Comments