Build a REST API in Next.js 13 app directory! Master RESTful techniques and paging w/ Prisma & Auth!

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
today we're going to build a rest API using next js13 in the app directory an API is useful when you need to share resources with clients other than your web app in next.js Think Like mobile applications command line tools or maybe something that other developers need this API will be restful although there is some interpretation as to what exactly that means hi I'm Ethan a principal software engineer who's been writing and shipping code in the cloud for over a decade I sold my previous SAS company in 2020 and I'm helping people like you build yours so let's get started for this rest API I'm going to model it off of a restaurant like schema think something like doordash where there are restaurants which have a name perhaps a description a type some rating and then the restaurant has a menu such as a full menu a brunch menu a breakfast menu and then the menus have items in them and an item has a name a description and a price we're going to keep this pretty basic and we're not going to be adding ordering or anything like that however this should give you a full understanding of how you can build an API that has a lot of those moving pieces so before we dive in what is rest rest stands for representational State transfer that's a complicated way of saying a server will respond to a client with the representation what it looks like of a given resource that resource is the state a resource in this context is basically anything the end user or end client will need to get their job done or potentially manipulate in our API that's going to look like the restaurant themselves the menus and the items on the menu if you were to take this example further it might include the user's orders and items in that order and which restaurant they're going to whenever you're designing an API you need to understand what resources you have to share with the end clients that's where you need to start with figuring out how you're going to build the API and for a restful API the best place to start is with your database and what that model looks like so let's dive in we're building this with next js13 and I'm using Prisma as my orm to manage a postgres database and the schema within it you don't have to be using Prisma and you don't have to be using postgres these are simply technology choices that I like because we're using Prisma it's very easy to switch out what the underlying database is I also happen to have next auth installed because I want to show how you can protect or use a user session in order to work with your API so what does our restaurant database schema look like right now in our database schema we only have the user we have to add in the rest of the model to build out our database first let's add the restaurant a restaurant will have an ID a name an address which is required has to exist somewhere and then an optional description it also has some default metadata to make managing it easier we'll then add in a menu a restaurant will have many menus this is going to be like the brunch menu or the lunch menu or the dinner menu restaurants tend to manage multiple menus and they might cycle out depending on the day or depending on the time a menu has a name an optional description and then which restaurant it belongs to lastly a menu has several menu items and this is the actual thing a user would order a menu item has a name an optional description and a price we're modeling the price as a float but keep in mind if you're actually working with currency you might be better off modeling the price as a string or using the underlying databases currency functionality to make sure you don't have floating Point errors but for this API we're not going to worry about that all right with our database schema updated let's go ahead and recreate our database model I'm going to run a new migration here and it's okay to reset all the data now after the migration has been run we're only seeding a user which means our database does not have any test restaurant data to fix that we need to add in restaurants that have a name an address and an optional description let's ask chat gbt to make some of that for us thanks chat gbt let's go ahead and copy that code and update our seed file with that done we can go ahead and reseed and now if we go ahead and take a look at the database schema we have our test user and we have our seven test restaurants fantastic now we can start building out what the API looks like when building an API I like to design what the URL structure is going to look like and what it's going to return before diving into the code this is going to make the actual code writing very easy because we know what each route has to do the first thing our users are going to want to do with this hypothetical restaurant API is they are going to want to get a list of restaurants here we're using the HTTP verb get and the URL will be restaurants and we'll have a prefix of API for all of these routes this is going to return a list of restaurants that the user can then iterate over and display as they need the next thing they're going to want to do is they're going to want to get a specific restaurant this is pretty common where the list of all the restaurants might not have all the information because that might be a very expensive query and when you ask details about a specific restaurant by its ID then that request might return additional information it's also nice when a user is loading a specific restaurant page we know what the ID is and we can look up the restaurant buy that ID and only return that information this covers the read part of our API we have the create update and delete which we're going to do next to create a restaurant we'll do Post slash restaurants and this will create a new restaurant we don't use the ID here because we don't have an ID yet we're creating a new restaurant resource to update with both put and patch we'll have it at slash restaurants and then use the ID to identify the restaurant we're updating lastly the delete command to remove a restaurant is going to be the HTTP verb delete and we'll pass in the ID so with the API defined we can now write this into code in the app directory under the API folder here we have our next authentication but we're going to create a new file restaurants route and here we're going to export just git for getting all the restaurants and post for creating a new restaurant and now to test the API I like to build out the different requests we're going to use in a tool like insomnia all right We're Off to the Races starting to build our API here we're doing slash API slash restaurants and we're returning an array of all of the restaurants that we have seeded in our database let's go ahead and create a new restaurant and we got the response that we expected a 201 created with the restaurant information and if we go back to list restaurants and remake this request here it is at the bottom showing that we not only can fetch the restaurants but we can awesomely successfully create restaurants now the only thing left to do is to fill in the rest of the API for put patch and delete we have to make a new file and we're using the dynamic routing to get the ID parameter from the URL and here instead of getting all restaurants we will get to the restaurant by the ID to get the dynamic segments from a URL you have to look up the params as a second parameter that's passed into the function here the parameter we're reading from the URL is always going to be a stray because it came from the URL so let's parse it into an INT and now we can test for this we'll just fetch the first restaurant and there it is now on to update well that seemed to work perfectly let's give it a shot this updated the resource and the code's doing exactly what it should do but we're getting into very specific semantics between put and Patch semantically the put end point these days tends to mean I am replacing the entire resource with this data that I'm sending you and the patch HTTP verb means I am replacing partially what the information is and I'm only sending the bits that I want updated here while we're passing in all of the Json that the request sent because the user might not have sent all the Json this is functioning much more like the patch verb because we're using Prisma Prisma distinguishes between undefined and null if the key is null it's going to set that value to be null in the database if the key is simply undefined or doesn't exist in the first place Prisma is going to ignore that update completely now how you handle this in your API is up to you most of the time people want something a little more like patch these days than put to make this a little more semantically correct we're gonna go through and change the update to actually remove things if they weren't included in the request here if the data was not sent we're going to explicitly set it to null now this is going to break if the user doesn't send the name and the address because we Mark those as required so if we run our request again we're going to get a 500 which if we looked in the log the argument says that address must not be null so now it's doing exactly what we want it to be doing well maybe it could have a nicer error message but functionally it's doing what we want it to do let's go ahead and write patch and delete and then see where we're at and then we can test perfect and then if you run the delete again that's gone we're now starting our restaurant list at restaurant number two excellent so now we are covering all of the HTTP verbs and the various crud create read update delete parts to our API for the entire restaurant section of our API let's go one level deeper and add in the menus a restaurant might have many menus right one for breakfast lunch and dinner perhaps we're not going to do this for all of the restaurants because that's a lot of menus to add instead we'll just pick one restaurant and add some data to our database for that particular restaurant yo chat GPT we'll add these menus to the first restaurant and then reseed our database because well don't forget we deleted the first restaurant if you're running these commands you might need to reset your database so all of the objects in it are removed and now in our database we have our number one restaurant back and it has a menu of breakfast lunch and dinner so now we can build the next section of our API this is going to represent the menus which belong to a restaurant now because a menu always belongs to a restaurant they're not an independent resource and that means we're going to Nest them underneath the restaurant URL so whenever you're referring to a menu you're always referring to it in the context of its restaurant which means the URLs are going to look something like this here the API looks very similar it's basically an extension of the restaurant API you're going to ask for a restaurant and a specific one and then you're going to ask for all of that restaurants menus and then you can ask for one specifically and then to create one you'll hit this endpoint and to update or delete it you'll use the dot slash menus dot menu ID and so you can see how the rest API Builds on itself now if you have a resource that's totally orthogonal perhaps something like a chef that exists outside of our restaurants you don't necessarily have to Nest that underneath the restaurants you could just have an end point for slash chefs so take some time when designing your API to think about how the resources relate to each other and what makes the most sense again in our API a menu has to belong to a restaurant so it doesn't make sense to have that be a top level resource because you would always be passing in the restaurant ID here the restaurant ID the scope of the menu is elegantly provided in the URL itself alright let's go ahead and build it and now we can test fetching a menu and creating one perfect let's create a new one oh wrong key name perfect success so now it's relatively simple as well to go through and build the rest of the apis we need just keep in mind you can't use the same parameter name since we're already using ID we have to specify it something differently we'll use menu ID foreign foreign tastic we've extended our API to now include all of the menus that belong to a restaurant you can see where this is going the menu items belong to a menu and so we're going to have to Nest the items underneath the menu in the URLs now it's basically the exact same thing again and again and again so I'm gonna leave that up to you to finish off if you're building something like that or you can check out the code down in the description and you'll be able to get the final result of this and see what I've done there but if you followed along and built this it's going to look very similar there's a couple other things I want to talk about though before we wrap this up the first one is that when you start dealing with lots of information you can't return all of the data in a single list that works for 10 20 50 100 items but if you're dealing with thousands of pieces of information like doordash and how many restaurants they have on their platform you're going to have to include some form of paging in your rest API jumping back to the restaurant's endpoint where we list out all seven restaurants we have let's go ahead and add page now there's two types of paging you can add one is offset based paging where you say how many you want and you also say what the offset is that's what we're going to go and Implement here because it's very easy to get started with however there is a performance hit if you want to offset by 200 000 records the database still has to Traverse that many records and so the other way you can Implement paging is by using a cursor now some databases have cursor specific implementations such as postgres which makes it very very performant however Prisma implements a much simpler cursor strategy where it uses a primary key as its cursor and then we'll jump to that place in the database so depending on your needs you might pick one of those two strategies again for this simple API we're going to go with offset based paging to implement the paging we're going to pass in the Skip and take parameters that Prisma needs as query parameters in the URL and then we'll pass them through to Prisma instead of using the regular request object which is a web standard next.js augments the object with a couple of nice helpers and that's in their next request type so go ahead and change the request to next request if you have to get something like query parameters to make your life a little easier now that we have Skip and take we can go ahead and pass it through to Prisma foreign here I've added skip equals 2 and take equals 2 to the URL and so you can see it starts at ID number three and only took two objects so now the client can you skip and take to implement their own version of paging another small thing you might want to do is you might want to check to see if a user is authenticated before doing some sort of action in the system for example you only want users to be able to create restaurants now anybody can fetch a restaurant but only a user should be able to create one because we have next auth set up we can simply check to see if a user has a session and then go ahead and either return a 401 or let them complete an action here we're getting the session and then if they don't have one we're returning null which means in our create restaurant test because we have no session here we're now going to get a 401 unauthorized you could also check for roles if you have those in your session something like session user role does not equal admin lastly I want to talk a little bit about the structure of data in our rest API traditionally well historically a rest API would not only return the data it would also return URLs that a user could use to look up other data or perform actions on the current data for example our endpoint that returns a single restaurant returns this and that's the top level object it returns but traditionally the rest API might include other URLs to other resources and so they would Nest this underneath a data key like this and now the data is underneath the data key which allows there to be other top level keys for other resources for example links with a link to self which is this object and a link to get the menus for this object as well by having the API be self-descriptive the client only needs to know how to get the First Resource and then can use the various links and actions to be able to Traverse the API without knowing any additional information now I have seen apis that follow this rest pattern to the T and let me tell you that did not make them easier to work with when building an API remember you're building it for clients you're building it to solve a specific problem by trying to be super super die hard and follow an implementation or pattern you may find that that is detrimental to the problem you're actually trying to solve for example if a client were actually using our API they might ask you hey when we display a restaurant we also want to show the most popular dishes at that restaurant could you give us a way to get that information now you could add that as a new endpoint and so you would have to make a request to get the restaurant and that would return this information and then a request to restaurants ID popular might return the popular dishes but if a user is always using the popular dishes when they're displaying it it might make sense to just Nest the popular dishes within the restaurant object itself now that might be a different table in the database you might need to do some querying to pull out the dishes and calculate which ones are most popular it very well could be a totally separate resource but depending on how the clients are using it you might want to tweak your API implementation to better suit their needs remember you're building this to solve a problem not to make the most puritanical API ever when I always have questions around design or how to implement something I tend to like to look to see what other great companies are doing for example I think stripe does a great job with their API and if you look at their API for well retrieving a customer object you can see that the response includes some nested IDs some nested settings nest and metadata and it does not include any of the links to other resources here stripe is relying on its API documentation and client libraries to best guide the user I think for the Modern Age that's the better strategy to go with and with that you've created a rest API end to end you can now go ahead and tweak these resources and build an API that your clients need and that you need for your application remember if the only consumer for your app is your own web app you generally don't need a fully fledged rest API you can get away with just making the endpoints that you need but once your clients are outside of your web app that's when an API is very very useful to have that's it for now until next time happy coding
Info
Channel: Build SaaS with Ethan
Views: 8,712
Rating: undefined out of 5
Keywords: next, nextjs, next.js, react, software, coding, software engineering, next.js 13, nextjs 13, REST, REST API, Restful API, API, Fetch, data, web standards, prisma, postgres, orm, next-auth, next auth, authentication, authorization
Id: Sv1b-P2dO2E
Channel Id: undefined
Length: 36min 4sec (2164 seconds)
Published: Fri Mar 31 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.