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.
Defining Server Routes
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.
Return Values
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.
Getting Inputs
There are a couple main ways we can get inputs or arguments passed into an event handler:
- Through the route itself via route parameters or query parameters
- From the body of the request object
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!
Advanced Server Routes
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:
- Check out the list of h3 utility methods —lots of very helpful things in here like
readBody
,getCookie
,getRequestHeader
and many more. - Learn about server middleware —functions that run on every request that you can use to check or modify the request object.
- Play around with creating Nitro plugins (aka server plugins) to extend the behaviour of your backend.
- Check out the advanced usage section of the docs. It shows how you can use your own custom h3 router, stream responses, and leverage the storage layer for custom caching.