# Nuxt Nitro và Server Routes năm 2026: Full-Stack Vue và API Endpoints
> Làm chủ Nuxt Nitro server routes để xây dựng ứng dụng Vue full-stack. Hướng dẫn chi tiết về API endpoints, middleware, tích hợp database và triển khai production với Nuxt 4.
- Published: 2026-07-16
- Updated: 2026-07-16
- Author: SharpSkill
- Tags: nuxt, nitro, vue, full-stack, api, server-routes
- Reading time: 12 min
---
Nuxt Nitro biến đổi các ứng dụng Vue thành nền tảng full-stack mạnh mẽ bằng cách cung cấp engine server đa năng xử lý API routes, middleware và logic server-side song song với các component frontend. Với Nuxt 4.3 và Nitro v3 sắp ra mắt, việc xây dựng backend sẵn sàng cho production chưa bao giờ tích hợp tốt hơn với phát triển Vue.
> **Full-Stack trong Một Repository**
>
> Nitro cho phép chia sẻ TypeScript types giữa frontend và backend, loại bỏ sự khác biệt trong API contract và giảm boilerplate. Server routes nằm trong `server/api/` và tự động khả dụng tại các endpoint `/api/*`.
## Hiểu về Kiến trúc Nuxt Nitro Server Engine
Nitro đóng vai trò là runtime server JavaScript đa năng cung cấp sức mạnh cho thư mục server của Nuxt. Khác với server Node.js truyền thống, Nitro biên dịch thành các bundle được tối ưu hóa cho từng platform và có thể triển khai liền mạch tới [Vercel](https://vercel.com/docs/frameworks/nuxt), [Netlify](https://docs.netlify.com/frameworks/nuxt/), Cloudflare Workers và môi trường Node.js tiêu chuẩn mà không cần thay đổi code.
Kiến trúc phân tách các concerns thông qua các quy ước thư mục:
```typescript
// server/api/users.get.ts
export default defineEventHandler(async (event) => {
// Nitro auto-serializes return values to JSON
// HTTP method suffix (.get.ts) restricts to GET requests
const users = await getUsersFromDatabase()
return { users, count: users.length }
})
```
Nitro quét bốn thư mục: `server/api` cho API routes với prefix `/api`, `server/routes` cho routes không có prefix, `server/middleware` cho request interceptors, và `server/utils` cho các server utilities dùng chung. Các file nằm ngoài những thư mục này sẽ không hiển thị với server runtime.
## Tạo RESTful API Endpoints với defineEventHandler
Server routes tuân theo quy ước file-based routing trong đó tên file được ánh xạ trực tiếp tới URL paths. Hậu tố HTTP method kiểm soát request nào mỗi handler chấp nhận.
```typescript
// server/api/products/[id].get.ts
// Handles GET /api/products/123
export default defineEventHandler(async (event) => {
// Extract route parameter from URL
const id = getRouterParam(event, 'id')
if (!id) {
throw createError({
status: 400, // Nuxt 4 uses Web API naming (not statusCode)
statusText: 'Product ID required' // Not statusMessage
})
}
const product = await fetchProduct(id)
if (!product) {
throw createError({
status: 404,
statusText: 'Product not found'
})
}
return product
})
```
Nuxt 4 áp dụng quy ước đặt tên Web API để chuẩn bị cho Nitro v3. Property `statusCode` trở thành `status`, và `statusMessage` trở thành `statusText`. Mặc dù các property cũ vẫn hoạt động, việc chuyển sang đặt tên mới đảm bảo tương thích với Nuxt 5.
Xử lý POST requests với validation body:
```typescript
// server/api/products/index.post.ts
// Handles POST /api/products
import { z } from 'zod'
const ProductSchema = z.object({
name: z.string().min(1).max(200),
price: z.number().positive(),
category: z.enum(['electronics', 'clothing', 'food'])
})
export default defineEventHandler(async (event) => {
// readBody automatically parses JSON
const body = await readBody(event)
// Validate with Zod schema
const result = ProductSchema.safeParse(body)
if (!result.success) {
throw createError({
status: 422,
statusText: 'Validation failed',
data: result.error.flatten()
})
}
const product = await createProduct(result.data)
// Set 201 Created status for resource creation
setResponseStatus(event, 201)
return product
})
```
## Server Middleware cho Authentication và Logging
Các file middleware được thực thi trước route handlers, cho phép các cross-cutting concerns như authentication, logging và biến đổi request. Khác với route handlers, middleware không trả về response trực tiếp.
```typescript
// server/middleware/auth.ts
export default defineEventHandler(async (event) => {
// Skip auth for public routes
const publicRoutes = ['/api/health', '/api/auth/login']
if (publicRoutes.includes(event.path)) {
return // Continue to next handler
}
const authHeader = getHeader(event, 'authorization')
if (!authHeader?.startsWith('Bearer ')) {
throw createError({
status: 401,
statusText: 'Authentication required'
})
}
const token = authHeader.slice(7)
try {
// Verify JWT and attach user to event context
const user = await verifyToken(token)
event.context.user = user
} catch {
throw createError({
status: 401,
statusText: 'Invalid or expired token'
})
}
})
```
Đối tượng `event.context` tồn tại xuyên suốt middleware và handlers trong một request duy nhất, cung cấp cách type-safe để chia sẻ dữ liệu. Định nghĩa context types trong file khai báo:
```typescript
// server/types/context.d.ts
declare module 'h3' {
interface H3EventContext {
user?: {
id: string
email: string
role: 'admin' | 'user'
}
}
}
```
Để chuẩn bị phỏng vấn về các khái niệm Nuxt server, hãy xem module [câu hỏi phỏng vấn Nuxt Server Routes](/technologies/vue-nuxt/interview-questions/nuxt-server-routes).
## Tích hợp Database với Prisma và Server Utils
Server utilities trong `server/utils/` tự động import xuyên suốt toàn bộ server code, giúp database clients và logic dùng chung có thể truy cập mà không cần import rõ ràng.
```typescript
// server/utils/db.ts
import { PrismaClient } from '@prisma/client'
// Singleton pattern prevents multiple instances in development
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined
}
export const prisma = globalForPrisma.prisma ?? new PrismaClient({
log: process.env.NODE_ENV === 'development'
? ['query', 'error', 'warn']
: ['error']
})
if (process.env.NODE_ENV !== 'production') {
globalForPrisma.prisma = prisma
}
```
Với database client có sẵn toàn cục, route handlers có thể truy cập trực tiếp:
```typescript
// server/api/posts/index.get.ts
export default defineEventHandler(async (event) => {
// Query parameters for pagination
const query = getQuery(event)
const page = Number(query.page) || 1
const limit = Math.min(Number(query.limit) || 10, 100)
const skip = (page - 1) * limit
// Parallel queries for data and count
const [posts, total] = await Promise.all([
prisma.post.findMany({
skip,
take: limit,
orderBy: { createdAt: 'desc' },
include: { author: { select: { name: true, avatar: true } } }
}),
prisma.post.count()
])
return {
posts,
pagination: {
page,
limit,
total,
pages: Math.ceil(total / limit)
}
}
})
```
## Server Plugins cho Runtime Initialization
Server plugins trong `server/plugins/` được thực thi một lần khi server khởi động, lý tưởng để khởi tạo kết nối, lập lịch tasks, hoặc thiết lập monitoring.
```typescript
// server/plugins/database.ts
export default defineNitroPlugin(async (nitroApp) => {
// Verify database connection on startup
try {
await prisma.$connect()
console.log('Database connected successfully')
} catch (error) {
console.error('Database connection failed:', error)
process.exit(1) // Fail fast if DB unavailable
}
// Graceful shutdown hook
nitroApp.hooks.hook('close', async () => {
await prisma.$disconnect()
console.log('Database disconnected')
})
})
```
Hệ thống plugin tích hợp với [Nitro hooks](https://nitro.unjs.io/guide/plugins) để quản lý lifecycle. Hooks được kích hoạt tại các điểm cụ thể: `request` trước khi xử lý, `beforeResponse` trước khi gửi, và `close` trong khi shutdown.
## Gọi API Type-Safe từ Vue Components
Utility `$fetch` của Nuxt cung cấp type inference từ server routes khi sử dụng alias `~/server`. Kết hợp với [composable useFetch](/technologies/vue-nuxt/interview-questions/nuxt-data-fetching), các component sử dụng APIs với hỗ trợ TypeScript đầy đủ.
```vue
Loading...
{{ error.message }}
```
## Chiến lược Caching cho Server Routes
Nuxt 4.3 giới thiệu các điều khiển caching nâng cao thông qua `routeRules` trong `nuxt.config.ts`. Server routes được hưởng lợi từ cùng một cơ sở hạ tầng caching như pages.
```typescript
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
// Cache product listings for 5 minutes
'/api/products': {
cache: {
maxAge: 300,
staleMaxAge: 600, // Serve stale while revalidating
swr: true
}
},
// No cache for user-specific data
'/api/user/**': { cache: false },
// Pre-render static API responses at build time
'/api/categories': { prerender: true }
}
})
```
Để invalidate cache động, sử dụng wrapper `defineCachedEventHandler`:
```typescript
// server/api/trending.get.ts
export default defineCachedEventHandler(async (event) => {
const trending = await calculateTrendingProducts()
return trending
}, {
maxAge: 60 * 5, // 5 minutes
name: 'trending-products',
getKey: () => 'trending', // Cache key for invalidation
shouldBypassCache: (event) => {
// Bypass for admin users
return event.context.user?.role === 'admin'
}
})
```
## Xử lý Error và Định dạng Response
Các error response nhất quán cải thiện khả năng sử dụng API. Tạo một utility error handler dùng chung:
```typescript
// server/utils/errors.ts
import { H3Error } from 'h3'
export function handleDatabaseError(error: unknown): never {
console.error('Database error:', error)
// Prisma-specific error handling
if (error instanceof Error && 'code' in error) {
const prismaError = error as { code: string }
if (prismaError.code === 'P2002') {
throw createError({
status: 409,
statusText: 'Resource already exists'
})
}
if (prismaError.code === 'P2025') {
throw createError({
status: 404,
statusText: 'Resource not found'
})
}
}
throw createError({
status: 500,
statusText: 'Internal server error'
})
}
```
Để tìm hiểu toàn diện về các pattern Vue.js và state management, hãy khám phá bài viết [so sánh Pinia vs Vuex](/blog/vue-nuxt/vue-3-pinia-vs-vuex-state-management).
> **Checklist Production**
>
> Trước khi triển khai Nitro servers: validate environment variables với Zod schemas, implement rate limiting middleware, thêm request logging để debugging, và cấu hình CORS headers cho truy cập API cross-origin.
## Triển khai Đa Nền tảng
Hệ thống preset của Nitro tạo ra các build được tối ưu hóa cho từng platform:
```typescript
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
// Auto-detected on Vercel/Netlify, or specify manually
preset: 'node-server', // 'vercel', 'netlify', 'cloudflare-workers'
// Compress responses
compressPublicAssets: true,
// External packages not to bundle
externals: {
external: ['@prisma/client']
}
}
})
```
Đối với triển khai Node.js, output bao gồm một server standalone:
```bash
# Build for production
npm run build
# Start the server
node .output/server/index.mjs
```
> **Nuxt 3 End of Life**
>
> Nuxt 3 đạt end-of-life vào ngày 31 tháng 7 năm 2026. Các dự án nên migrate sang Nuxt 4 sử dụng [hướng dẫn migration chính thức](https://nuxt.com/docs/getting-started/upgrade). Nuxt 5 với Nitro v3 sẽ theo sau ngay sau đó.
## Kết luận
- Server routes trong `server/api/` tự động ánh xạ tới các endpoint `/api/*` với hậu tố HTTP method (`.get.ts`, `.post.ts`) kiểm soát loại request
- Middleware trong `server/middleware/` xử lý authentication, logging và biến đổi request trước khi routes thực thi
- Đối tượng `event.context` chia sẻ dữ liệu có type giữa middleware và handlers trong một request
- Server utils trong `server/utils/` tự động import xuyên suốt server code, lý tưởng cho database clients và logic dùng chung
- Nuxt 4 áp dụng đặt tên Web API (`status`/`statusText`) để chuẩn bị cho Nitro v3
- `defineCachedEventHandler` cung cấp caching cấp route với các điều khiển invalidation
- Preset Nitro cho phép triển khai không cần cấu hình tới Vercel, Netlify, Cloudflare và server Node.js
---
Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack.
HTML version of this page: https://sharpskill.dev/vi/blog/vue-nuxt/nuxt-nitro-server-routes-2026