Next.js Middleware & Cors | Nextjs 13 tutorial

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] hello and welcome I'm Dave today we will apply middleware to our next JS rest API route handlers and I'll provide links to all resources in the description below I'll also provide a link for you to join my Discord server where you can discuss web development with other students and you can ask questions that I can answer and receive help from other viewers too I look forward to seeing you there Today's starting code is the completed code from the previous lesson on building a rest API and you'll find that completed code under lesson 8 in the course resources and note that I'm still using next JS 13.2.4 today the new 13.3 still has some unresolved bugs now if you're watching this in the future hopefully they've moved on from that and there are no issues to consider however if you're watching this as it's a brand new video then consider going to 13.2.4 for now one other a quick note from the last lesson before we get started on middleware so I want to go back to the API folder that's inside of the source and then app and then API and then let's go to to Do's then let's go to the dynamic directory the dynamic route for the to do ID and look at the route file route TS I'll press alt Z to wrap any code that you can't see and in the previous lesson I got the ID from the request like this I didn't consider it to be too complicated just using the slice method on a string however some noted that we could use the second parameter here inside of the route Handler that is the params that allows us to get the ID as well and yes you can do that and that's probably the appropriate way in next JS and maybe this was an old habit but at least I showed an alternate alternative way to do that so I'll comment this out and let's go ahead and do it the next JS way with that second parameter to start since I'm using typescript I'll need to put a type above so I'll just put type props and I'll set that equal to a type that has params and then we have the prams object which is ID and it will be a string now this is something you had seen me previously do in past lessons for a dynamic route that we used on the front end but you can do this with the route handlers as well so that's what we're doing right here so the second parameter here after the request is an object that has params and then the params object we can destructure the ID from and then over here we just need to say this is type props and now we have the ID ready to use so again from the previous lesson you could get the ID from the request URL as I did here with the slice method or probably the appropriate next JS way of doing things would be to use the second parameter in the route Handler with the props here and destructure the params you get the ID that way so let's save this file and now let's move on to middleware I'm in the beta docs and in the last lesson we built a simple rest API with the new next JS route handlers and if we look at these beta docs we can see for middleware they just have a to do that's not very helpful right now but this may have changed in the future if you're watching this tutorial so always reference the docs but this is still going to let us go back to the stable docs and learn some more about middleware now I'm in the stable docs for next JS and note that they do have version 13.1 and version 13.0 noted here in the stable docs and while we're not really going to use these Advanced middleware Flags today when we talk about middleware and create some middleware we are going to use this feature that was added with 13.0 where middleware can modify request headers response headers and send responses we'll definitely be doing that so let's just scroll down on this same page and it talks about about using middleware and we already have the experimental version installed remember I'm using version 13.2.4 of the experimental app right now with the betadocs you can ignore this but other than that we do want to create a middleware.ts we are using typescript in this tutorial series at least if you're not you could use dot JS but we're going to create this middleware file and they give an example of some middleware here and we're going to follow this fairly closely one thing that is different is it says it needs to be at the same level as your pages in the root or Source directory well we don't have Pages anymore but we'll still put it in the source directory because that's what we're using or what we used in our previous code that built the rest API we're back in vs code so now let's go to that Source directory not the app directory but the source directory if you're not using the source directory you would just create this file in the root directory just where we see tsconfig and in the readme.md and all of these files but since we have a source directory it's going to go in here so now we'll create a new file and it has a special name which is middleware.ts and now we'll create the function by typing export function and this could be an async function if we need it to be then it is the middleware function it's going to receive the request which is a type request and then of course we have the body of the function we're also going to need the next response so let's go ahead and import that with import and then we'll say next response and there it comes from next server so now we have that as well and we're ready to do something inside of the middleware to start off with let's just create a few console logs to see how this works I'll say console log and I'll just say middleware here but we've also got the request and that means we can get some information about it so let's console.log request dot method and I'll shift alt in the down arrow because let's also get the request URL and this means the URL that is being requested from our application after that let's get the origin if it exists so I'll say const origin because this will come in handy we get this from the request headers which by the way you could log these headers to see what other useful information is available in them here we'll say get because it has a get method and we'll get the origin value if that origin exists so as I Mouse over you can see it's string or null right now it doesn't necessarily exist but if it does we'll log that value as well so we'll say origin here now let's just return our next response and we'll call next which means just move on to the route like it was supposed to let's go ahead and test out our middleware by opening a terminal window typing npm run Dev and with the application running then I'll go ahead and click localhost 3000 I need to control click there it started and we see several requests rolling through the middleware already so any one of these URLs that was requesting some type of resource applied to the middleware as well and that's the thing about that middleware file is it applies to every request on the web page not just the request to our API so I should say every request for the web application not just the page and that means we might want to limit where that middleware is applied because right now all we did is load the home page and we're seeing all of these requests roll through the middleware and surely some of those we don't want there so I'm going to go ahead and stop the application and close the terminal window and now I'm back in the next JX stable docs and here we're talking about matching paths so note it says middleware will be invoked for every route in your project and that's what we just saw for every resource needed that middleware file was invoked and that's not necessarily what we want we want to pair that down to probably just our API routes today but you may have other needs where you need to specify where it applies this will help you do that and there's actually two ways to do it one I'm just going to scroll up instead of down to show a very simple way you see this matching paths here where we Define a config and then it has a matcher defined now as we discuss this matcher it can be constructed in several ways it can be a static match just without any parameter like you see here with path or the asterisk it could just be a static route that is provided here however this path parameter with the asterisk could be very useful so let's see what that's about as we scroll down here and they discuss the matcher a little bit more you can see it can also be an array so there could be more than one path that matches or one URL in the or end point if you will and it also accepts reg X so you could even do something here like a negative look ahead which is a little more complex when you're working with regex but there's many different options but essentially let's talk about this path if we can find where it's discussed a little bit more essentially this path parameter matches anything after about Slash so it matches about a also matches about B but it doesn't match about Slash a slash C however if you put the asterisk after it that does match anything that might even go deeper into directories like slash a slash b c because the asterisk is zero or more now you can look here in the docks to find more details about that but that's basically all we're going to need today is something just like you see here with a slash about Slash path and then we put the asterisk which means we could have nested directories deeper than so now I'm back in our middleware file in vs code and let's go ahead and apply a matcher so I'll say export const config and we'll set this equal to an object that has a matcher and with the matcher we'll set it equal to slash API slash and then put the path parameter and we'll put an asterisk there as well so that means basically any route we have inside of our API folder will be caught and this middleware file will be applied to it so now if I go ahead and restart our application we shouldn't see the same thing we saw before I start with npm run Dev once it starts and if we request this localhost 3000 page so I'll control click and it goes ahead and loads in the browser notice we don't see all of those requests coming through or all of those console logs we have in the middleware file because that home page although it opened in my browser and I'm not looking at that right now I'd rather a look at this to see the output we are seeing no console logs because that middleware file is not being applied to that path in our application okay I'll close the terminal window and now let's talk about the second way you can optionally apply this middleware file to only specific paths we can use conditionals to accomplish the same thing or to apply specific things to other routes as well so inside of our middleware function I could say if the request dot URL dot includes and then if I wanted to just blanket our API routes I would say slash API slash and of course that would be true or false and then we could put everything else inside of this if statement if we wanted to so this means we wouldn't need this config with the matcher here if we didn't want to use it that way we could just use conditionals or we could just use the matcher or you can use a common nation of both another example would be to create a reg X so let's say if I said const regex equals and then make a new reg X which it's a regular expression and then I made this expression slash API slash with the asterisk after it here so now that I have that we could test that inside so instead of the request.url dot includes we might say regex DOT test and then we pass in the request URL and of course it's testing to see if it contains this pattern or if it matches that or not so a couple of different ways to accomplish the same thing I'm going to leave this here for now because we will be coming back to this middleware file to apply a conditional statement later and now that we know how the middleware file works we can consider adding some useful middleware with it but not all middleware works with this file so we'll come back to this file for now let's look at a middleware dependency I'm on npmjs.com where we can look up different packages that have already been created that we can use as dependent agencies in our application here I'm looking at limiter it's a rate limiter that we can use in our application this is something you would easily use in a node.js and express rest API but we can use it with our next JS rest API as well you can see the install here is npm I limiter so we're going to need that to add that and you can get details here but I'll put this link in the description for the video and the course resources and besides that I'm going to show you how to apply it so let's go back to vs code we're back in vs code let's open up the terminal window again Ctrl C to currently stop the application let's type npm I limiter as we saw in the npmjs website okay it says it's installed let's check that in our package Json which is always a good idea after you install something and yes we see limiter is now listed in the package Json dependencies and I'm back in the next JS docs now it would be very convenient if we could run that rate limit which will protect our application from being overloaded with abusive requests but it would be convenient if we could run that rate limiter in the middleware file but it has a dependency on node.js that won't work in the edge runtime and that is where the middleware actually runs in the edge runtime at least that middleware file so we're going to have to apply that dependency for limiter inside of our routes instead okay we're back in vs code so now let's go to our routes so we're at source app API and then we have the different routes inside of our API directory now what I want to do just inside of the API directory itself is create another directory and I'm going to call this config and then I'm going to create a file inside of config and I'm going to call it limiter.ts now inside this file is where we're going to create our limiter instance or you could have multiple limiters and you could create one for each different route if you wanted to apply different rules different limits to each of those routes here I'm just going to create one so I'll say import rate limiter comes from limiter after we get that we'll say export const and Define our limiter because I'm only going to use one so if I created one just for a get route or a post route I might say git limiter post limiter or if it was a specific route I might say get to Do's limiter or something like that or get hello limiter but here I'm just going to say limiter and I'm going to say new rate limiter and then inside of this we'll pass some options one is tokens per interval and here let's start out with something small like three so we can see how it works after tokens per interval let's create the interval itself and we should be able to see those options here and yes we do so there's day hour minute second and for now I'm going to choose minute so we've only got three tokens per minute right now after that one other thing to make this work asynchronously and go ahead and continue to the rest of the code or of course to remove tokens immediately we want to put fire immediately here and I'll set that to True also okay now let's go to our simple hello route that comes with every next JS application currently when you build one and we see the hello route right here at the top of the file we need to import our limiter I'll say import and this is limiter comes from config limiter where we have it and then inside of the function there's a couple of lines we could put well really only one line we need but if we want to see the result then we'll put in a console log as well so I'll say const remaining and we'll set this equal to await limiter dot remove tokens and we'll just remove one token so you could remove more than one but we'll just remove one then I'm going to console.log and here I'll just say remaining and after that I'm going to put in the remaining value just so we can see what happens here with this request and the limiter so let's start the application once again so I will npm run Dev in the terminal we'll get that up and running and I will use thunderclient to go ahead and try out that hello route for our API so I'll close that out you could use Postman if you want I'm going to use thunderclient to send the request and now I'll go to a get request and it looks like I've set that to the to Do's for now so let's switch this to the hello route it is a get request we can send that it's process the request now let's open the terminal again since we're still here in vs code we can see that and we have two tokens remaining let's send it again now we've got less than two now we've got less than one and now we're at minus one so now if we send another request we still have minus one but everything was okay so there's something else we still need to do let's close this out and let's go back to our hello route and we need to put in a response if we don't have enough tokens remaining so here let's say if remaining is less than zero then we want to send a response so we will return a new next response and so that immediately Imports next response at the top when I type that now the first param in next response would be the body so this is where we would send Json we could stringify Json here if we want to I'm just going to make that null because there's no body to send but now in the second param we can put all of the other things we would normally send with a response like the status which needs to be a 429 when we have limited the requests here after that we can put in status text and we're going to say too many requests which is the typical response you see when your rate is limited and then we'll put in headers that's an object here we'll have the access control Dash allow Dash origin and we could put this as the origin if we had defined it or we could just put all if we wanted to which would be the asterisk here so if we want to get that origin above we could do that as well this isn't really required but it is good to put this in especially if you are working with cores which we'll get to so let's go ahead and grab that origin also here and we get that from the request that's coming in so here I'll say const origin that needs to be equal to request dot headers dot get and we get the origin as you saw me previously do in the middleware file so now this could be equal to the origin value or you could just have this asterisk here for all because possibly this request is being sent from thunderclient or Postman and then An Origin won't exist okay after that the other header we'll put is the content Dash type and right now we are sending back Text slash plain content okay let's go back to our Thunder client here and we'll of course open up the terminal again so we can see the replies we'll send one one request now we have two remaining tokens another less than two now less than one and now we have our 429 too many requests here and that's what we expect to get when we are being rate limited and you can see we have remaining minus one now the nice thing about pulling this instance in from our config slash limiter is it's going to share that token count between the different files so if you wanted to use this in the hello route like we are here and then also use it inside of the to do route you could and it's still going to count those tokens you would want to create separate limiters for separate routes if you didn't want to share the tokens between them or instead of using the config file just created in the route file where you're using it of course you could do that as well I am back in the next JS docs once again and of course we've already applied rate limiting middleware we've learned how the middleware file works as well that runs in the edge run time but one piece of middleware that you almost always have to use if you want to make your API public is cores and here let me quickly jump from the docs to the npm JS website once again and here is the cores middleware that many use with node and express now this is actually recommended I will show in the docs coming up as well I want to scroll back down to where I was here just we'll come back to this but in the docs where we're learning about API routes here in next JS it talks about core's request helpers and this will end up linking you to a GitHub repo where they created a wrapper but this is with the older version of next JS it's not going to work with the new version that we're working with in the beta docs so just telling you this ahead of time so you don't spend too much time on that however you don't have to specify core's middleware with next JS and this new version of the routes to make it work so I'm going to show you how to do everything cores would apply without actually using that middleware from npm so right now the big thing this is under caveats when we're talking about apis here I come to the API routes but under caveats the main thing to note when you're creating a rest API in next.js is that those API routes are always same origin only so you're always going to get a cores error unless you work around that now that is great if you don't want anybody else besides your own application to access your rest API but if you want to share it with the rest of the world or at least in a loud list of URLs where a few other websites are permitted to go ahead and access that API well then you need to know how to do this workaround and actually it's not so much of a workaround as it's just applying everything that the core's middleware does in a different way without using that dependency so as we apply this there are some things we need to keep in mind this is essentially what the core's middleware does it checks the origin and it says if it's allowed or not the asterisk means all Origins are allowed but we can provide an allowed list and you'll see this provided up here as they put in a white list is what they call it here in the docks I like to call it allowed origins or in a loud list But whichever you prefer anyway from there you're going to check those Origins other than that next JS already handles the options request and that's pretty awesome because it bases its options reply on what methods you already have in your route handlers and then it also handles the options request success status for you so essentially we're going to handle checking the origin and who we want to access our API and everything else next JS is pretty much going to handle for us now I'm in the beta docs and if you're wondering where to find that information I found about the options request well it wasn't that easy to find but here we're under HTTP methods and this comes under route.js now what we've got here is the comment if options is not defined as a function here inside of the routes next JS will automatically Implement options let me not scroll too fast will automatically Implement options and set the appropriate response allow header depending on the other methods defined in the route Handler so essentially it handles it for us and this is the only place I found this information but it's good to know that way we don't have to create our own response for those options requests and those options requests are triggered by requests for Json data and other complex requests and of course if you want to find out more about that you can look under mdn and look under the options request and pre-flight course that's also a place where you'll learn more about that I'm back in Visual Studio code and let's go go back to our middleware file because here is a great place to check all of the origins we're talking about or create that allowed origin list I'm going to do that above the function here because this would be a static list that we would add to just as we wanted to allow origins or not so I'm going to say const allowed Origins and this list may change depending on what mode you are in say development or production and we can check that mode so let's set allowed Origins equal to a ternary statement and I'm going to say process.env dot node environment this provides which environment we're in so let's say if it's equal to production this would all be lowercase so if it's equal to production now we can provide our list and this list for you for example if you deployed your application which would be production it might be https colon slash slash www dot whatever your site is named so I'll put your site.com that would be an example of that another one might be the same thing but maybe without the W's here so let me go ahead and copy this of course you'd want to test this out to make sure everything was working as expected we just go ahead and remove the W's a couple of different URLs that are fairly common for a deployed site of course replace your site with whatever your domain name would be but now in development we might have a totally different list so we might have something like our HTTP instead of https colon slash slash and then we might have our Local Host three thousand that's the current website and of course it's already same site but let's go ahead and include this because we're going to use this in a conditional so we want to do this here after that you might also say you'd replace localhost with the IP address 127.0.0.1 that still means your computer or translates to localhost or localhost actually translates to that IP after that let's put in something else we might just play around with and we can see examples of how to work or not so I'll put in HTTPS colon slash slash www.google.com and we can issue a fetch request from the dev console while we're on the Google Website and we can see if we get access or not so we'll put in those for now and now remember I've already left this config in for the matcher I just showed a couple of alternative ways to do that here with conditionals but I'm not going to leave this in I'm going to put in a different conditional so I'll just highlight all of that and hit backspace and now let's put in our conditional for our Origins and here I first want to say if we have an origin so I need to get that origin ahead of time so let's go ahead and do that up here and we'll just control X put it back right here above so now we have the origin wherever it's coming from and we want to make sure we have an origin and let's say it is not in the list so we'll say not in the allowed Origins dot includes and then pass in the origin so here we want to make sure we have an origin value but that value is not in the list we've applied above and now we'll do an early return here so from here let's go ahead and have our if block and we will return a new next response and inside the next response once again this is where the body would be and we don't need a body here so I'll make that null and we're going to have a status 400 which is typically the status for something that is blocked by cores so this would be the same status we would get from that Coors middleware package if we didn't have the allowed Origins matched up and then from there we'll say status text here we'll just say bad request which is what a 400 is and we'll have headers and inside the headers we're going to have our content Dash type and this is Text slash plane once again now this will essentially block anything that is not in our allowed Origins list but there is a problem here as well because thunderclient Postman and possibly other tools that we might test our API with would not have an origin at all so for now those would bypass this and that's that's fine and you might want to leave it that way but in production for example you might also want to block those when you deploy your app and if that's the case you could just put in an or here like or no origin so then it would block those rest API tools as well I'm not going to leave this in because we are going to use thunderclient today but I did want to let you know if you want to block those you would just look for no origin at all and that would do the trick however that might also block some browser requests and things if you didn't limit it to your API for example so there could be a trade-off there as well just always remember to test everything out and make sure your conditional routing logic is working as expected okay let's make sure our application is running and it is still running here so now we can test out this logic to see if it is working okay I'm back in the web browser and here on the Google home page now from here I'm going to open the dev tools with Ctrl shift I and I'll have a terminal here so I'm at the console not a terminal but actually the dev console and I want to just fetch from my local site now when I say localhost that means my computer I can do this because I'm running this browser on my computer too so when you fetch on yours you're not checking my website or my API you're actually checking your project localhost refers always to your local machine so here I'm going to say fetch and I'll say HTTP and then colon slash slash we have localhost on 3 000 from there I believe we had slash API slash to do's and that should be all I really need to do here so let me go ahead and press enter and we have a promise pending and it is currently blocked by cores you're thinking wait a minute we put that in the allowed list but yes we're not quite finished yet so Google should be able to access our API but we haven't changed everything we need to but we did check the origins so let's go back and go ahead and make the rest of the changes and try this again okay I'm back in vs code so now let's go to our to-do's route and we were issuing a get request so that hits this route right here that is get inside of our to Do's route.ts file so from here where we have our next response dot Json we need to change this so we can apply the appropriate header so first of all we'll need to create a new next response once and we'll change all of this so the new next response is going to receive Json here as the body and we'll use json.stringify then I'll pass in the to do's and then after that let's go ahead and put in everything else that we would expect to have here and to bypass the core's response we're getting we need headers and that headers is an object and it's going to have access dash control Dash allow Dash origin and that is going to be set to the origin which we once again need to get above and if we were testing with a tool like thunderclient instead of making a fetch from Google we might just want to put in a reply like all right here as well after this let's put in the content type as well we're replying with and that is going to be application slash Json here now what we don't currently have yet is the origin and remember we're getting that from the request and of course that's going to instantly make this a dynamic request instead of a static request as next.js is documented but we need that request object here so we're going to go ahead and put that in and then we're going to Define our origin so we'll say const origin set this equal to the request dot headers dot get and we'll get the origin if it exists on that request now that's in place we'll open up the terminal and yes we're still running the application here so let's close the terminal out again and we'll go back and check this on Google okay we're back in the browser back at Google I will clear this out now I can just Arrow up to pull that fetch up once again and let's send our request now we're not getting a course error at all it's a promise pending I didn't await the fetch or anything like that but the fetch doesn't error out due to cores and that's because we're sending that appropriate header in the response okay I'm back in vs code so what you want to remember about cores is not only are we checking our allowed Origins list inside of our middleware file that applies to everything the middleware file is applied to so here we limited that to our API routes but after that then inside the routes themselves you have to make sure you're always sending this Access Control will allow origin header and specifying the proper value of course the origin that it actually came from or if you're wanting to work with rest API tools like Postman or thunderclient this is an appropriate response as well just to have something there so that's the short circuit here in JavaScript the or so if origin doesn't exist you're at least replying with that but once again it's only made it this far because it made it past the middleware where it's checking the allowed Origins now a couple of things we could still check here one is let's remove Google from the allowed Origins and make sure our bad request response also works okay I'm back at Google let's once again issue that request after we've removed Google from our allowed Origins and yep now we have a 400 bad request and of course it's blocked by cores now too because we didn't put that header in that response so they get the cores message that is expected and then it's also getting that 400 bad request test and this essentially mirrors what that core's middleware would do that we looked at at npmjs now one more thing to look at on npmjs we see that options success status is a 204 and I said that next.js already handles that for us so let's confirm that back in vs code I'm going to go back to thunderclient and I'm going to just go to our request that we sent here we had too many requests let's make sure it once again works all good there but now let's send just an options request now we should get a 204 and that's exactly what we get so next JS is handling that appropriately so today we've learned about this middleware file and it runs in the edge runtime and of course we learned how to apply the different routes with either the config down here and the matcher or conditional logic or you can use both together and after that we also learned how to apply rate limiting middleware and we of course have had to do that inside of the routes file itself because it had a node dependency that won't run in the edge runtime then we learned about next JS handling those options requests and how to provide all the checks that the popular core's middleware provides without really needing that dependency in your app at all and while I don't expect it to with next.js things can change quickly so remember to check back to the docs for whenever this to do is completed remember to keep striving for Progress over Perfection and a little progress every day will go a very long way please give this video a like if it's helped you and thank you for watching and subscribing you're helping my channel grow have a great day and let's write more code together very soon
Info
Channel: Dave Gray
Views: 13,533
Rating: undefined out of 5
Keywords: next.js middleware, middleware, next.js, nextjs, next.js 13, next.js 13 middleware, nextjs 13, nextjs 13 middleware, next.js cors, nextjs cors, next.js 13 cors, nextjs 13 cors, cors in next.js, cors in nextjs, cors middleware, cors, cors in next.js 13, cors in nextjs 13, cors middleware in next.js, cors middleware in nextjs, cors middleware in next.js 13, cors middleware in nextjs 13, how to apply cors in next.js, how to use cors in next.js, cors route handlers, rate limiter
Id: h4-2K7nFf7s
Channel Id: undefined
Length: 38min 7sec (2287 seconds)
Published: Fri Apr 14 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.