DEV Community

Alex Spinov
Alex Spinov

Posted on

Nuxt Has a Free API You Should Know About

Nuxt is more than a Vue framework — it has a powerful server engine (Nitro) that turns it into a full-stack platform. API routes, middleware, caching, and auto-imports make backend development seamless.

Server API Routes

Create API endpoints by adding files to server/api/:

// server/api/users.get.ts
export default defineEventHandler(async (event) => {
  const query = getQuery(event)
  const users = await db.user.findMany({
    take: Number(query.limit) || 10,
    skip: Number(query.offset) || 0
  })
  return users
})

// server/api/users.post.ts
export default defineEventHandler(async (event) => {
  const body = await readBody(event)
  const user = await db.user.create({ data: body })
  return user
})

// server/api/users/[id].get.ts
export default defineEventHandler(async (event) => {
  const id = getRouterParam(event, "id")
  const user = await db.user.findUnique({ where: { id } })
  if (!user) throw createError({ statusCode: 404, message: "User not found" })
  return user
})

// server/api/users/[id].delete.ts
export default defineEventHandler(async (event) => {
  const id = getRouterParam(event, "id")
  await db.user.delete({ where: { id } })
  return { deleted: true }
})
Enter fullscreen mode Exit fullscreen mode

Server Middleware

// server/middleware/auth.ts
export default defineEventHandler(async (event) => {
  const token = getHeader(event, "authorization")?.replace("Bearer ", "")
  if (event.path.startsWith("/api/protected")) {
    if (!token) throw createError({ statusCode: 401 })
    event.context.user = await verifyToken(token)
  }
})

// server/middleware/log.ts
export default defineEventHandler((event) => {
  console.log(`${event.method} ${event.path}`)
})
Enter fullscreen mode Exit fullscreen mode

Caching with Nitro

Nuxt's Nitro engine has built-in caching:

// server/api/stats.get.ts
export default defineCachedEventHandler(async () => {
  const stats = await computeExpensiveStats()
  return stats
}, {
  maxAge: 60 * 60, // Cache for 1 hour
  swr: true,       // Stale-while-revalidate
  name: "stats"
})

// Cached functions
const getSettings = defineCachedFunction(async (key: string) => {
  return await db.setting.findUnique({ where: { key } })
}, {
  maxAge: 300,
  getKey: (key) => key
})
Enter fullscreen mode Exit fullscreen mode

useFetch — Client-Side Data Fetching

Nuxt's composable for fetching from your API:

<script setup>
const { data: users, pending, error, refresh } = await useFetch("/api/users", {
  query: { limit: 20 },
  transform: (data) => data.map(u => ({ ...u, fullName: `${u.first} ${u.last}` }))
})

// Lazy fetch — doesn"t block navigation
const { data: stats } = useLazyFetch("/api/stats")

// With watch — refetch when params change
const page = ref(1)
const { data: posts } = await useFetch("/api/posts", {
  query: { page },
  watch: [page]
})
</script>
Enter fullscreen mode Exit fullscreen mode

WebSocket Support

// server/routes/_ws.ts
export default defineWebSocketHandler({
  open(peer) {
    console.log("Connected:", peer.id)
    peer.subscribe("chat")
  },
  message(peer, message) {
    peer.publish("chat", message.text())
  },
  close(peer) {
    console.log("Disconnected:", peer.id)
  }
})
Enter fullscreen mode Exit fullscreen mode

Server Utils & Auto-Imports

// server/utils/db.ts
import { PrismaClient } from "@prisma/client"
export const db = new PrismaClient()
// Auto-imported everywhere in server/ — no import needed!

// server/utils/auth.ts
export async function verifyToken(token: string) {
  return jwt.verify(token, process.env.JWT_SECRET)
}
// Available in all server handlers automatically
Enter fullscreen mode Exit fullscreen mode

Key Takeaways

  • File-based API routes with HTTP method suffixes
  • Nitro caching with SWR support
  • Server middleware for auth, logging, rate limiting
  • useFetch/useLazyFetch for type-safe client→server calls
  • WebSocket support built-in
  • Auto-imports for server utilities

Explore Nuxt docs for the full server guide.


Building web scrapers or data pipelines? Check out my Apify actors for ready-made solutions, or email spinov001@gmail.com for custom development.

Top comments (0)