DEV Community

Cover image for Nuxt Server Routes: Build a Full-Stack App with Zero Backend
Jakub Andrzejewski
Jakub Andrzejewski

Posted on

3 1 1

Nuxt Server Routes: Build a Full-Stack App with Zero Backend

When people hear "full-stack," they usually think of a separate frontend and backend applications. But what if you could build a complete full-stack app without setting up a separate backend? Thanks to Nuxt Server Routes, you totally can.

In this article, we'll dive into what Nuxt Server Routes are, why they feel magical, and how to build a simple full-stack app that talks directly to a database — no separate server needed

Enjoy!

🤔 What Are Nuxt Server Routes?

In Nuxt 3, anything you export inside the /server directory automatically becomes a server-side API route.

Let's take a look at the following example. Creating a file like /server/api/hello.ts:

export default defineEventHandler(() => {
  return { message: 'Hello from the server!' }
})
Enter fullscreen mode Exit fullscreen mode

And now visiting /api/hello in the browser returns:

{ "message": "Hello from the server!" }
Enter fullscreen mode Exit fullscreen mode

Mind-blowing simplicity, right?

Using Nuxt Server Routes comes with several benefits such as:

  • No need for a separate backend repo
  • Faster API development
  • Perfect for JAMstack deployments
  • Seamless TypeScript support out of the box
  • Great for "backend-lite" apps: blogs, dashboards, SaaS MVPs

Let's try it out in the real app example below.

🟢 Building a Simple Notes App ✍️

Imagine building a tiny "Notes" app where users can create and list notes. For our technology stack, we will use Nuxt both on the frontend and the backend and Prisma (ORM) with SQLite for super lightweight database.

1.Set Up the Nuxt App

npx nuxi init nuxt-notes-app
cd nuxt-notes-app
npm install
Enter fullscreen mode Exit fullscreen mode

2.Install prisma and initialize sqlite provider:

npm install prisma @prisma/client
npx prisma init --datasource-provider sqlite
Enter fullscreen mode Exit fullscreen mode

3.Set up your prisma/schema.prisma:

datasource db {
  provider = "sqlite"
  url      = "file:./dev.db"
}

generator client {
  provider = "prisma-client-js"
}

model Note {
  id    Int    @id @default(autoincrement())
  title String
  body  String
}
Enter fullscreen mode Exit fullscreen mode

4.Generate the Prisma client:

npx prisma generate
npx prisma db push
Enter fullscreen mode Exit fullscreen mode

Now you have a working database.

5.Create Server Route /server/api/notes/index.ts to handle read operation:

import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

export default defineEventHandler(async (event) => {
  return await prisma.note.findMany()
})
Enter fullscreen mode Exit fullscreen mode

6.Create Server Route /server/api/notes/create.ts to handle write operation:

import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

export default defineEventHandler(async (event) => {
  const body = await readBody(event)
  return await prisma.note.create({
    data: {
      title: body.title,
      body: body.body,
    },
  })
})
Enter fullscreen mode Exit fullscreen mode

7.Create Homepage /pages/index.vue:

<script setup lang="ts">
const notes = ref([])
const newNote = ref({ title: '', body: '' })

async function fetchNotes() {
  notes.value = await useFetch('/api/notes')
}

async function createNote() {
  await useFetch('/api/notes/create', {
    method: 'POST',
    body: newNote.value,
  })
  newNote.value = { title: '', body: '' }
  await fetchNotes()
}

onMounted(fetchNotes)
</script>

<template>
  <div>
    <h1>My Notes</h1>

    <form @submit.prevent="createNote">
      <input v-model="newNote.title" placeholder="Title"/>
      <input v-model="newNote.body" placeholder="Body"/>
      <button type="submit">Add Note</button>
    </form>

    <div v-for="note in notes" :key="note.id">
      <h2>{{ note.title }}</h2>
      <p>{{ note.body }}</p>
    </div>
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

Regarding deployment of our tutorial application, platforms like Vercel or Netlify automatically recognize the /server directory and there is no need to configure anything extra — just deploy! For database use something like Railway, PlanetScale, or Supabase instead of local SQLite.

📖 Learn more

If you would like to learn more about Vue, Nuxt, JavaScript or other useful technologies, checkout VueSchool by clicking this link or by clicking the image below:

Vue School Link

It covers most important concepts while building modern Vue or Nuxt applications that can help you in your daily work or side projects 😉

✅ Summary

Nuxt Server Routes feel almost like cheating — in the best way possible. If you're building side projects, MVPs, or even production apps that don't need a heavy backend, Nuxt is an amazing good choice.

Take care and see you next time!

And happy coding as always 🖥️

AWS Security LIVE! Stream

Go beyond the firewall

Watch AWS Security LIVE! to uncover how today’s cybersecurity teams secure what matters most.

Learn More

Top comments (3)

Collapse
 
nevodavid profile image
Nevo David

Gotta admit, having backend right in Nuxt makes setting stuff up less stressful for me lol

Collapse
 
jacobandrewsky profile image
Jakub Andrzejewski

Same for me! :)

Collapse
 
jay_bharadia profile image

Thanks for sharing. Good source of inspiration for learning and knowledge. I notice you are not using the nuxt prisma orm module, any specific reason/ issues using that?

ACI image

ACI.dev: The Only MCP Server Your AI Agents Need

ACI.dev’s open-source tool-use platform and Unified MCP Server turns 600+ functions into two simple MCP tools on one server—search and execute. Comes with multi-tenant auth and natural-language permission scopes. 100% open-source under Apache 2.0.

Star our GitHub!

AWS GenAI LIVE!

GenAI LIVE! is a dynamic live-streamed show exploring how AWS and our partners are helping organizations unlock real value with generative AI.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️