# Nuxt Nitro dan Server Routes di 2026: Full-Stack Vue dan API Endpoints
> Pelajari cara menguasai Nuxt Nitro server routes untuk membangun aplikasi Vue full-stack. Tutorial lengkap API endpoints, middleware, integrasi database, dan deployment production dengan 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 mengubah aplikasi Vue menjadi platform full-stack yang powerful dengan menyediakan engine server universal yang menangani API routes, middleware, dan logika server-side bersamaan dengan komponen frontend. Dengan Nuxt 4.3 dan Nitro v3 yang akan datang, membangun backend siap-production tidak pernah semudah ini terintegrasi dengan pengembangan Vue.
> **Full-Stack dalam Satu Repository**
>
> Nitro memungkinkan berbagi tipe TypeScript antara frontend dan backend, menghilangkan perbedaan kontrak API dan mengurangi boilerplate. Server routes berada di `server/api/` dan otomatis tersedia di endpoint `/api/*`.
## Memahami Arsitektur Nuxt Nitro Server Engine
Nitro berfungsi sebagai runtime server JavaScript universal yang menggerakkan direktori server Nuxt. Berbeda dengan server Node.js tradisional, Nitro mengkompilasi menjadi bundle yang dioptimalkan untuk platform dan dapat di-deploy dengan mulus ke [Vercel](https://vercel.com/docs/frameworks/nuxt), [Netlify](https://docs.netlify.com/frameworks/nuxt/), Cloudflare Workers, dan lingkungan Node.js standar tanpa perubahan kode.
Arsitekturnya memisahkan concerns melalui konvensi direktori:
```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 memindai empat direktori: `server/api` untuk API routes dengan prefix `/api`, `server/routes` untuk routes tanpa prefix, `server/middleware` untuk request interceptors, dan `server/utils` untuk shared server utilities. File di luar direktori ini tetap tidak terlihat oleh server runtime.
## Membuat RESTful API Endpoints dengan defineEventHandler
Server routes mengikuti konvensi file-based routing dimana nama file langsung dipetakan ke URL paths. Suffix HTTP method mengontrol request mana yang diterima setiap 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 mengadopsi konvensi penamaan Web API sebagai persiapan untuk Nitro v3. Property `statusCode` menjadi `status`, dan `statusMessage` menjadi `statusText`. Meskipun property lama masih berfungsi, migrasi ke penamaan baru memastikan kompatibilitas dengan Nuxt 5.
Menangani POST requests dengan validasi 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 untuk Autentikasi dan Logging
File middleware dieksekusi sebelum route handlers, memungkinkan cross-cutting concerns seperti autentikasi, logging, dan transformasi request. Berbeda dengan route handlers, middleware tidak mengembalikan response secara langsung.
```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'
})
}
})
```
Objek `event.context` bertahan di seluruh middleware dan handlers dalam satu request, menyediakan cara type-safe untuk berbagi data. Definisikan tipe context dalam file deklarasi:
```typescript
// server/types/context.d.ts
declare module 'h3' {
interface H3EventContext {
user?: {
id: string
email: string
role: 'admin' | 'user'
}
}
}
```
Untuk persiapan wawancara tentang konsep Nuxt server, tinjau modul [pertanyaan wawancara Nuxt Server Routes](/technologies/vue-nuxt/interview-questions/nuxt-server-routes).
## Integrasi Database dengan Prisma dan Server Utils
Server utilities di `server/utils/` auto-import di seluruh kode server, membuat database clients dan logika bersama dapat diakses tanpa import eksplisit.
```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
}
```
Dengan database client tersedia secara global, route handlers dapat mengaksesnya langsung:
```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 untuk Inisialisasi Runtime
Server plugins di `server/plugins/` dieksekusi sekali saat server dimulai, ideal untuk menginisialisasi koneksi, menjadwalkan tasks, atau menyiapkan 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')
})
})
```
Sistem plugin terintegrasi dengan [Nitro hooks](https://nitro.unjs.io/guide/plugins) untuk lifecycle management. Hooks diaktifkan pada titik tertentu: `request` sebelum handling, `beforeResponse` sebelum mengirim, dan `close` saat shutdown.
## Panggilan API Type-Safe dari Vue Components
Utility `$fetch` Nuxt menyediakan type inference dari server routes saat menggunakan alias `~/server`. Dikombinasikan dengan [composable useFetch](/technologies/vue-nuxt/interview-questions/nuxt-data-fetching), komponen mengkonsumsi API dengan dukungan TypeScript penuh.
```vue
Loading...
{{ error.message }}
```
## Strategi Caching untuk Server Routes
Nuxt 4.3 memperkenalkan kontrol caching yang ditingkatkan melalui `routeRules` di `nuxt.config.ts`. Server routes mendapat manfaat dari infrastruktur caching yang sama seperti 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 }
}
})
```
Untuk invalidasi cache dinamis, gunakan 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'
}
})
```
## Penanganan Error dan Format Response
Response error yang konsisten meningkatkan usability API. Buat utilitas error handler bersama:
```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'
})
}
```
Untuk pola Vue.js yang komprehensif dan state management, jelajahi artikel [perbandingan Pinia vs Vuex](/blog/vue-nuxt/vue-3-pinia-vs-vuex-state-management).
> **Checklist Production**
>
> Sebelum men-deploy Nitro servers: validasi environment variables dengan skema Zod, implementasikan rate limiting middleware, tambahkan request logging untuk debugging, dan konfigurasi CORS headers untuk akses API cross-origin.
## Deployment Lintas Platform
Sistem preset Nitro menghasilkan build yang dioptimalkan untuk setiap 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']
}
}
})
```
Untuk deployment Node.js, output mencakup server standalone:
```bash
# Build for production
npm run build
# Start the server
node .output/server/index.mjs
```
> **Nuxt 3 End of Life**
>
> Nuxt 3 mencapai end-of-life pada 31 Juli 2026. Proyek harus bermigrasi ke Nuxt 4 menggunakan [panduan migrasi resmi](https://nuxt.com/docs/getting-started/upgrade). Nuxt 5 dengan Nitro v3 menyusul segera setelahnya.
## Kesimpulan
- Server routes di `server/api/` otomatis dipetakan ke endpoint `/api/*` dengan suffix HTTP method (`.get.ts`, `.post.ts`) mengontrol tipe request
- Middleware di `server/middleware/` menangani autentikasi, logging, dan transformasi request sebelum routes dieksekusi
- Objek `event.context` membagikan data yang di-type antara middleware dan handlers dalam satu request
- Server utils di `server/utils/` auto-import di seluruh kode server, ideal untuk database clients dan logika bersama
- Nuxt 4 mengadopsi penamaan Web API (`status`/`statusText`) sebagai persiapan untuk Nitro v3
- `defineCachedEventHandler` menyediakan caching level route dengan kontrol invalidasi
- Preset Nitro memungkinkan deployment tanpa konfigurasi ke Vercel, Netlify, Cloudflare, dan server Node.js
---
Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack.
HTML version of this page: https://sharpskill.dev/id/blog/vue-nuxt/nuxt-nitro-server-routes-2026