Nuxt 3 not only does it give us amazing tools to make building our frontend radically easier, it also gives us some awesome tools for building our backend.
Get notified when we release new tutorials, lessons, and other expert Nuxt content.
One of the great things about Nuxt 3 is that it’s a full stack framework.
Not only does it give us amazing tools to make building our frontend radically easier, it also gives us some awesome tools for building our backend.
In this article, we’ll take a look at how we can use server routes in Nuxt 3.
The most basic version of a server route is this:
export default defineEventHandler(() => 'Not so complicated.');
We use defineEventHandler
to create an event handler, and then directly export that so that Nuxt can use it.
All of our server files are placed in the /server/routes
directory. Routing here works exactly like page routing —it’s based on the filename. So if we put our server route in /server/routes/hello.ts
, we can access it by sending a request to /hello
.
But Nuxt 3 also gives us a shorthand for API routes since these are the most common type. Any route placed in /server/api
will automatically be prefixed with /api
. If we place our event handler in /server/api/hello.ts
, we can access it be sending a request to /api/hello
instead.
One of my favourite features is how h3 —the server that Nuxt uses internally —handles return values from defineEventHandler
.
Instead of needing to properly set content-type
headers and format our Response
object correctly, we just return whatever we need to return:
// Return JSON without any extra work!
export default defineEventHandler(() => ({
"name": "Chocolate",
"type": "Ice Cream",
"ingredients": ["Cream", "Sugar", "Cocoa Powder"],
"serving_suggestion": "In a waffle cone with whipped cream and a cherry on top",
"calories": 250
}));
But a lot of the time we need some sort of input to our endpoint —we’re not just sending the same JSON blob over and over again.
There are a couple main ways we can get inputs or arguments passed into an event handler:
If we have a server route at /server/api/icecream/[flavor].ts
we can use the flavor
route parameter to dynamically return the right object. We access it from event.context.params
:
import { IceCreamFlavor } from './types';
const flavors: IceCreamFlavor[] = [
{
name: 'Chocolate',
type: 'Ice Cream',
ingredients: ['Cream', 'Sugar', 'Cocoa Powder'],
serving_suggestion: 'In a waffle cone with whipped cream and a cherry on top',
calories: 250
},
{
name: 'Vanilla',
type: 'Ice Cream',
ingredients: ['Cream', 'Sugar', 'Vanilla Extract'],
serving_suggestion: 'In a waffle cone with chocolate syrup and sprinkles',
calories: 200
},
{
name: 'Strawberry',
type: 'Ice Cream',
ingredients: ['Cream', 'Sugar', 'Strawberry Puree'],
serving_suggestion: 'In a bowl with fresh sliced strawberries',
calories: 225
}
];
export default defineEventHandler(async (event): IceCreamFlavor | undefined => {
// Grab the parameter from the route
const { flavor } = event.context.params;
return flavors.find(flavor => flavor.name.toLowerCase() === name.toLowerCase());
});
We can also rewrite this handler to get the flavor from the body of the request using the readBody
utility from h3:
export default defineEventHandler(async (event): IceCreamFlavor | undefined => {
// Pass in the event object so we can parse out the body
const { flavor } = await readBody(event);
return flavors.find(flavor => flavor.name.toLowerCase() === name.toLowerCase());
});
Now we rename this route to /server/api/icecream.ts
, and send it a request where the body is { flavor: 'chocolate' }
and we’ll get the chocolate
object back!
That’s enough to get you started on server routes. They’re pretty straightforward, so you don’t need to know a lot in order to be productive with them.
But as always with Nuxt, there’s a lot more you can do with them if you need to.
Here are a few places you can continue your exploration:
readBody
, getCookie
, getRequestHeader
and many more.