Production-Grade Next.js

Setting Up API Routes

Scott Moss

Scott Moss

Superfilter AI
Production-Grade Next.js

Check out a free preview of the full Production-Grade Next.js course

The "Setting Up API Routes" Lesson is part of the full, Production-Grade Next.js course featured in this preview video. Here's what you'd learn in this lesson:

Scott live codes API routes using next-connect and middleware error handlers to handle users creating new docs.


Transcript from the "Setting Up API Routes" Lesson

>> So yeah, now we have the Getting Started here, which is the folder that we created. If I click this, you can see that now we have that second scenario, now there's a folder ID and a URL, so we handle that scenario. And we actually got the folder that's here.

And then here's the documents that we created, that one document Start Here, that's on sign up. So if I click that Then what happened? There we go. So now if I click that I'm here. And now I'm actually in the editor where I can write some stuff, right?

So I can do some things here. And yeah, this is an editor. It's a blog base editor, you can do stuff like this. You hit tab, you can add a code block if you want to, all different types of stuff. And then it just auto saves. So if you're interested in that code, it's in, where did I write this code?.

This is in components editor. It's not a lot of code, it's like 120 lines of code for an editor. So that's the editor that we get. And, yeah, sometimes it'll do this because of hot reloading. So we handled that, everything's looking good. Now we need to actually do the mutations of making these buttons work here.

So if I want to be able to click here to create a new document and click here to create a new folder I want those to actually work. Now we can't actually do mutations on the server side, right? We can't write code on this. To query the database, we wrote database queries on the server side props, to get information from the database.

But to actually mutate the database, we're gonna have to do that on the client's side because it's responding to a click, it's responding to an event. So that has to happen on the client's side. So, what I was telling you before, it's not recommended for you to use your own API inside your next.js app.

That's mostly for querying data. For mutating data, you have to. [LAUGH] yeah, you pretty much have to use your API, if you want to do that, if you're responding to events. So that's what we're going to do. The next one is client side mutations. So like I said before, we talked about using the database queries in the server side props.

So for mutations, we need to do them client's side. So first, we need some API routes to actually hit, we don't have any API routes, we actually have to make them so we can actually use them. So that's what we're going to do. And this will be the first API routes that we've made, we have that one with the auth, but the next auth handler just handles it for us.

So we're going to do one here, or actually a couple here, that we just make ourselves. So we need to be able to handle a put on a editor. So if you try to go into editor right now and type things in, you'll see it'll say auto saving, but it doesn't actually get saved, right?

Because we haven't hooked it up, it's not doing anything. So if I refresh this page, it's just going to go back to what it was, cuz there's no mutation here. So we need to do that. The other things we need to do is be able to click here and then make a new folder.

Right now it opens up a modal and I don't really think it does anything, or if it does, it definitely breaks. And then here we need to be able to create a new document as well. So we got those three mutations we need to make. So that's what we'll do.

In order to do that, we're gonna be using a library called next-connect. It's very much like express. If you've ever heard of express then you definitely know what connect is. Express is based off connect, and actually used to use connect as a dependency and now it doesn't use connect as a dependency anymore, but it's still the same API.

So it's pretty much the same thing. We're going to do that to create our routes because it actually helps us create routes a lot simpler. If we didn't use next connect, we basically have to create our own router inside of every function. We have to look at the request object, determine if it's a put, post, get or whatever and route off that and we gotta do a lot of low level stuff.

Whereas next-connect just gives us that API that we're used to, just being able to do this. So, that's what we're gonna do. So we're gonna make one for the document to be able to put a document, that's gonna be an update. And then we're gonna do one for the doc index, which is basically being able to post, which is create a document.

So these are two different, this one requires an ID, right, cuz we were updating a document so we need an ID. This one is saying just create a new document, we don't need an ID just make a new one. And then down here with folders, we're gonna say we want to be able to post and create a folder.

So let's do that. So what we'll do here is head over to our API. And then we're gonna make some new folders here, so I'm gonna say New Folder. I'm gonna put, what did I call that, just doc, I guess. So we'll start with doc. We'll say doc, and then we're going to do the one for, let's do the create first.

So index.ts for the create here. And I'm gonna import NC from next-connect. I'm gonna import our middleware from, where's that middleware, I know it's on here somewhere, there we go, middleware/all. So I'll just call that all, or I'll just call that middleware. Like that. And we'll see what else we need.

Wait, I need the database stuff, I think. Yeah, I need those helpers. So let's get the helpers from Where are they at? I think they're in db. Where's db? There's db, okay. And then we'll get the doc helper, cool. So then we'll say const_handler. And that's just gonna be I wanna say next connect like that.

