Advanced Server Routes in Nuxt 3
In order to understand how to create advanced server routes in Nuxt 3 we must first understand the basics of server routes. Check out my previous post on Server Routes to get a better understanding of the basics.
The server/
directory allows Nuxt to create powerful backend APIs using the modern Nitro Engine
. Due to the power of the auto-import feature, Nuxt is designed to scan files in the following api/
, routes/
, middleware/
and plugins/
- to register APIs and server handlers.
Building an API with Server Routes (Advanced)
With the basics down we can focus on the advanced features of server routes. In this tutorial, we will be building a simple API that will allow us to create, read, update, and delete (CRUD) recipes.
Creating a Server Route in the api/
folder
Let's take a look at the example below from the Nuxt documentation:
import { createRouter, defineEventHandler, useBase } from 'h3'
const router = createRouter()
router.get('/test', defineEventHandler(() => 'Hello World'))
export default useBase('/api/hello', router.handler)
We will use this basic concept of creating a server route to create our CRUD API. Let's start by creating a folder structure in our server/
folder that will allow us to create our CRUD API.
server/
api/
recipes/
[...].ts
events/
create.ts
read.ts
update.ts
delete.ts
Add the following code to the server/api/recipes/[...].ts
file:
import { createRouter, useBase, defineEventHandler } from "h3";
import sampleData from "~/data/data";
import {RecipeTodo} from "~/types";
import createRecipe from "~/server/events/create";
import listRecipe from "~/server/events/read";
import updateRecipe from "~/server/events/update";
import deletearecipe from "~/server/events/delete";
const router = createRouter();
const _recipes = sampleData
router.get("/list-recipe", readRecipe)
router.post("/create-recipe", createRecipe)
router.put("/update-recipe", updateRecipe)
router.delete("/delete-recipe", deleteRecipe)
export default useBase("/api/recipes/", router.handler)
We have some mock data in the data/
folder that we will use to create our CRUD API. We will also create a folder called events/
in the server/
directory. This folder will contain the functions that will handle the CRUD operations.
Create Recipe
import {RecipeTodo} from "~/types";
import sampleData from "~/data/data";
import { v4 as uuidv4 } from "uuid";
export default defineEventHandler(async (event) => {
console.log(event)
// Handle request with body
const body = await readBody(event);
const { title, description }: Partial<RecipeTodo> = body;
// @ts-ignore
const _recipes: RecipeTodo = {
id: uuidv4(),
// @ts-ignore
title: title,
// @ts-ignore
description: description,
}
const status = getResponseStatus(event)
sampleData.push(_recipes)
return {
status,
message: "Advanced API Routes Success",
data: sampleData
}
});
Read Recipes
import {RecipeTodo} from "~/types";
import sampleData from "~/data/data";
export default defineEventHandler(async (event) => {
console.log(event)
// Sample data would be fetched from a database
const _recipes: RecipeTodo = sampleData
const status = getResponseStatus(event)
return {
status,
message: "Advanced API Routes Success",
data: _recipes
}
});
Update Recipe
import {defineEventHandler} from "h3";
import {RecipeTodo} from "~/types";
import sampleData from "~/data/data";
export default defineEventHandler(async (event) => {
// @ts-ignore
const { id } = event.context.params
const body = await readBody(event);
const { title, description, done } = body;
const _recipe: RecipeTodo | undefined = sampleData.find((recipe) => recipe.id === id)
if (!_recipe) {
sendError(event, createError({
statusCode: 404,
statusMessage: "Not Found",
}))
}
// @ts-ignore
_recipe.title = title
// @ts-ignore
_recipe.description = description
// @ts-ignore
_recipe.done = done !== undefined ? done : _recipe.done
return {
status: 200,
message: "Success",
data: _recipe
}
})
Delete Recipe
import {defineEventHandler} from "h3";
import sampleData from "~/data/data";
export default defineEventHandler(async (event) => {
try {
const { id } = getRouterParams(event)
const _recipe = sampleData.findIndex((item) => item.id === id)
if (!_recipe) {
sendError(event, createError({
statusCode: 404,
statusMessage: "Not Found",
}))
}
const _deleted = sampleData.splice(_recipe, 1)
return {
status: 200,
message: "Success",
data: _deleted
}
} catch (error) {
sendError(event, createError({
statusCode: 500,
statusMessage: "Something went wrong",
}))
}
})
We have created our CRUD API, and we can now access it on our pages and components using the data fetching method useFetch
or using Postman can check out the API.