Xây dựng API Gateway với Zuplo: Hành trình cá nhân hoá và kiểm thử thực chiến
Tôi đã tự mình thử nghiệm và triển khai Zuplo — một API gateway hiện đại, cực kỳ linh hoạt, nhưng vẫn còn khá mới với cộng đồng dev Việt…
Xây dựng API Gateway với Zuplo: Hành trình cá nhân hoá và kiểm thử thực chiến
Tôi đã tự mình thử nghiệm và triển khai Zuplo — một API gateway hiện đại, cực kỳ linh hoạt, nhưng vẫn còn khá mới với cộng đồng dev Việt Nam. Nếu bạn cũng đang tò mò về cách ghép nối các khối ghép của Zuplo thành một hệ thống hoàn chỉnh, hành trình này dành cho bạn.
Chạm mặt Zuplo: Khi mọi thứ rời rạc trở nên liền mạch
Bạn từng bắt gặp một công nghệ mới mà tài liệu lại trải dài khắp nơi: nào là CLI, nào là bài blog, nào là trung tâm học tập? Tôi cũng vậy, và Zuplo là một ví dụ điển hình. Nhưng sau một buổi chiều kiên trì, tôi nhận ra mình có thể xâu chuỗi mọi thứ lại thành một quy trình cực kỳ nhất quán: tạo project, định tuyến, xác thực, giới hạn, policy TypeScript, deployment ra biên, rồi kiểm thử với Apidog. Nghe phức tạp? Thực ra lại rất mượt mà!
Và tin vui là, chỉ trong hơn 30 phút đồng hồ, bạn sẽ có một API gateway hoàn chỉnh đứng trước backend của mình — đã tích hợp xác thực, giới hạn tỷ lệ, cổng thông tin dev auto-gen và workflow Git chuẩn CI. Thú vị chứ?
Bức tranh tổng thể: TL;DR cho những ai muốn lướt nhanh
- Đăng ký ở portal.zuplo.com hoặc tạo project qua CLI với npm create zuplo.
- Định nghĩa route trong config/routes.oas.json, chuyển tiếp về backend bằng URL Forward Handler.
- Thêm inbound policies (API key, rate limit, schema validation) — thao tác file JSON hoặc thông qua Route Designer.
- Viết logic custom bằng TypeScript module trong modules/ — có sẵn context, request, env type-safe.
- Chỉ cần push Git để deploy preview, merge phát là lên production ở 300+ edge location.
- Kiểm thử từng route với Apidog trước khi rollout.
- Có gói free 100K request/tháng, Builder chỉ 25$/tháng.
Bạn cần gì để bắt đầu? Không nhiều đâu!
Có ba thứ không thể thiếu trước khi bắt tay vào: một tài khoản Zuplo, một API backend (chưa có? Dùng tạm https://echo.zuplo.io), và Node.js 18+ cho CLI.
- Tài khoản Zuplo
- API gốc để đứng phía sau gateway (hoặc echo.zuplo.io nếu chỉ muốn thử)
- Node.js 18 trở lên (nếu dùng CLI)
Bước 1 — Khởi động dự án Zuplo: Cổng hay CLI?
Bạn thích thao tác chuột, hay thích gõ lệnh? Tôi từng thử cả hai. Đa số demo sẽ bắt đầu với cổng web — nhanh, trực quan. Nhưng nếu bạn mê CI/CD, CLI là lựa chọn không thể bỏ qua.
Cổng web: Thao tác vài cú click là xong
- Đăng nhập portal.zuplo.com.
- Nhấn “New Project”, đặt tên (ví dụ: acme-gateway).
- Chọn “Empty Project” để tự mình làm chủ mọi thứ.
- Tab Code sẽ hiện ra cây file khởi tạo — nhìn rất quen nếu bạn làm backend.

Zuplo sẽ tự động liên kết với một repo Git quản lý. Bạn có thể đổi sang kho GitHub, GitLab, Bitbucket, Azure DevOps tuỳ ý — chỉ cần vào Settings.
CLI: Lập trình viên chân chính không thể bỏ qua
Nếu bạn khoái gõ lệnh, CLI giúp bạn tạo project local — sẵn sàng chỉnh sửa trong IDE và tích hợp Git từ đầu.
npm create zuplo@latest -- --name acme-gateway
cd acme-gateway
npm install
npm run dev
Máy chủ dev sẽ chạy ở cổng 9000, bạn còn được tặng thêm Route Designer bản địa ở http://localhost:9100. Mỗi lần save file, mọi thứ reload luôn.
Để gắn local project với tài khoản Zuplo khi muốn deploy:
npx zuplo link
Chọn tài khoản, chọn environment — xong! Sau đó, chỉ cần:
npx zuplo deploy
Bước 2 — Định tuyến: Bạn sẽ dẫn đường request ra sao?
Mở config/routes.oas.json — đây chính là file OpenAPI 3, nhưng có thêm mấy “phép màu” của Zuplo ở phần x-zuplo-route.
Tôi thử thêm route GET /v1/products chuyển tiếp về backend:
{
"openapi": "3.1.0",
"info": { "title": "Acme Gateway", "version": "1.0.0" },
"paths": {
"/v1/products": {
"get": {
"summary": "Liệt kê sản phẩm",
"operationId": "list-products",
"x-zuplo-route": {
"corsPolicy": "anything-goes",
"handler": {
"export": "urlForwardHandler",
"module": "$import(@zuplo/runtime)",
"options": {
"baseUrl": "${env.ORIGIN_URL}"
}
},
"policies": { "inbound": [] }
},
"responses": {
"200": { "description": "Thành công" }
}
}
}
}
}
Chú ý: x-zuplo-route là nơi Zuplo “cấy” thêm logic vào file OpenAPI. Handler là proxy tích hợp sẵn — baseUrl lấy từ biến môi trường, giúp bạn dễ chuyển đổi backend tuỳ môi trường.
Set ORIGIN_URL ở Settings > Environment Variables (cổng) hoặc config/.env (local). Nếu chưa có backend thật, cứ dùng https://echo.zuplo.io.
Giờ thì truy cập thử http://localhost:9000/v1/products — mọi thứ hoạt động tức thì!
Bước 3 — Xác thực API Key: Bảo vệ API từ sớm
Đừng để API của bạn “phơi nắng”. Zuplo cho phép tích hợp API key service quản lý — không cần tự build khoá nữa.
Chỉnh lại route để thêm policy inbound:
"policies": {
"inbound": ["api-key-auth"]
}
Định nghĩa policy trong config/policies.json:
{
"name": "api-key-auth",
"policyType": "api-key-inbound",
"handler": {
"export": "ApiKeyInboundPolicy",
"module": "$import(@zuplo/runtime)",
"options": {
"allowUnauthenticatedRequests": false
}
}
}
Tạo consumer (chủ sở hữu API key):
- Vào Services > API Key Service (trên portal).
- Nhấn “Create Consumer”.
- Đặt subject là định danh ổn định (ví dụ: acme-customer-1).
- Thêm email quản lý khoá.
- Copy lại khoá API vừa được tạo.
Thử nghiệm nhanh bằng curl:
curl -i https://YOUR-PROJECT.zuplo.app/v1/products
HTTP/2 401
curl -i https://YOUR-PROJECT.zuplo.app/v1/products \
-H "Authorization: Bearer YOUR_API_KEY"
HTTP/2 200
Nếu muốn kiểm thử UI, bạn có thể nhập OpenAPI spec vào Apidog, set header Authorization: Bearer {{api_key}} và mapping biến môi trường. Tất cả chỉ mất vài giây.
Bước 4 — Giới hạn tỷ lệ: Đừng để bị spam!
Bạn có biết, mọi API công khai không có rate limit đều sớm muộn sẽ gặp rắc rối? Zuplo cung cấp policy limit mặc định cực kỳ linh hoạt: theo IP, theo key, hay tuỳ biến.
Thêm vào inbound policies:
"policies": {
"inbound": ["api-key-auth", "rate-limit-by-key"]
}
Định nghĩa policy trong config/policies.json:
{
"name": "rate-limit-by-key",
"policyType": "rate-limit-inbound",
"handler": {
"export": "RateLimitInboundPolicy",
"module": "$import(@zuplo/runtime)",
"options": {
"rateLimitBy": "sub",
"requestsAllowed": 60,
"timeWindowMinutes": 1
}
}
}
rateLimitBy: “sub” sẽ chia theo chủ thể xác thực (mỗi API key có ngân sách riêng). Muốn limit theo IP thì sửa thành “ip.” Yêu cầu thứ 61 sẽ nhận 429 — kiểm thử bằng shell loop là rõ ngay!
for i in {1..70}; do
curl -s -o /dev/null -w "%{http_code}\n" \
https://YOUR-PROJECT.zuplo.app/v1/products \
-H "Authorization: Bearer YOUR_API_KEY"
done | sort | uniq -c
Bước 5 — Xác thực payload: Đừng để dữ liệu rác chui qua gateway
Ai cũng muốn API mình “sạch” ngay từ ngoài cửa. Nếu bạn có route POST nhận body JSON, hãy để Zuplo kiểm tra schema ngay trên gateway!
Ví dụ với route POST /v1/products (có body):
"/v1/products": {
"post": {
"summary": "Tạo sản phẩm",
"operationId": "create-product",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["name", "priceCents"],
"properties": {
"name": { "type": "string", "minLength": 1 },
"priceCents": { "type": "integer", "minimum": 1 },
"category": { "type": "string", "enum": ["food", "drink"] }
}
}
}
}
},
"x-zuplo-route": {
"handler": { /* tương tự như trên */ },
"policies": {
"inbound": [
"api-key-auth",
"rate-limit-by-key",
"validate-request"
]
}
}
}
}
{
"name": "validate-request",
"policyType": "open-api-request-validation-inbound",
"handler": {
"export": "OpenApiRequestValidationInboundPolicy",
"module": "$import(@zuplo/runtime)",
"options": {
"validateBody": "reject"
}
}
}
Giờ nếu ai gửi thiếu trường bắt buộc sẽ bị trả về 400 ngay trên gateway! Thử tạo ba request mẫu trong Apidog: thành công, thiếu trường, enum sai — chạy nhóm chỉ bằng một cú click.
Bước 6 — Chính sách TypeScript custom: Đỉnh cao tuỳ biến
Đôi khi bạn muốn kiểm soát thứ mà policy built-in không đáp ứng. Tôi đã thử viết một policy outbound để set Cache-Control khác nhau cho khách hàng free/paid.
modules/tiered-cache.ts:
import { ZuploRequest, ZuploContext, HttpProblems } from "@zuplo/runtime";
interface PolicyOptions {
paidPlanHeader: string;
paidMaxAge: number;
}
export default async function (
response: Response,
request: ZuploRequest,
context: ZuploContext,
options: PolicyOptions,
): Promise<Response> {
const plan = request.user?.data?.plan ?? "free";
if (plan === "free") {
response.headers.set("Cache-Control", "no-store");
} else {
response.headers.set(
"Cache-Control",
`public, max-age=${options.paidMaxAge}`,
);
}
context.log.info(`Cache header set for plan=${plan}`);
return response;
}
Thêm vào config/policies.json:
{
"name": "tiered-cache",
"policyType": "custom-code-outbound",
"handler": {
"export": "default",
"module": "$import(./modules/tiered-cache)",
"options": {
"paidPlanHeader": "x-plan",
"paidMaxAge": 300
}
}
}
Và tham chiếu ở policies route:
"policies": {
"inbound": ["api-key-auth", "rate-limit-by-key"],
"outbound": ["tiered-cache"]
}
Điều tuyệt là bạn có thể test unit từng policy custom bằng Vitest/Jest — không cần chờ deploy.
Bước 7 — Deploy ra biên: Push Git là mọi thứ lên production
Bạn có tin không: deploy chỉ là git push. Mỗi nhánh đều có preview riêng, link riêng, kiểm thử độc lập trước khi merge vào main.
git add .
git commit -m "Thêm gateway sản phẩm với xác thực, giới hạn tỷ lệ và bộ nhớ đệm phân cấp"
git push origin feature/products-gateway
Zuplo tạo preview environment, in URL trên log build — domain phụ như https://acme-gateway-feature-products-gateway-abc123.zuplo.app.
Kiểm thử nốt, merge vào main là xong:
git checkout main
git merge feature/products-gateway
git push origin main
Chỉ sau một phút, bản mới đã live ở hơn 300 edge location toàn cầu. Rollback? Cứ git revert là gateway lùi lại trạng thái cũ. Đơn giản, không cần UI riêng.
Bước 8 — Developer Portal: Cổng thông tin tự động sinh ra
Zuplo auto-gen một developer portal ở https://YOUR-PROJECT.developers.zuplo.com. Mỗi lần deploy đều rebuild lại portal. Bạn sẽ có:
- Trang riêng cho mỗi route, có schema, mô tả, bảng thử API trực tiếp.
- Sample code đa ngôn ngữ: cURL, JS, Python, Go…
- Khách tự đăng ký lấy API key — không cần support thủ công!
- Branding tuỳ chỉnh dưới Developer Portal > Settings.
OpenAPI spec càng chi tiết, portal càng đẹp, càng chuyên nghiệp. Nếu cần tuỳ biến sâu, có thể fork source code Next.js trên GitHub Zuplo. Nhưng đa số chỉ cần dùng bản host sẵn là đủ.
Bước 9 — Kiểm thử toàn diện với Apidog: Đừng để lỗi lọt production!
Thẳng thắn nhé: mình từng trả giá vì không test kỹ mọi route trước khi deploy. Apidog giúp bạn kiểm tra mọi policy, mọi lỗi chỉ trong một workflow.