And then what you can do is you can pass in some options here. The one that we want is gonna be an onError. So I have a error handler and I want to say that one's in the middleware as well. So we'll do this from middleware/error. I was gonna call it onError, like that.

So this error handler is just, if we get a look at it, it's nothing really special. It basically just logs the error and sends back a 500. I mean, this is where you would probably do your reporting to like Century or whatever error reporting system that you have.

You would just put that here instead, and then you get the actual error. This is a great way to capture some error. So basically just wraps your handler in a try catch and it passes the error here and then now you can handle it however you see fit.

What I like to do is I like to create custom error classes that have their own status codes on them and stuff, and their own messages. And then when you need to throw an error, you just throw that new custom class that you made. And then somewhere like this in function, you could check to see, is this an instance of unauthorized error?

Is this an instance of payment late error? Whatever it is, and you can decide on what you want to do to figure out what message you want to show and stuff like that. So that's just my flow that I've been doing for a while. So take it or leave it.

Yeah, so we've got our handler, and then we want to do is set up some routes. So I'll say handler dot, what is this? This is for the index of doc. So we're going to be posting an index obviously, .post, like that. And we're going to get a function.

Okay, so then async, this, we're gonna get a request, we're gonna get a response. And then if we're posting something, actually, I think I have some TypeScript. I got like a custom TypeScript thing I have here, I think it's called request actually, let me see. It's that one, there we go.

So we got this request object here. It's just some custom one that I made, we're gonna look at it. It basically just extends the next API request and just adds a db client and then the user. So we get some nice type checking there, that's all it does.

So what I can do here, is if I want to create a post, I could say cool, not a problem. This middleware already connects to the database force, so we're good. If this function is running, we'll already connect to the database. And we're already authenticated as well, so we're good.

That middleware handles all of it, we can go look at it. It basically uses the db and then it uses auth. If I go look at the db middleware, it connects to the database and passes it to the request object, okay? And then if I go look at auth, what it does is it uses this jwt package from next-auth, and it just gets the token with the secret, which basically decodes the token, and I attach it to req.user.

And then I call it next, and that's it. So, and then I put both of them together inside of all, right here, use(db), use(auth). And that's how I'm able to get both of them. So by the time this function runs, I'm authenticated and the db is connected. So I should be safe to say, newDoc = await req.db.

Actually, I don't want to use req.db, I'm gonna use doc.createDoc, and then pass in req.db, like that. And then a new doc that I want to create is going to be whatever is on the body. So I'll say the new doc is gonna be req.body and then created by

Cool, so now, we got a new doc, and now we're gonna say, res.send. I always like to wrap mine in a data property like that, just to be consistent. So there we go. So now, we got that. Now all I have to do is just export defaults, not deflate.

[LAUGH] What is that? Export default handler. And I guess I need to actually use that middleware that I was saying that wes should use, [LAUGH] handle.use(middleware), there we go. So what else do we have to do? So we got, I did the Create, which was this one, so I just skipped and did this one.

So let's go back and do this one, which actually updates one. So we already have a doc.update here. And we're just gonna put on a query string, the ID. So basically the route for that is gonna be slash API slash Doc slash the Doc ID. So I'll say id.ts, like that.

I could copy most of this. I could put this here. And instead of post, this is gonna be a put. And instead of creating, we're gonna updateOne. And the update that we wanna do is gonna be whatever is on the body. Is that what it wants? Let's see, updateOne takes a, it takes a string, obviously.

So that'll be, cuz that's the name of it right there, so Id. I think Tyson is gonna freak out unless I cast it to string, so I'm gonna do that. And then any updates, which will just be req.body. So we got the new doc, and this is technically like a updatedDoc.

So I'm gonna do that. There we go. So pretty much the same thing, we're just updating using our helpers. That's why we made these helpers, cuz otherwise we'd be doing all that stuff over and over again, which is gross. So we're just gonna do that. Have the req.body.

We're good here. And then last thing we want to do is the same thing for a folder, boom. So we got folder, and then we're gonna, I think we're creating a folder. So we're going to say index. And pretty much the same thing, I'm going to paste that again.

Except for this case, we're gonna get folder. And this is going to be folder.createFolder, like that. And createFolder takes in just a db, the created by, and then a name. So the name, I'm guessing, is going to be our, like that. And we'll update this to be newFolder.

Boom, there we go. So now we're creating a new folder. So we got our routes, it's using our little handlers here. The next thing we gotta do is just hook this up to the client side to mutate. Most of the work on the client is already done, so it's not too bad and there's already some examples of how it works.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now