Nuxt 3 Server Routes API (Advanced)

Nuxt 3 Server Routes API (Advanced)

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.