Quy trình tôi thường dùng:
- Nhập OpenAPI spec từ https://YOUR-PROJECT.zuplo.app/openapi vào Apidog — mọi operation đều thành request có thể gửi được.
- Tạo các environment: local, preview, production — mỗi env để base_url, api_key riêng.
- Lưu ít nhất 3 test cho mỗi route: success, auth lỗi, rate limit lỗi. Chạy cả nhóm test trước mỗi lần deploy.
- Dùng test script automation của Apidog: liên kết các call, kiểm định response shape.
- Gen code mẫu đa ngôn ngữ, copy luôn vào runbook khi cần.
Câu hỏi thường gặp: Tôi cũng từng thắc mắc như bạn
Làm sao chuyển route giữa các môi trường mà không sửa spec?
Chỉ cần dùng biến môi trường (ví dụ: ORIGIN_URL). Định nghĩa riêng cho từng env trong portal hoặc config/.env — route giữ nguyên, chỉ backend thay đổi.
Có chạy Zuplo offline được không?
Được nhé. npm run dev spin một gateway local trên port 9000, có cả Route Designer (9100). Chính sách custom, xác thực, rate limit đều chạy local. Duy nhất API key service cần Internet — có thể link cloud bất kỳ lúc nào.
Roll back production lỡ có lỗi kiểu gì?
git revert commit merge rồi push lên. Đơn giản mà hiệu quả, không cần UI rollback riêng vì mọi thứ nằm trong history Git.
Deploy có downtime không?
Không. Deploy atomic tại edge: request cũ vẫn vào bản cũ, request mới sang bản mới — không hề downtime.
Zuplo có hỗ trợ gRPC hay WebSocket không?
Có luôn. urlForwardHandler proxy WebSocket upgrade, gRPC có handler riêng. Tuy nhiên REST và GraphQL vẫn là best-case.
Công khai API cho AI agent kiểu gì?
Thêm MCP Server Handler vào route, trỏ đến OpenAPI spec và chọn operation cần công khai. Policies auth, rate limit vẫn áp dụng. Tài liệu Zuplo MCP Server hướng dẫn chi tiết.
Chi phí vận hành thế nào?
Free 100K request/tháng. Builder 1M request giá 25/tháng, thêm request 100/100K. Doanh nghiệp từ 1.000$/tháng theo năm. Xem chi tiết ở trang giá Zuplo.
Lời kết: Hành trình của tôi — còn bạn thì sao?
Sau khi mày mò cùng Zuplo, tôi thực sự bất ngờ về độ linh hoạt và tốc độ triển khai. Từ xác thực API key, rate limit, validate schema đến custom policy, cổng dev auto-gen, mọi thứ đều xoay quanh Git và kiểm thử liên tục. Chính vòng lặp test — deploy — test ấy giúp tôi phát hiện lỗi ngay từ sớm, tránh những pha “toang” production không đáng có.
Bạn đã thử áp dụng workflow này cho dự án của mình chưa? Hay còn vướng mắc nào với Zuplo? Cùng chia sẻ và thảo luận nhé!
메타데이터
- post_id
- 8a9d07282c4a
- slug
- xây-dựng-api-gateway-với-zuplo-hành-trình-cá-nhân-hoá-và-kiểm-thử-thực-chiến-8a9d07282c4a
- url
- https://medium.com/@trannkhanh/x%C3%A2y-d%E1%BB%B1ng-api-gateway-v%E1%BB%9Bi-zuplo-h%C3%A0nh-tr%C3%ACnh-c%C3%A1-nh%C3%A2n-ho%C3%A1-v%C3%A0-ki%E1%BB%83m-th%E1%BB%AD-th%E1%BB%B1c-chi%E1%BA%BFn-8a9d07282c4a
- canonical_url
- https://medium.com/@trannkhanh/x%C3%A2y-d%E1%BB%B1ng-api-gateway-v%E1%BB%9Bi-zuplo-h%C3%A0nh-tr%C3%ACnh-c%C3%A1-nh%C3%A2n-ho%C3%A1-v%C3%A0-ki%E1%BB%83m-th%E1%BB%AD-th%E1%BB%B1c-chi%E1%BA%BFn-8a9d07282c4a
- author_url
- https://medium.com/@trannkhanh
- status
- ok
- fetched_at
- 2026-07-10 22:23:26