In order to implement the images functionality, we first have to create an API endpoint which will fetch the images from Unsplash. For that, we're going to be using Hono. But before we add Hono, let's explore how we learned to create API routes in the first or second chapter of this course. So let's repeat that knowledge of ours. Inside of the app folder create a new folder called API and inside of here simply create another folder called hello and then create a new file route.ts so this is how we create API routes inside of Next.js and then in here we would write export constant then the method which in this case would be get and then we are going to return response JSON Hello world.
So something like this and Now if you go inside of localhost 3000 Slash API slash. Hello. There we go. You get back hello world. So this is fine but it's missing a lot of things in my opinion.
First of all, I'm okay with folder and file based routing for my client routes But I just think it's too tedious and just not nice enough for a good backend. So because of that, I'm going to be implementing something called Hono. So Hono is a web application framework. It's fast, lightweight and it's built on web standards. And what's great about it is that it has support for any JavaScript runtime, which means you can run this on Cloudflare, Fastly, Deno, Bun, AWS or Node.js.
And you can also easily inject it inside of Next.js, for example. What's also great about it is that it has batteries included. In our case, what matters is that it has third-party middleware for Auth.js, or previously called NextAuth. It also has delightful developer experience and clean APIs and first-class TypeScript support. This is great because we're going to turn our API into an RPC.
You're going to see what that is in a second. And then we're going to have excellent developer experience in combination with React Query and Hono, which is something which is simply not possible if you want to use this kind of API routes. This is completely fine. You can see that it works as intended But I kind of want to give you a way of creating more in my opinion more scalable API inside of Next.js. So let's go ahead and click Get Started right here.
And now we're going to kind of explore Hono. So click on Get Started, go inside of the documentation and select Vercell right here. So Vercell basically means for Next.js. That's how you can read that. Right?
So first things first, we can skip step one because we already have our app. And then let's go ahead and let's install the package called Hono. So I'm going to go inside of my terminal here. And I will simply run bun add Hono and then run your app again. And then we're gonna go ahead and create for the app router this page inside of the app folder API and then route route.ts.
Let's go ahead and do that. So we're gonna go inside of the API folder, we're gonna close our existing hello which I've just created and Then inside of here. We're gonna create a catch-all Route like this and then inside of that a route dot DS So so far very similar to our hello, right? Except this will catch every single API route, right? So if I write something that's not hello, it's going to fall back to going inside of this.
That's what we are doing with this catch all route here, right? So inside of the route, let's go ahead and let's import HONO from HONO and let's import handle from HONO vs CEL. Let's export const runtime and for the runtime you can define whatever you want inside so they use the edge example but you don't have to use edge you don't even have to define anything right what I want you to write is a node.js So I want you to be able to deploy this application everywhere, right? I don't want you to depend on edge-only services. So I'm going to leave a slight comment here.
Revert to edge if planning on running on the edge. So that is a possibility for us because we're going to be using Drizzle ORM which natively supports the edge. So if you want that you can just go back here, enable the edge and you will get all the features of the edge. But I just want to keep it simple for this tutorial. I want us to be able to deploy this thing on VPS which costs $5 if needed.
So that's why we're gonna change the runtime to node.js here. Then let's define our app. So we're gonna call this new Hono, and then we're gonna chain base back to slash API, which is basically this folder right here. And finally let's write our route now. So I'm gonna write slash hello here.
Oh so we already have slash hello so let's write something different. Slash test and now inside of here the second argument is going to be the controller so just like in Express if you remember using Express. So the controller gets the context, whoa my apologies, and then we have to return context.json test HonoTest and finally right now this will not work That's because route.ts is a reserved keyword and it expects a certain export. Either get, post, put, patch or delete. So it expects a specific HTTP request.
So what we are going to do is we are going to hijack the get request the same way we wrote it inside of our normal API route but we are going to use the handle which we've imported from hono slash vercel so now this Hono app will handle the hijacking here. I'm explaining it using the word hijacking because I just feel like it's the right word to use but hijacking in this case has no negative connotations, right? I'm just using a specific language to explain what's happening here. Maybe overriding would be a better word to use. So let's go ahead and try it out now.
Let's save this. And now let's go to localhost 3000 slash API slash test. So the same way we just attempted to go to slash hello, let's revisit our app. I'm gonna go inside of here. We are currently on api slash hello and now I'm gonna change to api slash test and there we go test hono test.
So now we can write our api routes inside of Next.js using in my opinion a more scalable and a more familiar way. If you've ever used Express.js, this is probably more familiar to you than file and folder based routing. Because again, file and folder based routing for me, it's completely fine for client side. Like this is fine for me. But back end, I feel like it's more scalable if written like this.
So what I want to explore now are all the quirks that Hono has. And then we are slowly going to go ahead and learn how to, well, build, how to use Hono for larger applications and finally how to turn our routes into RPC type of routes. So let's go ahead and see if there are any instructions further here. So I'm gonna go ahead and go inside of the menu here. We finished getting started.
So let's go ahead and click here. So let's explore if there's anything useful here. Of course it is, but I want to go specifically inside of routing perhaps. There we go. So this is what I kind of wanted to see.
The path parameter. So that's kind of the most basic thing we expect. Let's go ahead and check it out. So I'm going to go ahead and write another route. So app.get user and then I'm going to write colon slash name.
And here's an interesting thing that will happen now. So we're going to go ahead and return C.json here. Let's just write hello world for now. But let's say we want to fetch this name from our API route. So what we can do is get const name c.request.param and this is the cool thing as you can see we have type safety here so if we change this to name two whoops you can see how it recommends name two if I change this to name this should get an error I believe.
Well not an error but it does not auto-complete it. Right? So let's try it out. So I'm gonna go ahead and write username will be the name value like that. Let's try it out now.
I'm gonna go inside of here, I'm gonna change the API to slash user slash Antonio and there we go. Username is Antonio because my last parameter here is Antonio. We can of course handle the same thing with the normal API but let's see how complicated or not complicated it is. So instead of user I'm gonna go ahead and create a new folder this will be called project for example and then inside of here we would have to create a new project name and then we will have to create a route.ds like this and then inside of here we would have to create a new project name and then we will have to create a route.ds like this and then inside of here we will get export const get and inside of here we would get I think the request which would be a type of next request. And then we would get the response, which would be a type of next response here.
And then in here, we would have to destructure the params, which is part of the response, I believe. So let me just collapse these two. You don't have to write this with me we're just looking at the difference between how Hono and Next.js works here. So if you want to get the project name in kind of the same logic as... Look what it took us to get the name here, right?
So just a very simple type safe way to get it because we have auto-completion but in here we would manually have to destructure this, right? So we would have to write params and then we would have to write params is an object which accepts the project name which is a type of string and Then inside of here. I will get the project name, which is params dot project name and then return response JSON project name is project name like this. So I would have to now go to API slash project, project name. Let's try it out.
So instead of slash user, it would be project. And then this will be, for example, editor. There we go, so project name editor. We just got the exact same thing, but you can see that it's not exactly type safe. So I manually had to write this, right?
And I only know this because I named my folder inside of square brackets project name. But if I made a typo here like project Nama like this nothing will warn me that this is not working. I just get a faulty API right Nothing is warning me that something is wrong here. So that's why I prefer this way of working. Now if you change this to name too, nothing is warning you here either but that's only because we have not gotten to strict validation which also supports the Zod package.
So I simply think this is a way more scalable way and way more secure way of writing API routes. So now let's go ahead and do the following. Let's remove the hello route, We no longer need it and let's remove the project route Let's simply keep the catch all route with our Hono here and let's continue exploring What else Hono has in store for us? So as you can see you can also Chain parameters here. You can do posts ID comment and then comment ID and using the same method you can destructure multiple things right so you don't have to call the specific one you can also do params and then simply call all params like this and then inside of here you would get params and you can see the auto completion params.name so if I change this to name2 now you can see the error so name doesn't exist but name2 exists perfect So you can see just how more safe Hono is.
It also has optional types for example and you can also do regex. Perfect. And you can also write chained routes which is something we are going to need in a second. And this is what I want to explore next. It is the grouping, right?
So, but before we do it, there is one thing that I kind of want to find in this documentation because they have a very nice explanation of this. So let me just go ahead and pause until I can find this. So here it is. Inside of the documentation go ahead and find the guides and then best practices. So in here they instruct you to not make controllers when possible.
So this is a practice that I have from Express.js. This is the first thing I wanted to do when I added Hono. So for example, when we have a route here, it might be an instinct of yours to abstract this into a separate method right so const something this would be a controller actually right and then you would add this here and then you would pass the controller right something like that well they instruct you not to do that and the issue is related to the great types which we've just tested right So the problem is even if you manage to define the correct type context it simply can't infer the path param here so you would run into issues inside of here but if you write the controller in line then it works just fine So that's the issue with separating methods inside of controllers. If you really really want to use a controller you can do that using HonoFactory, right? But I have, you know, Hono in my personal website and I never have any need for this.
Instead what I do is this. I use groups to build a larger application. So let's go ahead and try this method of building. So this is what we are going to do now. We can remove this controller and we can remove this.
Just leave the test right here. Let's say we want to create a bunch of routes for our user. What we would do is we would create a new file called user.ts. We would import Hono again from Hono and then we would define the app again new Hono and then app.get slash let's go ahead and get the context here we can return c.json user get like this and then we would go inside of route.ts we would import that user let's just see I forgot to add export default app inside of the user.es file. So don't forget that.
And then you can import this. And then what you would do is instead of app.get, you would do app.route. And then you would add a prefix, for example, users, and simply add the user import like this. And then you can go ahead, for example, and try and go to API slash users. And there we go, user get, Right?
So that's how you would group your app. So now you don't have to write everything in one file. Instead you can go ahead and you know write different things here. So you can go ahead and add the name here and then you will get the name or params using cRequestParam like this and return c.json username would be param.name. So again even though we don't have the slash user prefix here we defined it inside of here So this would actually be user instead of users.
So let's go ahead and fix that. Let's go to slash user. There we go. It returns username. And if I go ahead to slash user slash Antonio, it returns back username Antonio.
So the same thing that we had previously, but now it's more in this neat way, which allows us to have this user file pretty much anywhere you want in your project. You don't have to keep them here. You can create, you know, inside of the features, you can create a new folder called users and then you can keep that inside of there and then simply import that from there and pass it here. So that's what Hono allows us to do and that's what I prefer. Great!
So now what I want to do is I want to learn how to create an RPC. Right. So let's go or perhaps we can do validation before that. So let me just go ahead and show you validation because we are going to need this. So let's go ahead and use the with Zod validation.
First of all we need to add Zod and we also need to add the Zod validator middleware. So we're gonna add those two. Let's go inside of the terminal and let's do... Whoops... Bun add Zod and also add Hono slash Zod dash validator.
There we go. Let's do bun run dev again and we can actually use this user.ts file. So, so far we've been using this c.request.param here which gives us the param's name string. But if you want to you can also use the Zod validator here. So, what you would do is you would import the z-validator from HONO, Zod validator, and then inside of here, everything before the controller here is the middleware, right?
So you can chain as many middlewares here as you want and let's add one the Z validator so we would we are going to validate our params here and then we will simply import Zod So let's go ahead and add z. I think it's the object. Yes, name is a type of z.string. And let me just format this. There we go.
So app.get, the first argument is slash name. The second argument is my Zod validator here, which in the first, first param, sorry, in the first, yes, the first param accepts the word, the keyword param, because you can only validate certain stuff like cookies, form, header, JSON, param and query and then you manually define that name should be a string. And then instead of using this you would use crequest.valid like this and then inside of here also you have to define what valid field you're looking for. So now you get the exact same thing, but it's validated via Zod, right? So if I change this to number, for example, you can see that this is now a type of number.
Right, so this will help us because we're going to share our, share our, you know, a backend using RPC to React query. So I just wanted to show you that. So make sure you have this installed, make sure you have Zod installed. I'm just going to revert this now to what it was before, you know, without the Z-Validator, because I just wanted to show you how it's going to be used. But for now, I just want to keep it simple like this.
So Zod validator will make more sense once we add the post requests so we can define the form or the JSON so we can be more specific with our data which the API will accept. So I just wanted to show you that that's the possibility to do with Hono, it's a very powerful tool. But what I wanna build now is an RPC. So let's go ahead and learn how to do that. Instead of guides, they have the RPC option.
And here's the thing that we want to do. First of all, the RPC is a feature which allows sharing the API specifications between the server and the client. So in React query, once we add it, we are not just gonna write plain routes. Instead, we're gonna be using very strict type safe RPCs. So what we need to do is we need to define the app type of our entire API.
So let's attempt to do this ourselves. If you go back inside of your route here, you might be tempted to do export type, app type and simply write type of app. Like this, right? That should technically have all the information because we chained the user here. So, but if I hover over this, you can see that it's not exactly true.
So we have blank environment and blank schema. The only thing that this type knows is that it begins with slash API but that's not enough. So here's what we have to do first. What we have to do is learn how to chain our elements. So let's define a route, a constant called routes and then inside of here go ahead and assign the app.route.
So now when I hover over the routes you can see that something has changed. It no longer has a blank schema. So that's a good start here. Let's go ahead and keep this. But now we still have a problem because we can't see any user information.
Why is that? Well, because we have to do the same thing inside of this group user. So instead of manually doing app.get every time we have to chain it here. So I'm going to chain this to Hono. So just make sure you remove the semicolons, right?
So I remove the semicolon here and then I will simply chain it here like this. And now if you hover over this app, you can see all the information. So the slash has a get, which returns this output in a format of JSON. The slash name has the input, which is the param and name, which is a type of string. So you can see this is very specific API information and now that we've done this if you hover over the app type you can see that you now have very specific information for each of your routes.
So slash API slash user now has very specific information here. Same thing for this. And later you can also go ahead and handle errors this way. So for example, if something happens, right, let's go ahead and just write true for now. You can return C.json error, something went wrong and you can pass in a 401 or 400, right?
And by default, everything else will be considered a 200, right? So you didn't have to write this. So now if you hover over the app type, you can see that you can also get, well we can't because there's too much information, but can I try hovering over this? So you can see how the output now is separated into two different options. One is a status of 400 and then there's another one.
So both of these are hidden from me because it's too big to render in this little tooltip. But you can see how Hono is in my opinion simply a way more scalable solution than the existing app routes. Perfect. So just make sure that you have the final app type here which has the proper information which will mean that you have properly you know chained your elements here and chained them here. And if you're wondering, how did I figure this out that you have to do it that way?
That's because they instruct you to do it this way. So you can see that they define the route here and then they define app.post and then you would chain the other ones here. And if you go ahead and scroll all the way down here to using RPC with larger applications is the same thing. So our authors.ts is our user, right? So you simply chain one below another and then export all of that.
And then finally you create the routes and then you chain your routes simply here and then your app type will be functional. So that's how we're gonna do that. So later we're gonna have something like this. We would have app.route, routes.user. Then inside of here we would have projects and projects.
And then inside of here we would have projects and projects, right? And then inside of here we would have, you know, the same thing but for projects. So that's how we are going to do that in the future. Great great job! So you've learned how to set up Hono.
What we're gonna do next is we're gonna use this knowledge to create our first API route using React query in combination.