Bắt Đầu Với Express.js

Chào mừng bạn đến với Lộ Trình Học Express.js! Series này sẽ hướng dẫn bạn từ những kiến thức cơ bản của Express.js đến việc xây dựng các REST APIs và ứng dụng web production-ready với Node.js.
Trong bài đầu tiên này, bạn sẽ học các khái niệm nền tảng của Express.js, thiết lập môi trường phát triển, tạo server đầu tiên, và xây dựng một REST API đơn giản. Khi kết thúc, bạn sẽ hiểu cách Express hoạt động và sẵn sàng cho các chủ đề nâng cao hơn.
Bạn Sẽ Học Được Gì
Kết Quả Học Tập:
✅ Hiểu Express.js là gì và tại sao nó là Node.js framework phổ biến nhất
✅ Thiết lập môi trường phát triển Node.js từ đầu
✅ Tạo server Express đầu tiên và hiểu cách nó hoạt động
✅ Xử lý HTTP requests (GET, POST, PUT, DELETE)
✅ Hiểu khái niệm middleware và request-response cycle
✅ Xây dựng REST API đơn giản với CRUD operations
Express.js Là Gì?
Express.js (hay đơn giản là "Express") là một web application framework tối giản, linh hoạt và nhanh cho Node.js. Đây là Node.js framework phổ biến nhất với hơn 60 triệu lượt tải về mỗi tuần trên npm.
Tại Sao Chọn Express?
Express được ưa chuộng vì:
- Tối giản và Không Áp đặt - Cho bạn tự do cấu trúc ứng dụng theo ý muốn
- Nhanh và Nhẹ - Overhead tối thiểu, hiệu suất xuất sắc
- Hệ sinh thái Trưởng thành - Hàng nghìn middleware packages có sẵn
- Dễ học - API đơn giản, trực quan, thân thiện với người mới
- Sẵn sàng cho Production - Được sử dụng bởi các công ty như Uber, IBM và Accenture
Express vs Các Node.js Frameworks Khác
| Tính năng | Express | Fastify | NestJS | Koa |
|---|---|---|---|---|
| Triết lý | Tối giản | Hiệu suất | Có quan điểm | Tối giản |
| Độ khó học | Dễ | Dễ | Trung bình | Dễ |
| Hiệu suất | Tốt | Xuất sắc | Tốt | Tốt |
| TypeScript | Tùy chọn | Tích hợp sẵn | Tích hợp sẵn | Tùy chọn |
| Use Case | Đa mục đích | Hiệu suất cao | Ứng dụng doanh nghiệp | Devs có kinh nghiệm |
Khi nào nên dùng Express:
- Xây dựng REST APIs hoặc web applications
- Cần tính linh hoạt trong kiến trúc
- Muốn hệ sinh thái middleware lớn
- Ưu tiên sự đơn giản và dễ học
- Làm việc với team nhỏ đến trung bình
Khi nào KHÔNG nên dùng Express:
- Cần hiệu suất tối đa (xem xét Fastify)
- Muốn TypeScript và kiến trúc nghiêm ngặt (xem xét NestJS)
- Xây dựng ứng dụng real-time chủ yếu (xem xét Socket.IO native)
Thiết Lập Môi Trường Phát Triển
Yêu Cầu
Trước khi bắt đầu, đảm bảo bạn có:
- Node.js 18+ đã cài đặt (kiểm tra với
node --version) - npm hoặc yarn package manager
- Code editor (khuyên dùng VS Code)
- Kiến thức JavaScript cơ bản (ES6+)
Cài Đặt Node.js
Nếu bạn chưa có Node.js, tải về từ nodejs.org hoặc dùng version manager:
Sử dụng nvm (khuyên dùng):
# Cài đặt nvm (macOS/Linux)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
# Cài đặt Node.js LTS mới nhất
nvm install --lts
nvm use --lts
# Xác minh cài đặt
node --version # Nên hiển thị v20.x.x hoặc cao hơn
npm --version # Nên hiển thị v10.x.x hoặc cao hơnSử dụng Homebrew (macOS):
brew install nodeTạo Project Đầu Tiên
Hãy tạo một Express project mới từ đầu.
Bước 1: Khởi tạo project
# Tạo thư mục project
mkdir express-todo-api
cd express-todo-api
# Khởi tạo npm project
npm init -yLệnh này tạo file package.json với các thiết lập mặc định.
Bước 2: Cài đặt Express
npm install expressBước 3: Cài đặt development dependencies
npm install --save-dev nodemonnodemon tự động khởi động lại server khi files thay đổi - rất cần thiết cho development.
Bước 4: Cập nhật package.json scripts
Sửa package.json và thêm các scripts sau:
{
"name": "express-todo-api",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js"
},
"dependencies": {
"express": "^4.18.2"
},
"devDependencies": {
"nodemon": "^3.0.1"
}
}Cấu Trúc Project
Đây là cấu trúc khuyên dùng cho project đơn giản của chúng ta:
express-todo-api/
├── index.js # File ứng dụng chính
├── package.json # Dependencies và scripts
├── package-lock.json # Phiên bản dependencies đã lock
└── node_modules/ # Các packages đã cài đặtVới các project lớn hơn, bạn sẽ tổ chức code vào các folders như routes/, controllers/, middleware/, v.v. Chúng ta sẽ đề cập trong các bài sau.
Server Express Đầu Tiên
Hãy tạo một server "Hello World" tối giản để hiểu các khái niệm cơ bản.
Tạo index.js:
// Import Express
const express = require('express');
// Tạo Express application
const app = express();
// Định nghĩa port
const PORT = 3000;
// Tạo một route
app.get('/', (req, res) => {
res.send('Hello World!');
});
// Khởi động server
app.listen(PORT, () => {
console.log(`Server đang chạy tại http://localhost:${PORT}`);
});Chạy server:
npm run devTruy cập http://localhost:3000 trong trình duyệt - bạn sẽ thấy "Hello World!".
Hiểu Code
Hãy phân tích từng phần làm gì:
const express = require('express');Import Express module.
const app = express();Tạo một Express application instance. Object app này có các methods để định tuyến HTTP requests, cấu hình middleware, render HTML, v.v.
app.get('/', (req, res) => {
res.send('Hello World!');
});Định nghĩa một route handler cho GET requests đến root path (/). Callback function nhận:
req(request) - Chứa dữ liệu về incoming requestres(response) - Methods để gửi responses trả về client
app.listen(PORT, () => { ... });Bind Express app vào port 3000 và bắt đầu lắng nghe connections.
Hiểu Routes và HTTP Methods
Routes xác định cách ứng dụng phản hồi với client requests tại các endpoints cụ thể (URLs).
Cú Pháp Route
app.METHOD(PATH, HANDLER)- METHOD - HTTP method (get, post, put, delete, etc.)
- PATH - URL path
- HANDLER - Function thực thi khi route được match
HTTP Methods và CRUD
Express hỗ trợ tất cả HTTP methods, nhưng 4 methods này map với CRUD operations:
| HTTP Method | CRUD Operation | Mục đích |
|---|---|---|
| GET | Read | Lấy dữ liệu |
| POST | Create | Tạo dữ liệu mới |
| PUT | Update | Cập nhật dữ liệu có sẵn |
| DELETE | Delete | Xóa dữ liệu |
Ví Dụ Route Cơ Bản
// GET request - lấy dữ liệu
app.get('/users', (req, res) => {
res.send('Lấy tất cả users');
});
// POST request - tạo dữ liệu
app.post('/users', (req, res) => {
res.send('Tạo một user');
});
// PUT request - cập nhật dữ liệu
app.put('/users/:id', (req, res) => {
res.send(`Cập nhật user ${req.params.id}`);
});
// DELETE request - xóa dữ liệu
app.delete('/users/:id', (req, res) => {
res.send(`Xóa user ${req.params.id}`);
});Route Parameters
Sử dụng : để định nghĩa route parameters:
app.get('/users/:userId', (req, res) => {
const userId = req.params.userId;
res.send(`User ID: ${userId}`);
});
// Truy cập với: GET /users/123
// req.params.userId sẽ là "123"Nhiều parameters:
app.get('/users/:userId/posts/:postId', (req, res) => {
const { userId, postId } = req.params;
res.json({ userId, postId });
});
// Truy cập với: GET /users/5/posts/42
// req.params = { userId: "5", postId: "42" }Query Strings
Query strings truyền dữ liệu bổ sung trong URL:
app.get('/search', (req, res) => {
const { q, page, limit } = req.query;
res.json({ query: q, page, limit });
});
// Truy cập với: GET /search?q=express&page=1&limit=10
// req.query = { q: "express", page: "1", limit: "10" }Giới Thiệu Middleware
Middleware functions là khái niệm cốt lõi của Express. Chúng là các functions có quyền truy cập vào request object (req), response object (res), và middleware function tiếp theo trong request-response cycle.
Request-Response Cycle
Mỗi middleware có thể:
- Thực thi bất kỳ code nào
- Sửa đổi request và response objects
- Kết thúc request-response cycle
- Gọi middleware tiếp theo trong stack
Middleware Signature
function middleware(req, res, next) {
// Làm gì đó
next(); // Chuyển control sang middleware tiếp theo
}Function next chuyển control sang middleware tiếp theo. Nếu bạn không gọi next(), request sẽ bị treo.
Built-in Middleware
Express có built-in middleware cho các tác vụ phổ biến:
1. express.json() - Parse JSON request bodies
// Thêm trước routes
app.use(express.json());
app.post('/users', (req, res) => {
// Bây giờ req.body chứa parsed JSON
const { name, email } = req.body;
res.json({ message: 'User đã tạo', user: { name, email } });
});2. express.urlencoded() - Parse URL-encoded bodies
app.use(express.urlencoded({ extended: true }));3. express.static() - Serve static files
// Serve files từ thư mục "public"
app.use(express.static('public'));
// Bây giờ files trong public/ có thể truy cập:
// public/style.css -> http://localhost:3000/style.cssApplication-level Middleware
Bạn có thể tạo custom middleware để chạy trên tất cả requests:
// Logging middleware
app.use((req, res, next) => {
console.log(`${req.method} ${req.url} - ${new Date().toISOString()}`);
next();
});
// Bây giờ tất cả requests sẽ được logThứ Tự Thực Thi Quan Trọng
Middleware thực thi theo thứ tự bạn định nghĩa:
const express = require('express');
const app = express();
// 1. Chạy đầu tiên
app.use((req, res, next) => {
console.log('First');
next();
});
// 2. Chạy thứ hai (parsing JSON)
app.use(express.json());
// 3. Chạy thứ ba
app.use((req, res, next) => {
console.log('Third');
next();
});
// 4. Route handler chạy cuối
app.get('/', (req, res) => {
res.send('Hello');
});Quan trọng: Đặt middleware nên chạy trên TẤT CẢ requests trước các routes.
Hands-on Project: Todo REST API
Hãy xây dựng một REST API đơn giản để quản lý todos. Chúng ta sẽ dùng in-memory array làm "database" (sẽ đề cập database thật trong các bài sau).
Code Hoàn Chỉnh
Thay thế index.js với code này:
const express = require('express');
const app = express();
const PORT = 3000;
// Middleware
app.use(express.json());
// Logging middleware
app.use((req, res, next) => {
console.log(`${req.method} ${req.url} - ${new Date().toISOString()}`);
next();
});
// In-memory database
let todos = [
{ id: 1, title: 'Học Express.js', completed: false },
{ id: 2, title: 'Xây dựng REST API', completed: false },
];
// GET /api/todos - Lấy tất cả todos
app.get('/api/todos', (req, res) => {
res.json({
success: true,
data: todos,
});
});
// GET /api/todos/:id - Lấy một todo
app.get('/api/todos/:id', (req, res) => {
const id = parseInt(req.params.id);
const todo = todos.find(t => t.id === id);
if (!todo) {
return res.status(404).json({
success: false,
message: 'Không tìm thấy todo',
});
}
res.json({
success: true,
data: todo,
});
});
// POST /api/todos - Tạo todo mới
app.post('/api/todos', (req, res) => {
const { title } = req.body;
if (!title) {
return res.status(400).json({
success: false,
message: 'Title là bắt buộc',
});
}
const newTodo = {
id: todos.length + 1,
title,
completed: false,
};
todos.push(newTodo);
res.status(201).json({
success: true,
data: newTodo,
});
});
// PUT /api/todos/:id - Cập nhật todo
app.put('/api/todos/:id', (req, res) => {
const id = parseInt(req.params.id);
const { title, completed } = req.body;
const todoIndex = todos.findIndex(t => t.id === id);
if (todoIndex === -1) {
return res.status(404).json({
success: false,
message: 'Không tìm thấy todo',
});
}
// Cập nhật todo
if (title !== undefined) todos[todoIndex].title = title;
if (completed !== undefined) todos[todoIndex].completed = completed;
res.json({
success: true,
data: todos[todoIndex],
});
});
// DELETE /api/todos/:id - Xóa todo
app.delete('/api/todos/:id', (req, res) => {
const id = parseInt(req.params.id);
const todoIndex = todos.findIndex(t => t.id === id);
if (todoIndex === -1) {
return res.status(404).json({
success: false,
message: 'Không tìm thấy todo',
});
}
const deletedTodo = todos.splice(todoIndex, 1)[0];
res.json({
success: true,
data: deletedTodo,
});
});
// 404 handler cho unknown routes
app.use((req, res) => {
res.status(404).json({
success: false,
message: 'Không tìm thấy route',
});
});
// Khởi động server
app.listen(PORT, () => {
console.log(`Server đang chạy tại http://localhost:${PORT}`);
});Kiểm Thử API
Khởi động server:
npm run devTest với curl hoặc REST client:
1. Lấy tất cả todos:
curl http://localhost:3000/api/todos2. Lấy một todo:
curl http://localhost:3000/api/todos/13. Tạo todo mới:
curl -X POST http://localhost:3000/api/todos \
-H "Content-Type: application/json" \
-d '{"title":"Học middleware"}'4. Cập nhật todo:
curl -X PUT http://localhost:3000/api/todos/1 \
-H "Content-Type: application/json" \
-d '{"completed":true}'5. Xóa todo:
curl -X DELETE http://localhost:3000/api/todos/1Sử Dụng REST Client (Khuyên dùng)
Thay vì curl, dùng REST client để test dễ hơn:
- Postman - Full-featured GUI client
- Insomnia - Giao diện đơn giản, sạch sẽ
- VS Code REST Client - Test từ .http files trong VS Code
- Thunder Client - VS Code extension, nhẹ
Ví dụ file .http cho VS Code REST Client:
### Lấy tất cả todos
GET http://localhost:3000/api/todos
### Lấy một todo
GET http://localhost:3000/api/todos/1
### Tạo todo mới
POST http://localhost:3000/api/todos
Content-Type: application/json
{
"title": "Học middleware"
}
### Cập nhật todo
PUT http://localhost:3000/api/todos/1
Content-Type: application/json
{
"completed": true
}
### Xóa todo
DELETE http://localhost:3000/api/todos/1Hiểu Code
1. Cấu trúc Response:
Chúng ta dùng format response nhất quán:
{
success: true, // hoặc false cho errors
data: { ... }, // dữ liệu thực tế
message: "..." // error message tùy chọn
}2. HTTP Status Codes:
200- OK (mặc định cho GET, PUT, DELETE thành công)201- Created (cho POST thành công)400- Bad Request (lỗi client, như thiếu required field)404- Not Found (resource không tồn tại)500- Internal Server Error (sẽ xử lý sau)
3. Validation:
if (!title) {
return res.status(400).json({
success: false,
message: 'Title là bắt buộc',
});
}Luôn validate user input. Từ khóa return quan trọng - nó ngăn code tiếp tục thực thi.
4. 404 Handler:
app.use((req, res) => {
res.status(404).json({
success: false,
message: 'Không tìm thấy route',
});
});Middleware này chạy nếu không route nào match. Đặt ở CUỐI file.
Tham Chiếu Response Methods
Express cung cấp nhiều methods để gửi responses:
// Gửi JSON
res.json({ message: 'Hello' });
// Gửi plain text
res.send('Hello World');
// Gửi status code
res.sendStatus(404); // Gửi "Not Found"
// Set status và gửi
res.status(201).json({ message: 'Created' });
// Redirect
res.redirect('/new-url');
// Download file
res.download('/path/to/file.pdf');
// Gửi file
res.sendFile('/path/to/file.html');
// Set headers
res.set('Content-Type', 'application/json');
res.send('{"message":"Hello"}');Các Lỗi Thường Gặp Cần Tránh
1. Không gọi next() trong middleware
// ❌ Sai - request sẽ bị treo
app.use((req, res, next) => {
console.log('Hello');
// Quên gọi next()
});
// ✅ Đúng
app.use((req, res, next) => {
console.log('Hello');
next();
});2. Gửi nhiều responses
// ❌ Sai - lỗi: "Cannot set headers after they are sent"
app.get('/test', (req, res) => {
res.send('First');
res.send('Second'); // Lỗi!
});
// ✅ Đúng - dùng return để ngăn thực thi tiếp
app.get('/test', (req, res) => {
if (someCondition) {
return res.send('First');
}
res.send('Second');
});3. Thứ tự middleware
// ❌ Sai - routes định nghĩa trước middleware
app.get('/api/todos', (req, res) => {
// req.body sẽ là undefined
const { title } = req.body;
});
app.use(express.json()); // Quá muộn!
// ✅ Đúng - middleware trước routes
app.use(express.json());
app.get('/api/todos', (req, res) => {
const { title } = req.body; // Hoạt động!
});4. Không xử lý errors
// ❌ Sai - không xử lý error
app.get('/users/:id', (req, res) => {
const user = database.findUser(req.params.id); // Có thể throw error
res.json(user);
});
// ✅ Đúng - xử lý potential errors
app.get('/users/:id', (req, res) => {
try {
const user = database.findUser(req.params.id);
if (!user) {
return res.status(404).json({ error: 'Không tìm thấy user' });
}
res.json(user);
} catch (error) {
res.status(500).json({ error: 'Internal server error' });
}
});Tóm Tắt và Những Điểm Chính
Những Gì Đã Đề Cập:
✅ Express.js là một Node.js framework tối giản, linh hoạt để xây dựng web applications và APIs
✅ Thiết lập Node.js project với npm init, cài Express, và dùng nodemon cho auto-reload
✅ Tạo routes với app.get(), app.post(), app.put(), app.delete()
✅ Hiểu middleware - functions xử lý requests trước khi đến route handlers
✅ Built-in middleware: express.json() cho parsing JSON, express.static() cho static files
✅ Xây dựng CRUD REST API hoàn chỉnh để quản lý todos với status codes và validation đúng cách
Các Khái Niệm Chính:
- Routes map URLs và HTTP methods tới handler functions
- Middleware functions thực thi theo thứ tự và có thể sửa
req/reshoặc gọinext() - Request object (
req) chứa dữ liệu về incoming request (params, query, body) - Response object (
res) có methods để gửi responses (json, send, status, etc.) - Status codes truyền đạt kết quả request (200, 201, 400, 404, 500)
Best Practices:
- Luôn gọi
next()trong middleware trừ khi gửi response - Dùng
returnkhi gửi responses để tránh multiple responses - Đặt middleware trước routes
- Validate user input
- Dùng response format nhất quán
- Dùng HTTP status codes phù hợp
Bài Tập Thực Hành
Thử các bài tập này để củng cố kiến thức:
-
Mở rộng Todo API:
- Thêm filtering:
GET /api/todos?completed=true - Thêm pagination:
GET /api/todos?page=1&limit=10 - Thêm search:
GET /api/todos?search=learn
- Thêm filtering:
-
Tạo API Mới:
- Xây dựng notes API đơn giản với categories
- Mỗi note nên có: id, title, content, category, created_at
-
Thêm Custom Middleware:
- Tạo middleware log request duration
- Tạo middleware thêm timestamp vào tất cả responses
-
Error Handling:
- Tạo centralized error handling middleware
- Xử lý malformed JSON requests một cách graceful
-
Response Enhancements:
- Thêm pagination metadata (total, page, pages)
- Thêm timestamps vào tất cả responses
Tiếp Theo Là Gì?
Trong bài tiếp theo, chúng ta sẽ đi sâu vào Express Middleware. Bạn sẽ học:
- Middleware hoạt động như thế nào bên trong
- Tạo custom middleware cho authentication và logging
- Popular third-party middleware (morgan, cors, helmet)
- Error handling middleware patterns
- Async error handling
Nền tảng này sẽ giúp bạn xây dựng các ứng dụng robust, production-ready hơn.
Tài Nguyên Bổ Sung
Official Documentation:
Tài Nguyên Học:
Tools:
- Postman - API testing
- VS Code REST Client
- nodemon - Auto-reload trong development
Các Bước Tiếp Theo Trong Series:
- Bài 2: Middleware Deep Dive
- Bài 3: Routing and Request Handling
- Bài 4: Working with MongoDB and Mongoose
- Bài 5: Working with PostgreSQL and Prisma
Tiếp tục xây dựng, và hẹn gặp bạn trong bài tiếp theo! 🚀
📬 Đăng ký nhận bản tin
Nhận những bài viết mới nhất qua email mỗi tuần. Không spam, hủy đăng ký bất cứ lúc nào.
Chúng tôi tôn trọng quyền riêng tư của bạn. Hủy đăng ký bất cứ lúc nào.
💬 Comments
Đăng nhập để bình luận
Chúng tôi sẽ không đăng bất cứ điều gì mà không có sự cho phép của bạn.