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:
- Learn Node.js
- Compare [NestJS](/en/blog/nestjs-complete-guide/
- Deploy with [PM2](/en/blog/pm2-complete-guide/
Resources:
자주 묻는 질문 (FAQ)
Q. 이 내용을 실무에서 언제 쓰나요?
A. Complete Express.js guide for building web apps and REST APIs. Learn routing, middleware, authentication, error handling… 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.
Q. 선행으로 읽으면 좋은 글은?
A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.
Q. 더 깊이 공부하려면?
A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- [Express REST API Tutorial for Node.js | Routing](/en/blog/nodejs-express-rest-api-tutorial/
- [Express.js Complete Guide: Node.js Web Framework and REST](/en/blog/nodejs-series-04-express/
- [Koa.js Complete Guide | Next Generation Node.js Framework](/en/blog/koa-complete-guide/
이 글에서 다루는 키워드 (관련 검색어)
Express, Node.js, Backend, REST API, Web Framework, JavaScript 등으로 검색하시면 이 글이 도움이 됩니다.