Express.js Complete Guide | Fast Node.js Web Framework
이 글의 핵심
Express.js is the most popular web framework for Node.js. It provides a minimal, flexible, and robust foundation for building web applications and REST APIs.
Introduction
Express.js is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications. It’s the de facto standard for Node.js backends.
Why Express?
Native Node.js HTTP:
const http = require('http');
http.createServer((req, res) => {
if (req.url === '/users' && req.method === 'GET') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ users: [] }));
}
// ... manual routing for every endpoint
}).listen(3000);
With Express:
const express = require('express');
const app = express();
app.get('/users', (req, res) => {
res.json({ users: [] });
});
app.listen(3000);
Real-World Adoption
Express is the industry standard for Node.js web development:
Enterprise Usage:
- Uber: Powers backend services handling millions of ride requests with Express
- IBM: Uses Express for enterprise APIs and cloud services
- PayPal: Migrated from Java to Node.js/Express, reducing response time by 35% and development time by 50%
- Netflix: Employs Express for internal tools and microservices supporting 260M+ subscribers
- Twitter: Uses Express for internal dashboards and analytics platforms
- NASA: Built mission-critical systems with Express for spacecraft operations
Market Dominance:
- 25+ million weekly npm downloads (April 2026) - one of the most downloaded npm packages ever
- 65,000+ GitHub stars - the most popular Node.js framework
- Used in 4+ million public GitHub repositories
- 10+ years in production - first released in 2010, continuously maintained
- Powers an estimated 30-40% of all Node.js web servers globally
Why Express Leads:
- Minimal & unopinionated: You choose your architecture and tools
- Massive ecosystem: 20,000+ compatible middleware packages
- Battle-tested: Proven in production for over a decade
- Easy learning curve: Simple API that feels natural to JavaScript developers
- Performance: Handles 10,000+ requests/second on modest hardware
- Community support: Millions of StackOverflow answers and tutorials
Framework Evolution: Express has inspired modern alternatives:
- Fastify: Faster (2-3x), schema-based validation
- Koa: From Express creators, modern async/await
- Hapi: Enterprise-focused, configuration-driven
- NestJS: Built on top of Express, Angular-style architecture
Despite new frameworks, Express remains the default choice for Node.js APIs due to its simplicity, stability, and ecosystem maturity.
1. Installation
npm install express
# TypeScript
npm install express
npm install -D @types/express typescript ts-node
2. Basic Server
const express = require('express');
const app = express();
// Middleware
app.use(express.json()); // Parse JSON bodies
// Routes
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.get('/api/users', (req, res) => {
res.json({ users: ['Alice', 'Bob'] });
});
// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
3. Routing
Basic Routes
// GET request
app.get('/users', (req, res) => {
res.json({ users: [] });
});
// POST request
app.post('/users', (req, res) => {
const user = req.body;
res.status(201).json({ user });
});
// PUT request
app.put('/users/:id', (req, res) => {
const { id } = req.params;
res.json({ message: `Updated user ${id}` });
});
// DELETE request
app.delete('/users/:id', (req, res) => {
const { id } = req.params;
res.json({ message: `Deleted user ${id}` });
});
// Multiple methods
app.route('/users/:id')
.get((req, res) => { /* ... */ })
.put((req, res) => { /* ... */ })
.delete((req, res) => { /* ... */ });
Route Parameters
// URL params
app.get('/users/:id', (req, res) => {
const { id } = req.params;
res.json({ userId: id });
});
// Multiple params
app.get('/posts/:postId/comments/:commentId', (req, res) => {
const { postId, commentId } = req.params;
res.json({ postId, commentId });
});
// Optional params
app.get('/users/:id?', (req, res) => {
const { id } = req.params;
if (id) {
res.json({ user: { id } });
} else {
res.json({ users: [] });
}
});
Query Parameters
// /users?page=1&limit=10
app.get('/users', (req, res) => {
const { page = 1, limit = 10 } = req.query;
res.json({ page, limit });
});
Router
// routes/users.js
const express = require('express');
const router = express.Router();
router.get('/', (req, res) => {
res.json({ users: [] });
});
router.post('/', (req, res) => {
res.status(201).json({ user: req.body });
});
router.get('/:id', (req, res) => {
res.json({ user: { id: req.params.id } });
});
module.exports = router;
// app.js
const usersRouter = require('./routes/users');
app.use('/api/users', usersRouter);
4. Middleware
Built-in Middleware
// Parse JSON bodies
app.use(express.json());
// Parse URL-encoded bodies
app.use(express.urlencoded({ extended: true }));
// Serve static files
app.use(express.static('public'));
// Serve static files with prefix
app.use('/static', express.static('public'));
Custom Middleware
// Logger middleware
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
next(); // Pass to next middleware
});
// Timing middleware
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
console.log(`${req.method} ${req.url} - ${duration}ms`);
});
next();
});
// Authentication middleware
const authenticate = (req, res, next) => {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
// Verify token
try {
req.user = verifyToken(token);
next();
} catch (error) {
res.status(401).json({ error: 'Invalid token' });
}
};
// Use authentication
app.get('/api/profile', authenticate, (req, res) => {
res.json({ user: req.user });
});
Third-Party Middleware
npm install cors helmet morgan compression cookie-parser
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const compression = require('compression');
const cookieParser = require('cookie-parser');
// Security headers
app.use(helmet());
// CORS
app.use(cors({
origin: 'https://example.com',
credentials: true,
}));
// Logging
app.use(morgan('combined'));
// Compression
app.use(compression());
// Cookie parser
app.use(cookieParser());
// Body parsers
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
5. Error Handling
// 404 handler (must be after all routes)
app.use((req, res) => {
res.status(404).json({ error: 'Not found' });
});
// Error handler (must be last)
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(err.status || 500).json({
error: {
message: err.message,
...(process.env.NODE_ENV === 'development' && { stack: err.stack }),
},
});
});
// Async error handling
const asyncHandler = (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
app.get('/users/:id', asyncHandler(async (req, res) => {
const user = await User.findById(req.params.id);
if (!user) {
throw new Error('User not found');
}
res.json({ user });
}));
6. Request and Response
Request Object
app.get('/example', (req, res) => {
// URL params
const { id } = req.params;
// Query params
const { page, limit } = req.query;
// Body
const data = req.body;
// Headers
const contentType = req.get('Content-Type');
const auth = req.headers.authorization;
// Cookies
const sessionId = req.cookies.sessionId;
// IP
const ip = req.ip;
// Path
const path = req.path;
// Method
const method = req.method;
});
Response Methods
// JSON response
res.json({ message: 'Success' });
// Text response
res.send('Hello World');
// Status code
res.status(201).json({ created: true });
// Redirect
res.redirect('/login');
res.redirect(301, '/new-url');
// Download file
res.download('/files/document.pdf');
// Send file
res.sendFile('/path/to/file.html');
// Set headers
res.set('Content-Type', 'application/json');
res.set({
'Content-Type': 'application/json',
'X-Custom-Header': 'value',
});
// Cookies
res.cookie('sessionId', '123', { httpOnly: true, maxAge: 3600000 });
res.clearCookie('sessionId');
7. Authentication Example
npm install bcrypt jsonwebtoken
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
// Register
app.post('/api/register', async (req, res) => {
try {
const { email, password } = req.body;
// Hash password
const hashedPassword = await bcrypt.hash(password, 10);
// Save user (example)
const user = await db.users.create({
email,
password: hashedPassword,
});
res.status(201).json({ message: 'User created' });
} catch (error) {
res.status(400).json({ error: error.message });
}
});
// Login
app.post('/api/login', async (req, res) => {
try {
const { email, password } = req.body;
// Find user
const user = await db.users.findOne({ email });
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Verify password
const valid = await bcrypt.compare(password, user.password);
if (!valid) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Generate token
const token = jwt.sign(
{ userId: user.id, email: user.email },
JWT_SECRET,
{ expiresIn: '7d' }
);
res.json({ token });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Authentication middleware
const authenticate = (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader) {
return res.status(401).json({ error: 'No token provided' });
}
const token = authHeader.split(' ')[1]; // Bearer <token>
try {
const decoded = jwt.verify(token, JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
res.status(401).json({ error: 'Invalid token' });
}
};
// Protected route
app.get('/api/profile', authenticate, async (req, res) => {
const user = await db.users.findById(req.user.userId);
res.json({ user });
});
8. File Upload
npm install multer
const multer = require('multer');
const path = require('path');
// Configure storage
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'uploads/');
},
filename: (req, file, cb) => {
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
cb(null, uniqueSuffix + path.extname(file.originalname));
},
});
const upload = multer({
storage,
limits: { fileSize: 5 * 1024 * 1024 }, // 5MB
fileFilter: (req, file, cb) => {
const allowedTypes = /jpeg|jpg|png|gif/;
const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase());
const mimetype = allowedTypes.test(file.mimetype);
if (extname && mimetype) {
cb(null, true);
} else {
cb(new Error('Only images allowed'));
}
},
});
// Single file
app.post('/api/upload', upload.single('image'), (req, res) => {
res.json({ file: req.file });
});
// Multiple files
app.post('/api/upload-multiple', upload.array('images', 10), (req, res) => {
res.json({ files: req.files });
});
9. TypeScript Integration
// src/app.ts
import express, { Request, Response, NextFunction } from 'express';
const app = express();
interface User {
id: string;
email: string;
}
// Extend Request type
declare global {
namespace Express {
interface Request {
user?: User;
}
}
}
app.get('/users/:id', async (req: Request, res: Response) => {
const { id } = req.params;
const user = await findUser(id);
res.json({ user });
});
// Typed middleware
const authenticate = (req: Request, res: Response, next: NextFunction) => {
// Authenticate and set req.user
next();
};
app.get('/profile', authenticate, (req: Request, res: Response) => {
res.json({ user: req.user });
});
app.listen(3000);
10. Validation
npm install express-validator
const { body, validationResult } = require('express-validator');
app.post(
'/api/users',
[
body('email').isEmail().normalizeEmail(),
body('password').isLength({ min: 8 }),
body('age').isInt({ min: 0, max: 120 }),
],
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// Process valid data
res.json({ message: 'User created' });
}
);
11. Best Practices
1. Environment Variables
require('dotenv').config();
const PORT = process.env.PORT || 3000;
const DB_URL = process.env.DATABASE_URL;
const JWT_SECRET = process.env.JWT_SECRET;
2. Structured Folder Layout
src/
├── controllers/
│ └── userController.js
├── routes/
│ └── users.js
├── middleware/
│ ├── auth.js
│ └── errorHandler.js
├── models/
│ └── User.js
├── config/
│ └── database.js
└── app.js
3. Error Handling
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';
Error.captureStackTrace(this, this.constructor);
}
}
const errorHandler = (err, req, res, next) => {
err.statusCode = err.statusCode || 500;
err.status = err.status || 'error';
res.status(err.statusCode).json({
status: err.status,
message: err.message,
});
};
app.use(errorHandler);
4. Rate Limiting
npm install express-rate-limit
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per window
message: 'Too many requests',
});
app.use('/api/', limiter);
12. Production Setup
const express = require('express');
const helmet = require('helmet');
const compression = require('compression');
const morgan = require('morgan');
const cors = require('cors');
const app = express();
// Security
app.use(helmet());
// CORS
app.use(cors({
origin: process.env.ALLOWED_ORIGINS?.split(','),
credentials: true,
}));
// Compression
app.use(compression());
// Logging
app.use(morgan('combined'));
// Body parsing
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
// Trust proxy (for Nginx, etc.)
app.set('trust proxy', 1);
// Routes
app.use('/api', apiRouter);
// Error handling
app.use(errorHandler);
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Summary
Express.js is the foundation for Node.js backends:
- Minimal and unopinionated
- Flexible routing and middleware
- Robust ecosystem
- Battle-tested in production
- TypeScript support
Key Takeaways:
- Use middleware for cross-cutting concerns
- Organize routes with Router
- Handle errors centrally
- Validate input data
- Secure with Helmet and CORS
Next Steps:
Resources:
Frequently Asked Questions (FAQ)
Q. When would I use this in practice?
A. Complete Express.js guide for building web apps and REST APIs. Learn routing, middleware, authentication, error handling…
Q. What should I read before this?
A. Follow the previous article or related articles links at the bottom of each post to learn in sequence.
Q. Where can I study this more deeply?
A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.
Related Articles (Internal Links)
Other articles related to this topic.
- Express REST API Tutorial for Node.js | Routing
- Express.js Complete Guide: Node.js Web Framework and REST
- Koa.js Complete Guide | Next Generation Node.js Framework
Keywords Covered in This Article (Related Search Terms)
This article covers Express, Node.js, Backend, REST API, Web Framework, JavaScript.