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 server routes แสดงการไหลของ API endpoints และ middleware

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, Netlify, Cloudflare Workers และสภาพแวดล้อม Node.js มาตรฐานโดยไม่ต้องเปลี่ยนแปลงโค้ด

สถาปัตยกรรมแยก concerns ผ่านข้อตกลงของ directory:

server/api/users.get.tstypescript
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 จะรับ

server/api/products/[id].get.tstypescript
// 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:

server/api/products/index.post.tstypescript
// 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 โดยตรง

server/middleware/auth.tstypescript
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:

server/types/context.d.tstypescript
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 อย่างชัดเจน

server/utils/db.tstypescript
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 สามารถเข้าถึงได้โดยตรง:

server/api/posts/index.get.tstypescript
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

server/plugins/database.tstypescript
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 เต็มรูปแบบ

vue
<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

nuxt.config.tstypescript
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:

server/api/trending.get.tstypescript
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 ที่ใช้ร่วมกัน:

server/utils/errors.tstypescript
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

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 ที่ปรับแต่งสำหรับแต่ละแพลตฟอร์ม:

nuxt.config.tstypescript
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 โดยใช้ คู่มือการย้ายอย่างเป็นทางการ 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
#nitro
#vue
#full-stack
#api
#server-routes

แชร์

บทความที่เกี่ยวข้อง

Nuxt 4 directory structure migration guide

Nuxt 4 ในปี 2026: โครงสร้างไดเรกทอรีใหม่และคู่มือการย้ายจาก Nuxt 3

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

Vue 3 Composables Advanced Patterns

Vue 3 Composables ขั้นสูง: รูปแบบการใช้ซ้ำและคำถามสัมภาษณ์งาน 2026

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

เวิร์กโฟลว์การทดสอบ Vue ด้วย Vitest และ Vue Test Utils ในปี 2026

การทดสอบ Vue 3 ในปี 2026: Vitest, Vue Test Utils และคำถามสัมภาษณ์

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