Nuxt Nitro i Server Routes w 2026: Full-Stack Vue i API Endpoints

Opanuj Nuxt Nitro server routes do budowania aplikacji full-stack Vue. Poznaj API endpoints, middleware, integrację z bazą danych oraz wzorce wdrożeń produkcyjnych z Nuxt 4.

Diagram architektury Nuxt Nitro server routes pokazujący API endpoints i przepływ middleware

Nuxt Nitro przekształca aplikacje Vue w pełnoprawne rozwiązania full-stack, dostarczając uniwersalny silnik serwerowy obsługujący API routes, middleware oraz logikę serwerową obok komponentów frontendowych. Wraz z Nuxt 4.3 i nadchodzącym Nitro v3, budowanie produkcyjnych backendów nigdy nie było tak zintegrowane z rozwojem Vue.

Full-Stack w jednym repozytorium

Nitro umożliwia współdzielenie typów TypeScript między frontendem a backendem, eliminując rozbieżności kontraktów API i redukując boilerplate. Server routes znajdują się w server/api/ i automatycznie stają się dostępne jako endpointy /api/*.

Architektura silnika Nuxt Nitro Server

Nitro służy jako uniwersalne środowisko uruchomieniowe JavaScript zasilające katalog server w Nuxt. W przeciwieństwie do tradycyjnych serwerów Node.js, Nitro kompiluje do bundli zoptymalizowanych pod konkretne platformy, które wdrażają się bezproblemowo na Vercel, Netlify, Cloudflare Workers oraz standardowe środowiska Node.js bez zmian w kodzie.

Architektura rozdziela odpowiedzialności poprzez konwencje katalogów:

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 skanuje cztery katalogi: server/api dla API routes z prefiksem /api, server/routes dla routes bez prefiksu, server/middleware dla interceptorów żądań oraz server/utils dla współdzielonych narzędzi serwerowych. Pliki poza tymi katalogami pozostają niewidoczne dla środowiska serwerowego.

Tworzenie RESTful API Endpoints z defineEventHandler

Server routes podążają za konwencjami routingu opartymi na plikach, gdzie nazwy plików mapują bezpośrednio na ścieżki URL. Sufiksy metod HTTP kontrolują, które żądania akceptuje każdy 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 przyjmuje konwencje nazewnictwa Web API w przygotowaniu do Nitro v3. Właściwość statusCode zmienia się na status, a statusMessage na statusText. Chociaż starsze właściwości nadal działają, migracja do nowego nazewnictwa zapewnia kompatybilność z Nuxt 5.

Obsługa żądań POST z walidacją 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 dla uwierzytelniania i logowania

Pliki middleware wykonują się przed handlerami tras, umożliwiając obsługę przekrojowych zagadnień takich jak uwierzytelnianie, logowanie i transformacja żądań. W przeciwieństwie do handlerów tras, middleware nie zwraca bezpośrednio odpowiedzi.

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'
    })
  }
})

Obiekt event.context utrzymuje się między middleware a handlerami w ramach pojedynczego żądania, zapewniając typowo-bezpieczny sposób współdzielenia danych. Typy kontekstu definiuje się w pliku deklaracji:

server/types/context.d.tstypescript
declare module 'h3' {
  interface H3EventContext {
    user?: {
      id: string
      email: string
      role: 'admin' | 'user'
    }
  }
}

Do przygotowań do rozmów kwalifikacyjnych dotyczących koncepcji serwera Nuxt warto zapoznać się z modułem pytań rekrutacyjnych o Nuxt Server Routes.

Gotowy na rozmowy o Vue.js / Nuxt.js?

Ćwicz z naszymi interaktywnymi symulatorami, flashcards i testami technicznymi.

Integracja bazy danych z Prisma i Server Utils

Narzędzia serwerowe w server/utils/ automatycznie importują się w całym kodzie serwerowym, czyniąc klienty baz danych i współdzieloną logikę dostępnymi bez jawnych importów.

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
}

Z globalnie dostępnym klientem bazy danych, handlery tras uzyskują do niego bezpośredni dostęp:

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 dla inicjalizacji runtime

Pluginy serwerowe w server/plugins/ wykonują się raz podczas startu serwera, idealnie nadając się do inicjalizacji połączeń, planowania zadań lub konfiguracji monitoringu.

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')
  })
})

System pluginów integruje się z hookami Nitro do zarządzania cyklem życia. Hooki uruchamiają się w określonych momentach: request przed obsługą, beforeResponse przed wysłaniem i close podczas zamykania.

Typowo-bezpieczne wywołania API z komponentów Vue

Narzędzie $fetch w Nuxt zapewnia wnioskowanie typów z tras serwerowych przy użyciu aliasu ~/server. W połączeniu z composable useFetch, komponenty konsumują API z pełnym wsparciem 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>

Strategie cachowania dla Server Routes

Nuxt 4.3 wprowadza ulepszone kontrole cachowania poprzez routeRules w nuxt.config.ts. Server routes korzystają z tej samej infrastruktury cachowania co strony.

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 }
  }
})

Dla dynamicznej inwalidacji cache należy użyć wrappera 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'
  }
})

Obsługa błędów i formatowanie odpowiedzi

Spójne odpowiedzi błędów poprawiają użyteczność API. Należy utworzyć współdzielone narzędzie do obsługi błędów:

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'
  })
}

Dla kompleksowych wzorców Vue.js i zarządzania stanem warto zapoznać się z artykułem porównanie Pinia vs Vuex.

Checklista produkcyjna

Przed wdrożeniem serwerów Nitro należy: zwalidować zmienne środowiskowe schematami Zod, zaimplementować middleware rate limiting, dodać logowanie żądań do debugowania oraz skonfigurować nagłówki CORS dla cross-origin dostępu do API.

Wdrożenie na różnych platformach

System presetów Nitro generuje zoptymalizowane buildy dla każdej platformy:

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']
    }
  }
})

Dla wdrożeń Node.js, output zawiera samodzielny serwer:

bash
# Build for production
npm run build

# Start the server
node .output/server/index.mjs
Koniec wsparcia dla Nuxt 3

Nuxt 3 osiąga koniec wsparcia 31 lipca 2026. Projekty powinny migrować do Nuxt 4 używając oficjalnego przewodnika migracji. Nuxt 5 z Nitro v3 pojawi się wkrótce potem.

Zacznij ćwiczyć!

Sprawdź swoją wiedzę z naszymi symulatorami rozmów i testami technicznymi.

Podsumowanie

  • Server routes w server/api/ automatycznie mapują się na endpointy /api/* z sufiksami metod HTTP (.get.ts, .post.ts) kontrolującymi typy żądań
  • Middleware w server/middleware/ obsługuje uwierzytelnianie, logowanie i transformację żądań przed wykonaniem tras
  • Obiekt event.context współdzieli typowane dane między middleware a handlerami w ramach żądania
  • Narzędzia serwerowe w server/utils/ automatycznie importują się w kodzie serwerowym, idealnie dla klientów baz danych i współdzielonej logiki
  • Nuxt 4 przyjmuje nazewnictwo Web API (status/statusText) w przygotowaniu do Nitro v3
  • defineCachedEventHandler zapewnia cachowanie na poziomie tras z kontrolami inwalidacji
  • Presety Nitro umożliwiają bezproblemowe wdrożenia na Vercel, Netlify, Cloudflare i serwery Node.js

Tagi

#nuxt
#nitro
#vue
#full-stack
#api
#server-routes

Udostępnij

Powiązane artykuły