
NO β this is not a tutorial for cloning Discord Nitro on Nuxt3 π. Although that would be a great project to do someday π€. In this article, we are going to write some backend server code on Nuxt3.
Whaaaatttttttt?π± But isnβt Nuxt3 a front-end framework? How and why will I write my backend code on my front-end?
To create a full-stack application, we need a server-side code and a client-side code. The traditional frameworks bundle their code, and this file is sent whenever the user makes a GET '/'
request on your server. We could have a Flask backend with a Svelte frontend to do this. Problems might arise while trying to bridge the two technologies together.
What if I tell you that we can do this on the same codebase?
Yes β Nuxt3 introduces the Nitro server where you can write your server-side code in your project and deploy it too!
Getting Started
Like any other feature that Nuxt3 provides out-of-the-box, we need to create a server/
directory in the root folder of your project. Any code inside this will be rendered by the Nitro server for the backend. Let us create 4 different routes that will illustrate every concept required to know the basics of Nitro.
1. GET '/api/users/β
β this route will fetch all the users we have registered with us.
2. POST '/api/users/'
β this route will create a new user with the username.
3. PUT '/api/users/{id}'
β this route will change the online status of the user.
4. DELETE '/api/users/{id}'
β this route will, no brainer, delete the user.
Creating our fake DB
We wonβt be dealing with any local or cloud database here, so letβs create a folder inside the server/
directory db/
and a TypeScript file called index.ts

Creating our server routes
Based on the 4 routes listed above, we have to create a structure that resembles it. In Nuxt3 projects, it is very important to maintain the directory structure.
|- server/
| |- api/
| | |- users/
| | | |- [id].ts
| | | |- index.ts
| |- db/
| | |- index.ts
|- app.vue
|- nuxt.config.ts
|- package.json
|- package-lock.json
|- README.md
|- tsconfig.json
The db/
route is for the fake database that we have. The other folders are slightly self-explanatory. The server/api/users/index.ts
calls any route associated with /api/users
while server/api/users/[id].ts
is used where is a path parameter /api/users/{id}
.
#1 β GET β/api/usersβ
For the 1st route, we place our code in the /api/users/index.ts
file because it does not accept any path parameters. We have to default export a function named defineEventHandler
which would handle any incoming route based on the directory structure. We have an anonymous function with the event passed down as the parameter. This event parameter contains all the required details about the incoming request.

e.req.method
fetches the name of the incoming HTTP method and we can act according i.e. to return the users.
#2 β POST β/api/users/β
Now, since the URL of the request is the same, we will be working on the same index.ts
file route.
Prerequisite: you need to run the following command:
npm i uuid @types/uuid
.

If the incoming request for that URL is a POST request, we can extract the body by await
ing with the useBody()
function where we pass the event object. The rest of the code is self-explanatory. The username property is asserted, pushed to our mock database and returned the user object. Note the code for sending the error.
#3 β PUT β/api/users/{id}β
This requires a path parameter and hence goes to the [id].ts
file. We can start by creating a function that would try and find the user from the database and return the object along with the index of that object in the array. If the user is not found by the ID, we can simply throw a 404 HTTP error.

This time, I can use a switch case instead of two if
s to write my code.

If the incoming request is a PUT method, I am extracting the ID from the event object by using e.context.params
. With this specific ID, I am searching for the user from the database with the function we wrote above. The user object returned to us will never be null
or undefined
. We change the value of the online status and return the updated user object.
#4 β DELETE β/api/users/{id}β
In the same file, under the case "DELETE"
, we can code the logic for deleting the user from the mock database.

Based on what we have done before, this code is too self-explanatory. We find the index of the required user and splice it from the array in the db
object.
AND WE ARE DONE WITH OUR SERVER CODE! π
How to use this on the front-end? Simple β we can use useFetch('/api/users')
or $fetch('/api/users')
on your front-end to make server requests to these routes.
Conclusion
Having the server code and client code on the same codebases minimizes any effort to bridge the two together and eliminates any problems that may arise as Nuxt3 bundles the server along during deployment. The directory structure provides a very systematic approach and is very easy to read and understand working as a team.
Follow my Nuxt3 Tutorial List to master the basics of using Nuxt3. Thank you, and I hope you enjoyed and learnt something. βοΈ


