Introduction to Nuxt 3 Server Routes
Alright the gang. So for most of this series, we've been focused on writing code for the front end, the browser. And now in this lesson, I want to switch that to the back end a little bit and see how we can create custom server routes.
Nuxt uses a back-end server called Nitro, which allows us to make server-side routes or API endpoints for the application. If you're familiar with something like Node and Express to create an API, then you can think of it as a little bit like that, but the way that we create these server routes in Nuxt is a little bit different.
But first of all, why would we want to make those back-end API routes if we can already do things like fetch data in the script tag in our components on the front end in the browser? Well, it might be that you need to use sensitive data like a private key that you don't really want exposed to the user on the front end. So we might have a server route for that. Or it might be that you have an authentication system in place and you need endpoints for that.
Basically any code that you don't want to expose on the front end would use a server route for, because anything you put inside the script tag here inside a component, whether it runs on the server or in the browser, will ultimately be exposed to the front-end user.
Creating Your First API Endpoint
So then how do we create these server routes? Well, to do this in Nuxt, all we need to do is create a folder in the root of the application called server, and then inside that folder, we create another one called api. And then all of our API routes would live inside this folder, and the path to those API endpoints would be /api/ whatever we call the file.
So say for example, I create a file called ninja.js in here. We would have a function in here which would fire when a request comes in to /api/ninja.
Alright, so we need to export a special function in here for this route Handler. And the way we do that, if I say export default, and then it's a function built into Nuxt called defineEventHandler. So that's a function, and as an argument, it takes in a function. And this function is going to fire whenever a request comes in for this particular backend route. So in here, we can return something to the front end or whoever makes the call to this route. So we're going to return a JSON object, and we'll just have one property on there: a message. It's going to be a template string because later we'll output variables, but for now we'll just say something like, Hello ninjas.
Fetching Data from the Server Route
Alright, so now when a request comes into /api/ninja, all we're doing is returning this JSON object right here. So then let's go and call this from somewhere. I'm going to go to the about page, and we will make a call to the endpoint inside this script from the front end. So what we need to do is use the useFetch function. So I'll say const { data } and that's going to be whatever data comes back, is equal to await useFetch, and then inside here the endpoint is just /api/ninja, which is the name of this particular endpoint right here, this file. Alright, and then what do we want to do with the data? Uh, we shall output it up here. Let's just do a div and output the data. Alright, so let's test this out.
Okay then. So now if we go to the about page over here, we should see the data over here on the screen, which we do. So we're sending a request to that endpoint and we're getting that JSON data back. Awesome.
Handling GET Request Query Parameters
Alright, so inside this function right here, we also have access to an argument which is the event object, and that contains information about the request, if you like. So what I could do is when I make the request, for example, I could add on some kind of query parameter right here, like a name. So I could say ?name=Mario. Right? And then using this event object, I could access that query parameter and any other query parameters we added to it.
So how do we do that? Well, pretty simply, I can come down here and let's just say, handle query params like so. I could say const and destructure whatever parameter I want, so name in this case, that's what we called it over here, name, and we set that equal to useQuery. So this is a built-in function we can use, and then pass in the event, this object right here. So it will use that to get the query parameters. That returns us all the query parameters, but we want to destructure the name one so that we have that. And then what I'll do is just output that name here in a variable. So dollar sign curly braces and name. So now it should say Hello Mario because the query parameter value is Mario.
And now if we go to the about page, we can see Hello Mario. Awesome. So that's worked.
Handling POST Requests and Body Data
Alright, so we can also use these server functions to accept POST requests and we can also extract any post data from the request as well using this event object again. So first of all, let's construct this POST request right here because this at the minute is just a GET request. So to make this into a POST request, we can add on an options object right here and then we can say that the method is going to be post.
Now we also want to send some data, some body data. So we can add on a body property to send that, which is an object with some different key-value pairs. So I could say for example, I want to send an age property, and the age is going to be 30. So this is body data that we're sending with the POST request, and we can extract that data over here using this event object again. So what I'll do is another comment right here to say handle post data. And then we'll destructure the age. const { age } is equal to something, and it's called age because that's what we called it right here, age.
So we set that equal to await useBody like so. So this function right here is asynchronous and we use the await keyword before it, otherwise we won't get the actual value here. So what we need to do is turn this function into an asynchronous function in order to use this await keyword, and we can do that by saying async right here. And now the error goes away. And inside here, we just pass in the event object again. So now we're extracting the age as well as the name from the query parameters, and I'm going to output it right here as well. So exclamation mark, then I'll say, you are, and then the age, and then years old, like so. So we should get Hello Mario, you are 30 years old because that's the data we sent over here: Mario and 30.
Okay, so let's save this and try it out. Okay, so once more, if we go to about, we can now see Hello Mario, you are 30 years old. Awesome.
Use Case: Securely Calling a Third-Party API
So I said earlier that one of the reasons we might want to make server routes is because we're using some kind of third-party API which requires a private key and we don't want to expose that private key to the front end. So I'm going to show you how we can work with that kind of example.
So I'm using this API right here, Currency API. You can sign up for free and use the free tier, which allows you x amount of free requests per month. Don't know how many it is, but there is a free tier. And this requires a private key when you're making requests. So I've already signed up for an account, I've got my private key, and this is the endpoint that we're going to use right here to get information about certain currencies. And you can see as request parameters we can also add on the API key, which is mandatory. We need that otherwise it's not going to allow us to get the data back. So, I've already got that and also we can specify which currencies we want to get information for. So let's go back to the code now and try setting this up.
Implementing the Server-Side API Call
Alright, so now let's try fetching the data from the API. Down here we have a comment already: API call with private key. And I'm going to say const { data } because we get data back from this API, is equal to await, and then we don't use useFetch in our server routes; instead, we use $fetch. Now, useFetch is just a wrapper for $fetch on the front end. We can use that; it makes it simpler to use. However, when we're using server routes, use $fetch. Okay.
So in here we can pass in our API endpoint. So I'm going to paste this in. So we're using this endpoint, which is slightly different to the one I showed you. It's just going to get us some latest information instead of specific currencies. And then right here, we have a query parameter, question mark, and it's the API key. And this is the API key right here. Now, I'm just hard-coding the API key into this fetch URI right here, but later on, I'm going to show you how we can put this in an environment variable in Nuxt applications. But for now, let's leave it here. And if I save this... In fact, what we want to do as well is we just want to return the data. So let's get rid of that and say we want to return the data, which is this stuff we get back. So we're still making that request from the about page over here, and it no longer needs to be a post request. So let's get rid of this. We no longer need to have on this query parameter as well, so let's get rid of that. And in fact, if we go back over here, I'm going to comment all of this out just so it doesn't get in the way.
So we have now just this API call. We're making a request right here and we should get the data back because we're just sending the data back right here, and then we're going to output it in the template. So let's save this and give it a whirl. Okey-dokey. So if we go to the about page now, we can see that we get all this data back. It all looks good. Well, it doesn't look good, it looks like a load of gobbledygook, but we can see we get that data back from the API. These look like the country codes right here and the value, I think that is basically the exchange rate as compared to the US dollar. I think that's the default kind of code that it compares to.
So at least we know this is working and this is one of the reasons we would use the server route: because we want to protect that private key to the third-party API. We don't want to expose that because then if anyone gets it, anyone can use it and use up our quota, if you like. So that's one of the reasons we would use server routes.
Now, in the next lesson, I'm going to show you how we can make dynamic server routes. So you know, like we had dynamic pages on the front end, if we go to product/some-kind-of-id, well we can do the same kind of thing on the back end with server routes as well. So we'll see that in the next lesson.