Koa 2 là framework backend hiện đại cho Node.js, được thiết kế với kiến trúc middleware dựa trên async/await. Dưới đây là hướng dẫn chi tiết về cách thiết lập môi trường, cấu trúc máy chủ, xử lý dữ liệu từ client, cũng như cách định tuyến và phục vụ tài nguyên tĩnh.
Khởi tạo dự án và cấu hình nền tảng
Trước khi viết code, hãy tạo thư mục làm việc và khởi tạo tệp mô tả gói:
npm init -y
Tiếp theo, cài đặt gói Koa vào dự án:
npm install koa --save
Tạo tệp index.js để định nghĩa máy chủ cơ bản:
const Koa = require('koa');
const server = new Koa();
server.use(async (context) => {
context.body = 'Chào mừng đến với dịch vụ API';
});
server.listen(3000, () => {
console.log('Máy chủ đang lắng nghe trên cổng 3000');
});
Chạy lệnh node index.js để khởi động dịch vụ và truy cập http://localhost:3000.
Khái niệm Context và Middleware Pipeline
Mỗi yêu cầu HTTP trong Koa sẽ đi qua một chuỗi xử lý middleware. Đối tượng context (thường gọi tắt là ctx) đóng gói toàn bộ thông tin request và response. Tham số next đại diện cho middleware tiếp theo trong hàng đợi xử lý.
server.use(async (context, next) => {
await next(); // Đợi middleware phía sau hoàn thành
context.response.type = 'text/html';
context.response.body = '<h1>Xử lý hoàn tất</h1>';
});
Để rút ngắn cú pháp, Koa cho phép truy cập trực tiếp qua context:
context.urltương đươngcontext.request.urlcontext.typetương đươngcontext.response.type
Xử lý yêu cầu GET
Dữ liệu truy vấn từ URL có thể được trích xuất qua hai thuộc tính chính trên đối tượng request:
context.request.query: Trả về đối tượng đã được phân tích cú pháp dạng key-value.context.request.querystring: Trả về chuỗi thô chưa phân tích (loại bỏ ký tự dấu hỏi).
Dưới đây là ví dụ về giao diện frontend gửi yêu cầu GET:
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<title>Trang gửi GET</title>
</head>
<body>
<label>Tên người dùng: <input type="text" id="username"></label>
<label>Tuổi: <input type="number" id="userAge"></label>
<button id="submitBtn">Gửi dữ liệu</button>
<script>
document.getElementById('submitBtn').addEventListener('click', () => {
const params = new URLSearchParams({
username: document.getElementById('username').value,
userAge: document.getElementById('userAge').value
});
fetch(`/api/users?${params.toString()}`)
.then(res => res.json())
.then(data => console.log('Kết quả GET:', data));
});
</script>
</body>
</html>
Phía server, chúng ta sử dụng koa-router để định tuyến và kiểm tra dữ liệu trong mảng mô phỏng:
const Koa = require('koa');
const Router = require('koa-router');
const serve = require('koa-static');
const path = require('path');
const server = new Koa();
const router = new Router();
const userDatabase = [
{ name: 'Nguyen Van A', age: 25 },
{ name: 'Tran Thi B', age: 30 }
];
router.get('/api/users', async (context) => {
const { username, userAge } = context.query;
const existingRecord = userDatabase.find(record => record.name === username);
const payload = { status: 200, message: 'Hoàn tất' };
if (existingRecord) {
payload.status = 409;
payload.message = 'Người dùng đã tồn tại';
} else {
userDatabase.push({ name: username, age: Number(userAge) });
}
context.body = payload;
});
server.use(serve(path.join(__dirname, 'public')));
server.use(router.routes());
server.listen(3000, () => console.log('Dịch vụ GET đã sẵn sàng'));
Cần lưu ý sự khác biệt giữa context.request (đối tượng được Koa đóng gói, thân thiện và trực quan) và context.req (đối tượng HTTP native của Node.js).
Xử lý yêu cầu POST
Với phương thức POST, dữ liệu thường nằm ở phần thân yêu cầu (body). Koa không tích hợp sẵn công cụ phân tích, do đó cần cài đặt middleware koa-bodyparser:
npm install koa-bodyparser
Khai báo middleware ngay sau khi khởi tạo server:
const bodyParser = require('koa-bodyparser');
server.use(bodyParser());
Frontend gửi POST bằng phương thức fetch:
document.getElementById('submitBtn').addEventListener('click', () => {
const formData = new URLSearchParams({
username: document.getElementById('username').value,
userAge: document.getElementById('userAge').value
});
fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: formData.toString()
})
.then(res => res.json())
.then(data => console.log('Kết quả POST:', data));
});
Backend xử lý và ghi nhận dữ liệu:
router.post('/api/users', async (context) => {
const { username, userAge } = context.request.body;
const found = userDatabase.find(item => item.name === username);
const response = { code: 200, description: 'Ghi nhận thành công' };
if (found) {
response.code = 400;
response.description = 'Dữ liệu trùng lặp';
} else {
userDatabase.push({ name: username, age: Number(userAge) });
}
context.body = response;
});
Quy tắc tiếp nhận dữ liệu: dùng context.query cho GET và context.request.body cho POST.
Định tuyến (Routing)
Để quản lý các endpoint hiệu quả, koa-router là công cụ không thể thiếu. Cài đặt gói:
npm install koa-router
Khai báo và cấu hình route:
const Router = require('koa-router');
const router = new Router();
router.get('/info', async (context) => {
context.body = { appVersion: '2.0.1' };
});
// Có thể tham chiếu trực tiếp từ file module khác
// router.post('/data', require('./controllers/dataController'));
server.use(router.routes());
Phục vụ tài nguyên tĩnh
Để máy chủ có thể trả về các file HTML, CSS, JS hoặc ảnh, hãy sử dụng koa-static:
npm install koa-static
Áp dụng middleware để trỏ đến thư mục chứa tài nguyên:
const serve = require('koa-static');
const path = require('path');
// Phục vụ toàn bộ thư mục 'public' ở gốc domain
server.use(serve(path.join(__dirname, 'public')));
Thứ tự đăng ký middleware rất quan trọng: koa-static và koa-bodyparser nên được gắn trước router.routes() để đảm bảo pipeline xử lý đúng thứ tự mong muốn.