# Nuxt Nitro และ Server Routes ในปี 2026: Full-Stack Vue และ API Endpoints
> เรียนรู้การใช้งาน Nuxt Nitro server routes เพื่อสร้างแอปพลิเคชัน Vue แบบ full-stack บทความครอบคลุม API endpoints, middleware, การเชื่อมต่อฐานข้อมูล และการ deploy production ด้วย 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 เปลี่ยนแอปพลิเคชัน Vue ให้กลายเป็นแพลตฟอร์ม full-stack ที่ทรงพลังโดยการมอบ server engine ที่ทำงานได้หลายแพลตฟอร์มซึ่งจัดการ API routes, middleware และ server-side logic ควบคู่ไปกับ frontend components ด้วย Nuxt 4.3 และ Nitro v3 ที่กำลังจะมาถึง การสร้าง backend ที่พร้อมสำหรับ production ไม่เคยผสานรวมกับการพัฒนา Vue ได้ดีเท่านี้มาก่อน
> **Full-Stack ใน Repository เดียว**
>
> Nitro ช่วยให้สามารถแชร์ TypeScript types ระหว่าง frontend และ backend ลดความแตกต่างของ API contract และลด boilerplate Server routes อยู่ใน `server/api/` และพร้อมใช้งานอัตโนมัติที่ endpoint `/api/*`
## ทำความเข้าใจสถาปัตยกรรม Nuxt Nitro Server Engine
Nitro ทำหน้าที่เป็น JavaScript server runtime แบบ universal ที่ขับเคลื่อน server directory ของ Nuxt แตกต่างจาก Node.js server แบบดั้งเดิม Nitro จะคอมไพล์เป็น bundle ที่ปรับแต่งสำหรับแต่ละแพลตฟอร์มและสามารถ deploy ได้อย่างราบรื่นไปยัง [Vercel](https://vercel.com/docs/frameworks/nuxt), [Netlify](https://docs.netlify.com/frameworks/nuxt/), Cloudflare Workers และสภาพแวดล้อม Node.js มาตรฐานโดยไม่ต้องเปลี่ยนแปลงโค้ด
สถาปัตยกรรมแยก concerns ผ่านข้อตกลงของ directory:
```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 สแกนสี่ directories: `server/api` สำหรับ API routes พร้อม prefix `/api`, `server/routes` สำหรับ routes ที่ไม่มี prefix, `server/middleware` สำหรับ request interceptors และ `server/utils` สำหรับ server utilities ที่ใช้ร่วมกัน ไฟล์นอกเหนือจาก directories เหล่านี้จะไม่ถูกมองเห็นโดย server runtime
## สร้าง RESTful API Endpoints ด้วย defineEventHandler
Server routes ปฏิบัติตามข้อตกลง file-based routing โดยชื่อไฟล์จะถูก map โดยตรงไปยัง URL paths ส่วนต่อท้าย HTTP method ควบคุมว่า request ใดที่แต่ละ handler จะรับ
```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 นำข้อตกลงการตั้งชื่อ Web API มาใช้เพื่อเตรียมพร้อมสำหรับ Nitro v3 Property `statusCode` กลายเป็น `status` และ `statusMessage` กลายเป็น `statusText` แม้ว่า properties เดิมจะยังคงทำงานได้ แต่การย้ายไปใช้การตั้งชื่อใหม่จะช่วยให้เข้ากันได้กับ Nuxt 5
การจัดการ POST requests พร้อมการ validate 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 สำหรับ Authentication และ Logging
ไฟล์ middleware จะถูกเรียกใช้งานก่อน route handlers เปิดใช้งาน cross-cutting concerns เช่น authentication, logging และการแปลง request แตกต่างจาก route handlers middleware จะไม่ส่งคืน response โดยตรง
```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'
})
}
})
```
object `event.context` คงอยู่ตลอด middleware และ handlers ภายใน request เดียว มอบวิธีที่ type-safe ในการแชร์ข้อมูล กำหนด context types ในไฟล์ declaration:
```typescript
// server/types/context.d.ts
declare module 'h3' {
interface H3EventContext {
user?: {
id: string
email: string
role: 'admin' | 'user'
}
}
}
```
สำหรับการเตรียมตัวสัมภาษณ์เกี่ยวกับแนวคิด Nuxt server สามารถศึกษาโมดูล [คำถามสัมภาษณ์ Nuxt Server Routes](/technologies/vue-nuxt/interview-questions/nuxt-server-routes)
## การเชื่อมต่อ Database ด้วย Prisma และ Server Utils
Server utilities ใน `server/utils/` จะถูก auto-import ทั่วทั้ง server code ทำให้ database clients และ logic ที่ใช้ร่วมกันสามารถเข้าถึงได้โดยไม่ต้อง import อย่างชัดเจน
```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
}
```
ด้วย database client ที่พร้อมใช้งานแบบ global route handlers สามารถเข้าถึงได้โดยตรง:
```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 สำหรับ Runtime Initialization
Server plugins ใน `server/plugins/` จะถูกเรียกใช้งานครั้งเดียวเมื่อ server เริ่มทำงาน เหมาะสำหรับการเริ่มต้นการเชื่อมต่อ การตั้งเวลา tasks หรือการตั้งค่า 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')
})
})
```
ระบบ plugin ผสานรวมกับ [Nitro hooks](https://nitro.unjs.io/guide/plugins) สำหรับการจัดการ lifecycle Hooks จะถูกเรียกใช้ที่จุดเฉพาะ: `request` ก่อนการจัดการ, `beforeResponse` ก่อนการส่ง และ `close` ระหว่าง shutdown
## การเรียก API แบบ Type-Safe จาก Vue Components
Utility `$fetch` ของ Nuxt มอบ type inference จาก server routes เมื่อใช้ alias `~/server` เมื่อรวมกับ [composable useFetch](/technologies/vue-nuxt/interview-questions/nuxt-data-fetching) components สามารถใช้งาน APIs พร้อมการสนับสนุน TypeScript เต็มรูปแบบ
```vue
Loading...
{{ error.message }}
```
## กลยุทธ์ Caching สำหรับ Server Routes
Nuxt 4.3 นำเสนอการควบคุม caching ที่ปรับปรุงแล้วผ่าน `routeRules` ใน `nuxt.config.ts` Server routes ได้รับประโยชน์จากโครงสร้างพื้นฐาน caching เดียวกันกับ 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 แบบ dynamic ใช้ 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'
}
})
```
## การจัดการ Error และการจัดรูปแบบ Response
Error responses ที่สอดคล้องกันช่วยปรับปรุงความสามารถในการใช้งาน API สร้าง utility error handler ที่ใช้ร่วมกัน:
```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'
})
}
```
สำหรับ patterns Vue.js และ state management แบบครอบคลุม สามารถศึกษาบทความ [การเปรียบเทียบ Pinia vs Vuex](/blog/vue-nuxt/vue-3-pinia-vs-vuex-state-management)
> **Checklist สำหรับ Production**
>
> ก่อน deploy Nitro servers: validate environment variables ด้วย Zod schemas, implement rate limiting middleware, เพิ่ม request logging สำหรับ debugging และกำหนดค่า CORS headers สำหรับการเข้าถึง API แบบ cross-origin
## การ Deploy ข้ามแพลตฟอร์ม
ระบบ preset ของ Nitro สร้าง builds ที่ปรับแต่งสำหรับแต่ละแพลตฟอร์ม:
```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']
}
}
})
```
สำหรับการ deploy บน Node.js output จะรวม server แบบ standalone:
```bash
# Build for production
npm run build
# Start the server
node .output/server/index.mjs
```
> **Nuxt 3 สิ้นสุดการสนับสนุน**
>
> Nuxt 3 จะสิ้นสุดการสนับสนุนในวันที่ 31 กรกฎาคม 2026 โปรเจกต์ควรย้ายไปยัง Nuxt 4 โดยใช้ [คู่มือการย้ายอย่างเป็นทางการ](https://nuxt.com/docs/getting-started/upgrade) Nuxt 5 พร้อม Nitro v3 จะตามมาในเร็วๆ นี้
## สรุป
- Server routes ใน `server/api/` จะถูก map โดยอัตโนมัติไปยัง endpoint `/api/*` โดยส่วนต่อท้าย HTTP method (`.get.ts`, `.post.ts`) ควบคุมประเภท request
- Middleware ใน `server/middleware/` จัดการ authentication, logging และการแปลง request ก่อนที่ routes จะทำงาน
- object `event.context` แชร์ข้อมูลที่มี type ระหว่าง middleware และ handlers ภายใน request
- Server utils ใน `server/utils/` จะถูก auto-import ทั่วทั้ง server code เหมาะสำหรับ database clients และ logic ที่ใช้ร่วมกัน
- Nuxt 4 นำการตั้งชื่อ Web API มาใช้ (`status`/`statusText`) เพื่อเตรียมพร้อมสำหรับ Nitro v3
- `defineCachedEventHandler` มอบ caching ระดับ route พร้อมการควบคุม invalidation
- Presets ของ Nitro ช่วยให้สามารถ deploy โดยไม่ต้องกำหนดค่าไปยัง Vercel, Netlify, Cloudflare และ Node.js servers
---
Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack.
HTML version of this page: https://sharpskill.dev/th/blog/vue-nuxt/nuxt-nitro-server-routes-2026