Nuxt Nitro และ Server Routes ในปี 2026: Full-Stack Vue และ API Endpoints
เรียนรู้การใช้งาน Nuxt Nitro server routes เพื่อสร้างแอปพลิเคชัน Vue แบบ full-stack บทความครอบคลุม API endpoints, middleware, การเชื่อมต่อฐานข้อมูล และการ deploy production ด้วย Nuxt 4

Nuxt Nitro เปลี่ยนแอปพลิเคชัน Vue ให้กลายเป็นแพลตฟอร์ม full-stack ที่ทรงพลังโดยการมอบ server engine ที่ทำงานได้หลายแพลตฟอร์มซึ่งจัดการ API routes, middleware และ server-side logic ควบคู่ไปกับ frontend components ด้วย Nuxt 4.3 และ Nitro v3 ที่กำลังจะมาถึง การสร้าง backend ที่พร้อมสำหรับ production ไม่เคยผสานรวมกับการพัฒนา Vue ได้ดีเท่านี้มาก่อน
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, Netlify, Cloudflare Workers และสภาพแวดล้อม Node.js มาตรฐานโดยไม่ต้องเปลี่ยนแปลงโค้ด
สถาปัตยกรรมแยก concerns ผ่านข้อตกลงของ directory:
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 จะรับ
// 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:
// 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 โดยตรง
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:
declare module 'h3' {
interface H3EventContext {
user?: {
id: string
email: string
role: 'admin' | 'user'
}
}
}สำหรับการเตรียมตัวสัมภาษณ์เกี่ยวกับแนวคิด Nuxt server สามารถศึกษาโมดูล คำถามสัมภาษณ์ Nuxt Server Routes
พร้อมที่จะพิชิตการสัมภาษณ์ Vue.js / Nuxt.js แล้วหรือยังครับ?
ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ
การเชื่อมต่อ Database ด้วย Prisma และ Server Utils
Server utilities ใน server/utils/ จะถูก auto-import ทั่วทั้ง server code ทำให้ database clients และ logic ที่ใช้ร่วมกันสามารถเข้าถึงได้โดยไม่ต้อง import อย่างชัดเจน
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 สามารถเข้าถึงได้โดยตรง:
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
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 สำหรับการจัดการ lifecycle Hooks จะถูกเรียกใช้ที่จุดเฉพาะ: request ก่อนการจัดการ, beforeResponse ก่อนการส่ง และ close ระหว่าง shutdown
การเรียก API แบบ Type-Safe จาก Vue Components
Utility $fetch ของ Nuxt มอบ type inference จาก server routes เมื่อใช้ alias ~/server เมื่อรวมกับ composable useFetch components สามารถใช้งาน APIs พร้อมการสนับสนุน TypeScript เต็มรูปแบบ
<script setup lang="ts">
// Inferred return type from server/api/products/[id].get.ts
const route = useRoute()
const { data: product, status, error } = await useFetch(
`/api/products/${route.params.id}`
)
// Mutation with optimistic updates
const { execute: updateProduct, status: updateStatus } = useFetch(
`/api/products/${route.params.id}`,
{
method: 'PATCH',
immediate: false, // Don't fetch on mount
watch: false // Don't refetch on dependency change
}
)
async function handleUpdate(updates: Partial<Product>) {
await updateProduct({ body: updates })
// Refetch to sync state
await refreshNuxtData(`product-${route.params.id}`)
}
</script>
<template>
<div v-if="status === 'pending'">Loading...</div>
<div v-else-if="error">{{ error.message }}</div>
<ProductDetail v-else :product="product" @update="handleUpdate" />
</template>กลยุทธ์ Caching สำหรับ Server Routes
Nuxt 4.3 นำเสนอการควบคุม caching ที่ปรับปรุงแล้วผ่าน routeRules ใน nuxt.config.ts Server routes ได้รับประโยชน์จากโครงสร้างพื้นฐาน caching เดียวกันกับ pages
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:
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 ที่ใช้ร่วมกัน:
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
ก่อน deploy Nitro servers: validate environment variables ด้วย Zod schemas, implement rate limiting middleware, เพิ่ม request logging สำหรับ debugging และกำหนดค่า CORS headers สำหรับการเข้าถึง API แบบ cross-origin
การ Deploy ข้ามแพลตฟอร์ม
ระบบ preset ของ Nitro สร้าง builds ที่ปรับแต่งสำหรับแต่ละแพลตฟอร์ม:
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:
# Build for production
npm run build
# Start the server
node .output/server/index.mjsNuxt 3 จะสิ้นสุดการสนับสนุนในวันที่ 31 กรกฎาคม 2026 โปรเจกต์ควรย้ายไปยัง Nuxt 4 โดยใช้ คู่มือการย้ายอย่างเป็นทางการ 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
แท็ก
แชร์
บทความที่เกี่ยวข้อง

Nuxt 4 ในปี 2026: โครงสร้างไดเรกทอรีใหม่และคู่มือการย้ายจาก Nuxt 3
คู่มือการย้ายจาก Nuxt 3 ไปยัง Nuxt 4 ฉบับสมบูรณ์ ครอบคลุมโครงสร้างไดเรกทอรี app/ ใหม่ singleton data fetching, shallow reactivity และการแยกบริบท TypeScript พร้อมตัวอย่างโค้ดจริง

Vue 3 Composables ขั้นสูง: รูปแบบการใช้ซ้ำและคำถามสัมภาษณ์งาน 2026
คู่มือ Vue 3 Composables ขั้นสูงฉบับสมบูรณ์: รูปแบบการใช้ซ้ำ การจัดการ Async การ Inject Dependencies การตรวจสอบฟอร์ม และคำถามสัมภาษณ์งานเทคนิคปี 2026

การทดสอบ Vue 3 ในปี 2026: Vitest, Vue Test Utils และคำถามสัมภาษณ์
คู่มือปฏิบัติสำหรับการทดสอบ Vue ในปี 2026: การตั้งค่า Vitest, การ mount คอมโพเนนต์ด้วย Vue Test Utils, การทดสอบ composable และ Pinia store, การ mock API, การวัด coverage และคำถามสัมภาษณ์ที่ทีมผู้ว่าจ้างถามจริง