
No Comments Yet
Be the first to share your thoughts and start the conversation.
Be the first to share your thoughts and start the conversation.
Complete source code available till this point of lesson is available at
How did you manage to remove the blur property and reach here?
Upgrading gives you access to quizzes so you can test your knowledge, track progress, and improve your skills.
By logging in, you'll unlock full access to this and other free tutorials on JSM Pro.
Why? Logging in lets us personalize your learning experience, track your progress, and keep you in the loop with new workshops, coding tips, and platform updates.
You'll also be the first to know about upcoming launches, events, and exclusive discounts.
No spam—just helpful content to level up your skills.
If that sounds fair, go ahead and log in to continue →
Enter your name and email to get instant access
In this lesson, we explore how to create a simple CRUD API using Next.js. The tutorial walks through setting up a mock database (tickets array) and defining API routes for creating, reading, updating, and deleting tickets. This implementation highlights the ease of exposing API endpoints in Next.js without the need for extensive server setup.
database.ts
file comprising an array of ticket objects, each with fields like ID, name, status, and type.splice
method.00:00:02 First, let's create a dummy database within our app folder and let's call it a database.ts file.
00:00:10 Within it, we can create something known as tickets.
00:00:13 Let's say these are some problems that we have to fix within our code base.
00:00:17 So that's going to be an array of tickets and each ticket will have something like an ID, which is going to have a number,
00:00:25 Then we're going to have a name of that ticket, which is going to be Fix Next.js API route bug.
00:00:33 And then it can also have a status, whether it's opened or closed.
00:00:37 And finally, it's going to have a type, whether it's a bug or a feature request or something else.
00:00:43 Now that we have those tickets, we can also create a couple more.
00:00:46 I think my AI is going to do it for me here.
00:00:49 Let's have three, four, and maybe we can even add a couple more.
00:00:53 Let's see if it does that.
00:00:55 There we go.
00:00:55 This is looking good to me.
00:00:57 You can add just a couple.
00:00:58 You don't have to do all of these, but as you can see, it was pretty simple for me to do.
00:01:03 You can just do those three and call it a day.
00:01:06 Now that we have those tickets, let's actually export it by saying export default tickets.
00:01:12 And let's make sure that we're properly closing it right here at the end.
00:01:16 There we go.
00:01:17 It looks like it ended on this 18th one.
00:01:20 There we go.
00:01:20 So this is our tickets array.
00:01:23 Each one has an ID, name, status, and a type.
00:01:27 Now let's head over to our API.
00:01:30 Within our API for the time being I will delete the hello world folder so we can start from scratch.
00:01:36 I'll create a new folder called tickets and within tickets I'll create a new file which I'll call a route.ts.
00:01:45 This is mandatory.
00:01:47 So now when a user goes to forward slash API, forward slash tickets, then they'll be able to make a specific request.
00:01:54 But only if we export an async function of the name of that HTTP request, like get in this case.
00:02:03 And here what we can do is say return next response coming from nextserver.json, inside of which we can return the tickets,
00:02:15 which we can import from app database.
00:02:17 Imagine we're making a mock database call right here.
00:02:21 Now, if you go to localhost 3000, forward slash API, forward slash tickets, and press enter, we're making a get request to this API,
00:02:30 which is returning our mock data.
00:02:32 I can pretty print it, and this is looking great.
00:02:35 Of course, we can do this because this is a GET request.
00:02:38 When you put something in the browser's URL, it's actually doing an HTTP GET request to that route.
00:02:45 But you can use Postman or even this standard client to make that request to forward slash API tickets.
00:02:53 So if you now send it without the body, of course, because this is a get request, you can see that we get back a 200 with a size of 1.22 kilobytes.
00:03:04 in 100 milliseconds, and there we go.
00:03:06 Our data is right here.
00:03:08 Now, I told you we'll simulate full CRUD functionalities, which means that we're not only gonna do get, let's also do a post.
00:03:16 So right below, let's add another function, exportAsync function.
00:03:22 post.
00:03:24 post typically needs a request of a type request because from the request we can extract what we pass into the body so you can think of this as request
00:03:35 body so const body is equal to await request.json So for the time being, let me simply console log the body, just so you can see what is in there.
00:03:47 Now, if we go to our thunder client or postman and try to make a new post request to localhost 3000 API tickets, and we don't pass anything into the body,
00:04:00 first of all, we get 500 because we're not returning anything from here.
00:04:04 So let's fix that.
00:04:05 For now, I will simply return a next response.json body.
00:04:12 So the same thing that we're actually passing.
00:04:14 And I'll try to make another request by passing.
00:04:18 Let's do something like test is equal to test right here and click send.
00:04:25 And there we go.
00:04:26 We got a 200 with a test back.
00:04:28 So that means that whatever you pass as the JSON body into a post request in Next.js, you can extract it by saying request.json,
00:04:38 awaiting it, and then you get the body.
00:04:41 Here, you'll also often see that people do something known as object destructuring.
00:04:46 So you can destructure it and then extract the property that you know will be passed.
00:04:51 So in this case, I can just extract a string of test.
00:04:55 And if I send it, you can see now we get only a string back.
00:04:59 But instead of only getting a string, let's actually extract the ticket.
00:05:03 So we could destructure the name, the status, or the type.
00:05:07 Or in this case, I can just say that this entire JSON body is actually a new ticket, which we want to add to the tickets database.
00:05:15 What we can do then is perform an action to push that ticket.
00:05:19 by saying tickets.push and then instead of simply passing the ticket, I will actually pass a new object inside of which I will spread the ticket,
00:05:29 but I will also give it an ID equal to tickets.length plus one.
00:05:35 So we're taking all of the data from the body that were passed through the JSON body.
00:05:40 And then I'm adding the ID, which is going to be equal to one ID higher than the previous ID in the tickets database.
00:05:47 And finally, we can return the tickets.
00:05:49 Now, if I go back right here, we have to form an actual ticket.
00:05:53 So let's actually pass it an object with something that looks like this.
00:05:58 Name, fixed deployment issue, status is closed and type is a bug.
00:06:04 Again, since we're passing JSON, make sure that every single key and value pair start and end with a double-quoted string.
00:06:12 It's just how JSON syntax works.
00:06:15 So now if I click Send, oh, we get a 500. Why is that?
00:06:20 Let me actually remove this closing comma.
00:06:23 I'm not sure if it's actually supported within JSON.
00:06:26 We might need to end it without it.
00:06:27 There we go.
00:06:29 And send.
00:06:30 And there we go.
00:06:31 Now we're good.
00:06:32 In JSON, you cannot have this final comma if you have no more key value pairs.
00:06:38 And as you can see, now we get back the entire database with hopefully, I think, 18 plus elements.
00:06:45 And the last one should be the one that we added with an ID of 18, fixed deployment issue status closed, and a type of bug.
00:06:54 So congrats, you have successfully added a new ticket to the Tickets database.
00:06:59 Of course, in this case, since it's just a mock database, it will not have actually been added, but hey, you have mocked that request and you would get
00:07:08 a response that looks something like this if you indeed had a functional database.
00:07:12 Now, another type of request that you also often see inside of CRUD applications, and CRUD stands for Create, Read, Update,
00:07:20 and Delete, is getById.
00:07:24 So that's going to look something like this, getById.
00:07:28 You can also export an async function get.
00:07:33 And you might be wondering, but hey, how can we have two get functions right here?
00:07:38 It says cannot re-declare variable get, duplicate function implementation.
00:07:43 Well, check this out.
00:07:45 this one will be a basic get which gets us all of the different tickets from the database but in this one you will get the request as we have done in the
00:07:55 post equal to request but you're also going to get a second parameter by destructuring params and we can set it to be a type of params is a promise inside
00:08:09 of which we're going to get an ID, which is going to be of a type string.
00:08:15 So this is just additional TypeScript syntax that we're writing.
00:08:18 But basically what we're doing here is expecting a second params object passed into the function get.
00:08:24 But here's the thing, even though you might think that this is gonna work, that this route will simply point to tickets ID,
00:08:32 and the top one will simply point to tickets, right?
00:08:35 That's the idea.
00:08:36 That's not gonna work.
00:08:38 We have to extract this ID through Next.js' file-based routing, specifically by implementing dynamic route handlers, similar to dynamic routes for pages.
00:08:49 And you already know how to do it, trust me.
00:08:52 So let's copy this and let's actually put it inside of a new file.
00:08:57 You're going to head over to app API tickets.
00:09:00 And within tickets, you'll create a new folder that's going to have a dynamic ID parameter.
00:09:06 And within that ID, you'll create another route.ts.
00:09:11 And now you can paste this function that we just copied.
00:09:15 So now these params, specifically the ID param, will be populated when people make a get request to tickets and then enter some kind of an ID,
00:09:25 like five, and then we can get that ID.
00:09:28 So let's immediately get it by saying const, these structure the ID.
00:09:33 And make it equal to await perhaps.
00:09:37 Next, what you can do is say const ticket is equal to tickets.find where we get one of the tickets we're mapping over.
00:09:45 And we want to check if ticket ID is triple equal to parse integer.
00:09:52 ID that we pass through params.
00:09:54 Why do we have to parse it?
00:09:56 Because by default, all params are strings.
00:09:59 And in our case, if you go to the database, you'll notice that IDs are numbers.
00:10:04 So to do a triple equality, we have to actually parse it into a number.
00:10:09 Of course, we'll have to import the tickets from the database.
00:10:12 And what do we do at the end?
00:10:13 Well, it's pretty simple.
00:10:15 Return next response.
00:10:18 And then we pass that JSON ticket.
00:10:20 So now if I head over to my browser and want to get the details of a specific ticket, I can go to forward slash API forward slash tickets forward slash five,
00:10:30 for example, and immediately we get something that looks like this.
00:10:34 Looking great.
00:10:35 Next, let's implement the functionality to update the value of a specific ticket.
00:10:40 To do that, we first need to determine which ticket we wanna update, and we'll do that using the same ID parameters.
00:10:47 So if a user wants to update something, they're gonna go to the same URL as with the get details, but they're gonna make a different HTTP function.
00:10:57 In this case, it's gonna be export async function put.
00:11:02 We get the request of a type request, and we get the params, which are gonna be the same as before.
00:11:08 So that's gonna look like this.
00:11:09 You can notice that we're not actually using the request right here, but we have to declare it to get to the second parameter.
00:11:15 So in the situations where you're not using a specific param, but you need to declare it to get to the second one, you can simply put an underscore there.
00:11:24 That means that you're not using it and you don't care what it is.
00:11:27 And you can see even our ESLint understands that because it's no longer giving us a squiggly warning saying that it's being unused.
00:11:33 It knows that we don't want to use it.
00:11:35 But in this put request, we actually will want to use it.
00:11:39 Do you know why is that?
00:11:41 Well, while in the get by ID, we simply need to get the params and that's it.
00:11:45 We don't have to pass anything else.
00:11:47 Here, we actually have to also pass the JSON body with the data that we want to update, okay?
00:11:55 So we first have to get the ID of the params to know which specific ticket or the database document we want to update.
00:12:02 And then through the request body, we can let it know which pieces of data we want to update.
00:12:07 So what we can do is say const, we can destructure the name, the status, and the type by saying await request.json.
00:12:18 Then we can find the ticket.
00:12:19 So we're doing the exactly the same thing as above.
00:12:22 const ticket is equal to tickets.find and we find it by a specific ID.
00:12:29 Of course, we also have to destructure.id right here at the top.
00:12:33 And then we can check if there is a name that we have passed through the JSON body.
00:12:39 And if so, we simply modify the name in the original ticket by saying ticket.name is equal to name.
00:12:46 In this case, it's saying that the ticket is possibly undefined.
00:12:50 So before we start doing any kind of updates, we can say if there is no ticket, then we can return a next response with an error saying ticket not found.
00:13:01 And instead of saying next response that error, I'm going to say dot JSON to which you can pass the two parameters, which is the actual body of the error.
00:13:10 And inside of the second one, you can pass the status.
00:13:14 Great.
00:13:14 Now let's do the same thing with the status as well.
00:13:17 If a status exists, then update it.
00:13:19 And if a ticket type exists, then update it.
00:13:22 And finally, return that new ticket.
00:13:25 Let's put it to the test by going right here, changing the post to put.
00:13:31 And this time, let's say that I just want to update the API tickets.
00:13:36 Let's go with 17 maybe, which is this one.
00:13:40 So forward slash 17. And we want to change the status from open to closed.
00:13:46 So I will not change the name nor the type.
00:13:50 I will simply change the status to closed.
00:13:53 Now, if I click send, we get a 500 server error.
00:13:57 Oh, I think I sent wrong JSON format again.
00:14:00 We must not add a closing comma right here.
00:14:03 So if I send it again with the right one, you can see that we returned the new updated ticket with the name ID, but now the status is set to closed.
00:14:14 Implementing a delete is straightforward as well.
00:14:16 We're going to do it in the same file because we also need to get access to the param ID.
00:14:21 So I will simply start typing and Copilot will do it for me, at least it seems.
00:14:28 What we are doing here is declaring an HTTP GET request where we get access to the request which we don't need to use because we're only interested in
00:14:37 the ID of the ticket which we want to delete.
00:14:40 We get that ID from the params.
00:14:43 We find the index of the ticket which we want to delete.
00:14:47 If that ticket exists, meaning if it's not minus 1, then if that ticket doesn't exist, meaning if the ID is set to minus 1, then return an error.
00:14:59 Else, simply splice it, which means remove it from the array and return the updated array.
00:15:05 You can pause the video and then copy this out.
00:15:07 But as you can see, once you have a nice structure, even Copilot will be able to implement some simple enclosed functions.
00:15:14 Once you have it, let's switch this over to delete and let's not pass anything in the body.
00:15:21 And if I do that, try to delete 17, we get back a new array of all of the existing tickets in the database, but now the 17 shouldn't be there,
00:15:31 and it is not.
00:15:32 So yeah, this is how you implement Simple CRUD in a serverless backend.
00:15:37 Simple and straightforward.
00:15:39 You have implemented two general routes like get and post that point to just forward slash API forward slash tickets, and then three dynamic routes that
00:15:50 point to tickets, API, and then a specific ID.
00:15:54 But now you might want to say, hey, Adrian, what do I need to do if I want to search for some tickets?
00:16:01 That's something we often need to do in the real world, right?
00:16:04 So how can I do it in Next.js?
00:16:06 The solution is simple, but you must be able to understand the difference between params and search params.
00:16:15 I covered that in more details in second to last lesson of the state management module.
00:16:20 where you can understand what are search params, like these things right here, also known as query parameters, where you can add a question mark to the
00:16:29 URL and then append some values which you want to search for.
00:16:32 And you have to differentiate that from general params like the ID, for example, forward slash tickets, forward slash one.
00:16:39 This one is the ID and the rest are query parameters.
00:16:43 Now that we've gotten that out of the way, let's implement the search within our route handlers.
00:16:48 I'll head over to API, tickets, and I will add a new route.
00:16:53 So that's going to be a new folder called search.
00:16:56 And within search, I will add a new route.ts file.
00:17:01 Here we can export an async function get inside of which we're going to get a request of a type request of a type next request.
00:17:10 And now we can extract the search params by saying const search params.
00:17:16 is equal to request.nexturl.searchparams.
00:17:22 This is different from getting the regular params, like what we have done here with the ID, and it's also different from getting the JSON body,
00:17:30 which looks something like this.
00:17:32 Now we're extracting the search params.
00:17:34 And what will the search params be equal to?
00:17:37 Well, it will be equal to some kind of a special object which contains whatever you pass into the URL.
00:17:43 So if you form a URL, something like this.
00:17:47 API forward slash tickets forward slash search question mark query is equal to hello.
00:17:56 Then this will contain a query value whose value will be set to hello.
00:18:02 So let's extract it by saying const query is equal to searchparams.get query.
00:18:09 You know how I said it's going to be some kind of an object?
00:18:13 Well, yeah, it's not exactly an object.
00:18:15 It's a special URL search params object.
00:18:18 So to extract a specific property, you have to run a .get on it.
00:18:23 Finally, if we have no query, then we can return a next response.json to which we can maybe pass all of the tickets because we have no query.
00:18:34 But if we do have the query, we can say const filtered tickets is equal to tickets.filter where we're trying to match the tickets whose name includes the
00:18:47 query we're searching for.
00:18:48 Typically, to improve the query, what you have to do is lowercase both the ticket name as well as the query.
00:18:56 So that way, if either one of these has uppercase letters, you will still match it.
00:19:01 So you can say ticket.name And then add a cursor here and also add a cursor at the query and say dot to lower case.
00:19:10 So now if either one of these two matches, it'll be good.
00:19:13 And finally, we can return next response dot Jason, where we return the filter tickets.
00:19:20 It is as simple as that.
00:19:23 So if you go to your browser or just any kind of a request maker, you can now go to API Tickets, go to forward slash search.
00:19:33 And instead of doing something like forward slash hello, now you can do question mark query is equal to, and then start typing the query.
00:19:42 What you can see is that this specific application, the thunder, actually creates a new query string where you can add more additional queries in a simpler
00:19:51 way using a form.
00:19:52 But basically this is doing nothing else than appending new key and value pairs.
00:19:58 So now I will send this and we get a four or five because I ran a delete, but this is going to be a get.
00:20:04 So if I send it, we get 200, nothing back because there are no tickets that match the query of hello.
00:20:12 But if I head over to my database and try to search something like refactor and send it, You can see that we have tickets with the ID of 3, 6, 9, 12, and 15,
00:20:24 which all of them contain the word refactor.
00:20:27 Let's make sure that really is the case.
00:20:30 Yep, we have refactor here and we have them in all of them.
00:20:33 So you can see this is working well.
00:20:36 We can try with some other query, like maybe let's do bug.
00:20:41 So we're searching for a query of bug and you can see many of them mentioned bugs.
00:20:48 So I hope this makes sense.
00:20:50 We have mocked a new database by creating just a basic JSON array or just a regular JavaScript array, should I say.
00:20:57 Then we have added so many server functionalities without ever initializing a server.
00:21:03 Next.js is doing that for us by exposing those routes that we have created on the server.
00:21:09 We didn't have to set up some kind of a server.
00:21:12 We didn't have to listen to a specific port.
00:21:14 Next.js did all of that for us.
00:21:17 and it allowed us to very quickly expose those endpoints so we can call them and they act like a real server.
00:21:25 It technically is a real server.
00:21:26 It's just so simple to use that it almost breaks the line between the frontend and backend.
00:21:33 Like sure, I know this is only going to render on the server, but like I'm so close to my front end as well.
00:21:39 If I just go outside of the API folder, I'm right here on my HTML.
00:21:43 So I gotta say, I love how these route handlers work within the Next.js, especially using its file-based routing system,
00:21:51 which we're so accustomed to.
00:21:53 So with that said, that's it for this quick introduction to route handlers.
00:21:57 So now that you understand the theory of how you can use Next.js to create APIs, and now that you've actually created a simple CRUD API within a dummy application,
00:22:06 you're definitely ready to use it within a large production-ready application.