Deploy Bun + Hono REST API lên Vercel miễn phí

Bun là JavaScript runtime nhanh nhất. Hono là web framework nhẹ nhất. Vercel cho phép triển khai serverless không cần cấu hình. Kết hợp lại, chúng cho phép bạn ship một REST API production-ready trong dưới 30 phút — hoàn toàn miễn phí.
Hướng dẫn này sẽ đưa bạn qua từng bước: tạo dự án, viết API, push lên GitHub và deploy lên Vercel với CI/CD tự động mỗi lần git push.
Bạn sẽ học được gì
✅ Cài đặt dự án Bun + Hono từ đầu
✅ Xây dựng REST API có kiểu dữ liệu với TypeScript
✅ Cấu hình Hono Vercel adapter
✅ Push code lên GitHub public repo miễn phí
✅ Deploy lên Vercel với CI/CD tự động không cần cấu hình
✅ Thêm biến môi trường và test trên production
✅ Hiểu giới hạn free tier của Vercel cho API
Điều kiện tiên quyết
Trước khi bắt đầu, hãy đảm bảo bạn đã có:
- Bun đã cài đặt (
curl -fsSL https://bun.sh/install | bash) - Git đã cài đặt
- Tài khoản GitHub miễn phí
- Tài khoản Vercel miễn phí (đăng ký bằng GitHub tại vercel.com)
# Kiểm tra Bun đã cài đặt
bun --version # 1.x.x
# Kiểm tra Git đã cài đặt
git --version # git version 2.x.xPhần 1: Tạo dự án Hono
Bước 1: Tạo khung với create-hono
Hono cung cấp công cụ scaffolding chính thức tự động cài đặt mọi thứ cho Vercel.
bun create hono my-apiBạn sẽ được hỏi để chọn template. Chọn vercel:
? Which template do you want to use?
aws-lambda
bun
cloudflare-pages
cloudflare-workers
deno
fastly
lambda-edge
netlify
nextjs
❯ vercel
...Sau đó chọn TypeScript khi được hỏi về ngôn ngữ.
Điều hướng vào dự án:
cd my-api
bun installBước 2: Hiểu cấu trúc dự án
Sau khi tạo khung, dự án của bạn trông như sau:
my-api/
├── api/
│ └── index.ts # Entry point của Vercel serverless
├── src/
│ └── index.ts # Ứng dụng Hono chính
├── package.json
├── tsconfig.json
└── .gitignoreĐiểm quan trọng: Vercel yêu cầu file tại api/index.ts. Dự án được tạo tự động đã kết nối sẵn điều này.
Hãy xem những gì đã được tạo ra:
// api/index.ts
import { handle } from 'hono/vercel'
import app from '../src/index'
export const config = {
runtime: 'edge',
}
export default handle(app)// src/index.ts
import { Hono } from 'hono'
const app = new Hono().basePath('/api')
app.get('/', (c) => {
return c.json({ message: 'Hello Hono!' })
})
export default appHàm handle từ hono/vercel bọc ứng dụng Hono của bạn thành một handler tương thích với Vercel. Config runtime: 'edge' báo cho Vercel chạy trên Edge Runtime (cold start nhanh hơn Node.js).
Phần 2: Xây dựng REST API
Hãy xây dựng một API thực tế — trình quản lý task đơn giản với các endpoint CRUD.
Bước 3: Tạo Task Router
Tạo file router riêng cho tasks:
mkdir -p src/routes
touch src/routes/tasks.ts// src/routes/tasks.ts
import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'
// In-memory store (thay bằng DB thực trong production)
type Task = {
id: string
title: string
done: boolean
createdAt: string
}
const tasks: Task[] = [
{ id: '1', title: 'Learn Bun', done: true, createdAt: new Date().toISOString() },
{ id: '2', title: 'Build with Hono', done: false, createdAt: new Date().toISOString() },
]
const createTaskSchema = z.object({
title: z.string().min(1).max(200),
})
const tasksRouter = new Hono()
// GET /api/tasks - Lấy danh sách tất cả tasks
tasksRouter.get('/', (c) => {
return c.json({ tasks, total: tasks.length })
})
// GET /api/tasks/:id - Lấy một task cụ thể
tasksRouter.get('/:id', (c) => {
const task = tasks.find((t) => t.id === c.req.param('id'))
if (!task) {
return c.json({ error: 'Task not found' }, 404)
}
return c.json({ task })
})
// POST /api/tasks - Tạo task mới
tasksRouter.post('/', zValidator('json', createTaskSchema), (c) => {
const { title } = c.req.valid('json')
const newTask: Task = {
id: String(Date.now()),
title,
done: false,
createdAt: new Date().toISOString(),
}
tasks.push(newTask)
return c.json({ task: newTask }, 201)
})
// PATCH /api/tasks/:id - Chuyển đổi trạng thái done
tasksRouter.patch('/:id', (c) => {
const task = tasks.find((t) => t.id === c.req.param('id'))
if (!task) {
return c.json({ error: 'Task not found' }, 404)
}
task.done = !task.done
return c.json({ task })
})
// DELETE /api/tasks/:id - Xóa task
tasksRouter.delete('/:id', (c) => {
const index = tasks.findIndex((t) => t.id === c.req.param('id'))
if (index === -1) {
return c.json({ error: 'Task not found' }, 404)
}
const [deleted] = tasks.splice(index, 1)
return c.json({ task: deleted })
})
export default tasksRouterCài đặt middleware Zod validator:
bun add @hono/zod-validator zodBước 4: Cập nhật App chính
Kết nối tasks router vào app chính:
// src/index.ts
import { Hono } from 'hono'
import { cors } from 'hono/cors'
import { logger } from 'hono/logger'
import tasksRouter from './routes/tasks'
const app = new Hono().basePath('/api')
// Middlewares
app.use('*', logger())
app.use('*', cors())
// Routes
app.route('/tasks', tasksRouter)
// Health check
app.get('/health', (c) => {
return c.json({
status: 'ok',
runtime: 'edge',
timestamp: new Date().toISOString(),
})
})
// 404 fallback
app.notFound((c) => {
return c.json({ error: 'Not found' }, 404)
})
export default appBước 5: Test trên local
bun run devDevelopment server khởi động tại http://localhost:3000. Test các endpoint của bạn:
# Health check
curl http://localhost:3000/api/health
# Lấy danh sách tasks
curl http://localhost:3000/api/tasks
# Tạo task mới
curl -X POST http://localhost:3000/api/tasks \
-H "Content-Type: application/json" \
-d '{"title": "Deploy to Vercel"}'
# Lấy một task
curl http://localhost:3000/api/tasks/1
# Chuyển đổi trạng thái done
curl -X PATCH http://localhost:3000/api/tasks/1
# Xóa task
curl -X DELETE http://localhost:3000/api/tasks/1Kết quả mong đợi cho health check:
{
"status": "ok",
"runtime": "edge",
"timestamp": "2026-02-11T10:00:00.000Z"
}Phần 3: Biến môi trường
API thực tế cần secrets — API keys, database URLs, v.v. Hono trên Vercel truy cập chúng theo cách chuẩn.
Bước 6: Thêm hỗ trợ biến môi trường
// src/index.ts (cập nhật)
import { Hono } from 'hono'
import { cors } from 'hono/cors'
import { logger } from 'hono/logger'
import tasksRouter from './routes/tasks'
// Khai báo kiểu cho env vars để có type safety đầy đủ
type Env = {
Bindings: {
DATABASE_URL: string
API_SECRET: string
}
}
const app = new Hono<Env>().basePath('/api')
app.use('*', logger())
app.use('*', cors())
app.route('/tasks', tasksRouter)
app.get('/health', (c) => {
// Truy cập env vars qua c.env (Edge Runtime) hoặc process.env (Node.js)
const hasDb = !!c.env?.DATABASE_URL || !!process.env.DATABASE_URL
return c.json({
status: 'ok',
database: hasDb ? 'connected' : 'not configured',
timestamp: new Date().toISOString(),
})
})
app.notFound((c) => c.json({ error: 'Not found' }, 404))
export default appTạo file .env.local cho development local:
# .env.local
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
API_SECRET=my-local-secretThêm vào .gitignore — không bao giờ commit secrets lên Git:
# .gitignore
node_modules/
.env
.env.local
.env*.local
.vercelPhần 4: Push lên GitHub
Bước 7: Tạo GitHub Repository
- Truy cập github.com/new
- Đặt Repository name là
my-api - Đặt visibility là Public (bắt buộc cho Vercel free tier với unlimited deployments)
- KHÔNG khởi tạo với README (chúng ta đã có code sẵn)
- Nhấp Create repository
Bước 8: Khởi tạo Git và Push
Trong terminal:
# Khởi tạo git repo
git init
# Stage tất cả files
git add .
# Commit đầu tiên
git commit -m "feat: init Hono API with Vercel adapter"
# Thêm remote GitHub (thay YOUR_USERNAME bằng username GitHub của bạn)
git remote add origin https://github.com/YOUR_USERNAME/my-api.git
# Push lên GitHub
git branch -M main
git push -u origin mainXác nhận code của bạn đã lên GitHub bằng cách truy cập https://github.com/YOUR_USERNAME/my-api.
Phần 5: Deploy lên Vercel
Bước 9: Import dự án vào Vercel
- Truy cập vercel.com/new
- Nhấp "Import Git Repository"
- Kết nối tài khoản GitHub nếu chưa kết nối
- Tìm
my-apivà nhấp Import
Vercel sẽ tự động phát hiện đây là dự án Hono và cấu hình build settings. Bạn sẽ thấy:
- Framework Preset: Other
- Root Directory:
./ - Build Command: (để trống hoặc
bun run build) - Output Directory: (để trống)
- Install Command:
bun install
Bước 10: Thêm biến môi trường trong Vercel
Trước khi nhấp Deploy, thêm biến môi trường:
- Mở rộng phần "Environment Variables"
- Thêm từng biến:
| Tên | Giá trị | Môi trường |
|---|---|---|
DATABASE_URL | your-db-url | Production, Preview, Development |
API_SECRET | your-secret | Production, Preview, Development |
- Nhấp Deploy
Vercel sẽ build và deploy API của bạn. Trong khoảng 30-60 giây, bạn sẽ thấy:
✓ Build completed
✓ Deployment complete
🚀 https://my-api-yourusername.vercel.appBước 11: Test API trên production
Test các endpoint live với URL production:
BASE_URL="https://my-api-yourusername.vercel.app"
# Health check
curl $BASE_URL/api/health
# Lấy danh sách tasks
curl $BASE_URL/api/tasks
# Tạo task mới
curl -X POST $BASE_URL/api/tasks \
-H "Content-Type: application/json" \
-d '{"title": "First production task"}'Phần 6: CI/CD tự động
Đây là điểm mạnh của Vercel. Mỗi lần git push tự động kích hoạt deployment mới.
Cách hoạt động
Bước 12: Thực hiện thay đổi và xem CI/CD hoạt động
Cập nhật health check response:
// src/index.ts
app.get('/health', (c) => {
return c.json({
status: 'ok',
version: '1.1.0', // Thêm dòng này
runtime: 'edge',
timestamp: new Date().toISOString(),
})
})Push thay đổi:
git add .
git commit -m "feat: add version to health check"
git pushTruy cập Vercel dashboard — bạn sẽ thấy deployment mới đang build trong thời gian thực. Trong ~30 giây, thay đổi của bạn đã live.
Preview Deployments cho PRs
Khi bạn mở pull request trên GitHub, Vercel tự động tạo preview deployment với URL duy nhất như https://my-api-git-feature-branch-yourusername.vercel.app. Điều này cho phép bạn test các thay đổi trước khi merge vào main.
Phần 7: Cấu hình dự án chi tiết
Hiểu vercel.json
Với hầu hết dự án Hono, scaffolder đã xử lý sẵn, nhưng bạn có thể tùy chỉnh bằng file vercel.json:
{
"rewrites": [
{
"source": "/api/(.*)",
"destination": "/api/index"
}
]
}Điều này đảm bảo tất cả các route /api/* được xử lý bởi entry point Hono duy nhất của bạn.
Edge Runtime vs Node.js Runtime
runtime: 'edge' trong api/index.ts rất quan trọng:
| Edge Runtime | Node.js Runtime | |
|---|---|---|
| Cold start | ~0ms | 100-500ms |
| Thời gian thực thi tối đa | 30 giây | 60 giây |
| Bộ nhớ | 128MB | 1024MB |
| Node.js APIs | ❌ (Chỉ Web APIs) | ✅ |
| File system | ❌ | ❌ (chỉ đọc) |
| npm packages | Hạn chế | Hỗ trợ đầy đủ |
Dùng Edge Runtime cho: API CRUD đơn giản, proxy endpoints, auth middleware, các transformation nhẹ.
Dùng Node.js Runtime cho: tính toán nặng, packages cần Node.js APIs gốc, tạo PDF, xử lý ảnh.
Để chuyển sang Node.js runtime, cập nhật api/index.ts:
// api/index.ts — biến thể Node.js runtime
import { handle } from 'hono/vercel'
import app from '../src/index'
export const config = {
runtime: 'nodejs', // Đổi từ 'edge'
maxDuration: 60, // Tối đa 60s trên free tier
}
export default handle(app)package.json hoàn chỉnh
package.json đầy đủ của bạn nên trông như sau:
{
"name": "my-api",
"version": "1.0.0",
"scripts": {
"dev": "bun run --hot src/index.ts",
"build": "bun build ./api/index.ts --outdir ./dist",
"start": "bun run dist/index.js"
},
"dependencies": {
"@hono/zod-validator": "^0.4.1",
"hono": "^4.6.0",
"zod": "^3.23.8"
},
"devDependencies": {
"@types/bun": "latest",
"typescript": "^5.0.0"
}
}Phần 8: Free Tier của Vercel — Bạn nhận được gì
Hiểu các giới hạn giúp bạn lên kế hoạch tốt hơn:
| Tính năng | Free (Hobby) Tier |
|---|---|
| Deployments | Không giới hạn |
| Serverless Functions | 100GB-hours / tháng |
| Edge Function invocations | 500,000 / tháng |
| Bandwidth | 100GB / tháng |
| Custom domains | Không giới hạn |
| SSL certificates | Tự động (miễn phí) |
| Preview deployments | ✅ Mỗi PR |
| CI/CD qua GitHub | ✅ Tự động |
| Thành viên team | 1 (cá nhân) |
Với REST API cho side project hoặc SaaS nhỏ, free tier là quá đủ — 500,000 edge invocations mỗi tháng tương đương khoảng 16,000 requests mỗi ngày.
Cấu trúc dự án hoàn chỉnh
Đây là cấu trúc cuối cùng của API đã deploy:
my-api/
├── api/
│ └── index.ts # Entry point Vercel (edge runtime)
├── src/
│ ├── index.ts # Hono app + middleware
│ └── routes/
│ └── tasks.ts # Task CRUD router
├── .env.local # Secrets local (git-ignored)
├── .gitignore
├── package.json
├── tsconfig.json
└── vercel.json # Cấu hình Vercel tùy chọnXử lý sự cố
"Function not found" trên Vercel
Nguyên nhân: Vercel không tìm thấy entry point của serverless function.
Sửa: Đảm bảo api/index.ts tồn tại và export một default handler:
export default handle(app)Lỗi CORS trong trình duyệt
Nguyên nhân: Middleware cors() chưa được áp dụng.
Sửa: Thêm app.use('*', cors()) trước các routes trong src/index.ts. Trong production, hãy giới hạn origins:
app.use('*', cors({
origin: ['https://yourfrontend.com', 'http://localhost:5173'],
}))Biến môi trường undefined trong production
Nguyên nhân: Biến đã thêm ở local nhưng chưa thêm trong Vercel dashboard.
Sửa: Vào Vercel dashboard → Dự án của bạn → Settings → Environment Variables. Thêm từng biến và redeploy.
Build thất bại do lỗi TypeScript
Nguyên nhân: Cấu hình TypeScript strict từ chối implicit any.
Sửa: Kiểm tra tsconfig.json và đảm bảo types đã được cài đặt:
bun add -d @types/bunTóm tắt và Bài học chính
Bạn đã có một REST API được deploy đầy đủ với CI/CD tự động — hoàn toàn miễn phí.
✅ Hono + Vercel adapter xử lý việc kết nối serverless function
✅ Edge Runtime cho cold start gần bằng không
✅ GitHub public repo kích hoạt deploy tự động mỗi lần push
✅ Vercel free tier hỗ trợ 500K edge invocations/tháng
✅ Biến môi trường giữ secrets ra khỏi Git repo
✅ Preview deployments trên mỗi PR để test an toàn
✅ Zod validation thêm type-safe request body parsing
Tiếp theo nên làm gì
- Kết nối database thực: Neon (serverless PostgreSQL, free tier) hoặc Upstash (Redis, free tier)
- Thêm JWT authentication với middleware
hono/jwtcủa Hono - Thêm rate limiting với middleware
hono/rate-limitercủa Hono - Viết tests với
bun test - Cài đặt custom domain trong Vercel settings
Stack Bun + Hono + Vercel là một trong những cách nhanh nhất để ship backend ngày nay. Bạn có được TypeScript-first development, hiệu năng cực cao và zero-config deployments — tất cả trên free tier hào phóng.
📬 Đă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.