Back to blog

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

bunhonoverceltypescriptbackendrest-api
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.x

Phầ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-api

Bạ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 install

Bướ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 app

Hà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 tasksRouter

Cài đặt middleware Zod validator:

bun add @hono/zod-validator zod

Bướ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 app

Bước 5: Test trên local

bun run dev

Development 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/1

Kế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 app

Tạo file .env.local cho development local:

# .env.local
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
API_SECRET=my-local-secret

Thêm vào .gitignore — không bao giờ commit secrets lên Git:

# .gitignore
node_modules/
.env
.env.local
.env*.local
.vercel

Phần 4: Push lên GitHub

Bước 7: Tạo GitHub Repository

  1. Truy cập github.com/new
  2. Đặt Repository name là my-api
  3. Đặt visibility là Public (bắt buộc cho Vercel free tier với unlimited deployments)
  4. KHÔNG khởi tạo với README (chúng ta đã có code sẵn)
  5. 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 main

Xá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

  1. Truy cập vercel.com/new
  2. Nhấp "Import Git Repository"
  3. Kết nối tài khoản GitHub nếu chưa kết nối
  4. Tìm my-api và 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:

  1. Mở rộng phần "Environment Variables"
  2. Thêm từng biến:
TênGiá trịMôi trường
DATABASE_URLyour-db-urlProduction, Preview, Development
API_SECRETyour-secretProduction, Preview, Development
  1. 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.app

Bướ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 push

Truy 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 RuntimeNode.js Runtime
Cold start~0ms100-500ms
Thời gian thực thi tối đa30 giây60 giây
Bộ nhớ128MB1024MB
Node.js APIs❌ (Chỉ Web APIs)✅
File system❌❌ (chỉ đọc)
npm packagesHạ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ăngFree (Hobby) Tier
DeploymentsKhông giới hạn
Serverless Functions100GB-hours / tháng
Edge Function invocations500,000 / tháng
Bandwidth100GB / tháng
Custom domainsKhông giới hạn
SSL certificatesTự động (miễn phí)
Preview deployments✅ Mỗi PR
CI/CD qua GitHub✅ Tự động
Thành viên team1 (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ọn

Xử 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/bun

Tó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/jwt của Hono
  • Thêm rate limiting với middleware hono/rate-limiter củ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.