747

March 25th, 2024 × #middleware#nodejs#webdev

Middleware Explained

Explaining what middleware is and examples of how it's commonly used in web development for things like authentication, caching, error handling, etc.

or
Topic 0 00:00

Transcript

Wes Bos

little scatterbrained right now because gotta you know, when you get back from vacation, there's a 1,000,000 little things that you gotta gotta get to, but pretty pretty stoked and, and well rested. So ready to talk about middleware.

Topic 1 00:35

Middleware runs between request and response to handle logic

Wes Bos

Yarn of building really any application, and this the concept of middleware applies to absolutely everything, systems design, but we're gonna be talking about it in terms of, like, you're building a back end web server and when might you need middleware Wes why is it handy? So middleware is for is a bunch of code or some code that will run in between the initial request and the actual event handler that handles your data that's coming in. So somebody visits a URL. They submit a form.

Wes Bos

They save something. They try to access a route that is behind a logged in state.

Topic 2 02:13

Middleware widely used for user authentication

Wes Bos

A lot of the times when you want to run some sort of logic or functionality before the user hits that route handler, you inject what's called middleware, which is a function that will run, and it could be multiple functions as well, and it's used to check if somebody has access to a specific route it's used to we'll we'll do a whole bunch of examples but generate some data from another system and bring it into that specific Wes, ability to skip expensive operations, take the logic out of a URL handler because sometimes you'll have a route handler that will do specifically something, and it doesn't make sense to muddy the logic of that route handler. Maybe, somebody saves an item to the database. Right? They update an item. They click the save button. It doesn't always make sense to put all of the logic behind authentication and, data parsing and, which which server is it going to, multitenant application. It doesn't make sense to put all of that logic into that specific route handler because you can just assume that those things are in place at that point and put that other logic into a middleware. So I've been using middleware for for quite a while. It's been a concept in Express. JS was was the 1st time that I've ran into it specifically via something called Connect, but all of the modern frameworks now as well have this concept of middleware. I'm gonna be talking about how to use it in in some examples as well. Yeah. And and to even just give a, food sandwich based analogy here, you could think of

Wes Bos

Is that a my brother-in-law, is that I think that's a Friends reference, isn't it? I don't even know. Oh, no. It's a my brother-in-law is a big Friends person. I for the record, I hate Friends. I think it's a a garbage show.

Wes Bos

But the record, I I got Node I think it's a great show. There's 2 things that make that make me laugh about Friends. It's pivot and Oh, yeah. The moist maker. And the moist maker is the the idea that you have a, a sandwich, and you need to add something that gives you moisture. Right? Like, a a turkey sandwich left over for Thanksgiving, and there there's a layer that you add to that to add the moisture to it. And I've never seen that Friends episode, but I I get the reference.

Topic 3 04:55

Can skip expensive operations with middleware

Wes Bos

Yeah. And it's often makes sense to like, for example, in my application, I have forward slash admin, and then I have, I don't know, probably 15 different routes, different routes that can be rendered as well as different routes that can be update data. And I don't put the logic of, are they logged in and do they have the permissions in every single one of those routes. You put it in a single piece of middleware and you say, apply this middleware to any forward slash admin route handlers.

Wes Bos

And what that middleware would do is it yeah. Like Scott says, it looks up the current user. It if and it will look up their current, if they have access to those specific things. And if they don't, it throws an error, and it will render out a an error page say you don't have access to this specific thing. But if it does, it it puts the user on the request, and then any route that's after admin will now have access to the current user. And you can generally access it via something like request Scott user or request that data.

Wes Bos

Or how how you access those values is different in every single application, but generally, they just stick it into the request or make it, available via the async local storage API, which is new in in Node. Js.

Wes Bos

Some other ideas for middleware that are are commonly used is redirecting users to a specific instance. So if somebody is coming into a URL that has been shared via American, this happens all the time in Node, sometimes you'll want to redirect that user to the Canadian version of that website. Or, if you have data privacy laws and you somebody is signing up for a specific use case, you could check what their IP address is or or where the request is originating from, and then you may want to save that user's data in a specific database that lives maybe in Europe or in a different region that you you must keep their data inside of that specific instance.

Topic 4 08:41

Middleware useful for logging and analytics

Wes Bos

Maybe I should explain one more thing about middleware. The idea with middleware is that you get the request in. Right? You can add stuff to the the context if you want or you can log stuff, but the idea is that you you call, like, next or you return a new response from the middleware, and then it will continue on down your specific route. So what you could do in development mode is that if you only want to have logging turned on in development because you wanna you wanna have a nice verbose mode, you could just say if process dotenv equals development, then log that value out. Otherwise, don't specifically do that. That can also be really handy for trying to just temporarily turn on some debugging. Vercel something's going wrong in production, you could flip on some logging in a middleware. You're not actually changing the code that is running, and that's that's such a nice thing to just leave that as is and not have to modify it and simply just jump in the middle there.

Wes Bos

AB testing, really handy as well. If you are building a landing page and you want to alright. For 10% of the users or users who have this specific flag on or users that have a, actually, this is one thing I do is is country codes as well. I provide discounts for different countries based on where your user is coming, And what I do is I have a set middleware in there that when the request comes in, I check via a header that says what country are they from. And if they are from a specific country, I'll populate the full name of the country because, like, I'll just get, like, CA, and I wanna populate that to Canada. And then I'll also populate some information about coupon codes that they get. And then when it comes time to actually rendering the application, you can simply just check if that value is there. Say, if there is a coupon code, then render out the coupon code banner. Of course, you can you could change see which heading specifically works better, and you can get really, really, really complicated with the different AB testing values.

Topic 5 11:53

Handle error logging and reporting with middleware

Wes Bos

Other things, caching expensive renders. Obviously, we have things in on servers and in the browser and on CDNs to to do caching.

Wes Bos

But also you could use you can simply just memoize a function or implement your own caching in a middleware, which JS, say, alright. Well, if I've already done this query, I've got the data here in in a key value store. I could just return the data directly, and then it never even needs to hit that value. Because that's another thing that can happen from middleware JS you don't have to continue on in the middleware. You can simply just return early, and then the the request will never actually hit that later middleware.

Wes Bos

And the last 1 I have here is just multi tenant applications. So I run, I I don't know, probably 11 different domain names on a single, Node. Js application for all my courses.

Wes Bos

And the way that I determine which domain name and which course somebody is actually viewing is I run a set of middleware. So one of the very first middlewares that the request goes through is it says, if the domain name has, beginner JavaScript .com in it, then set the course code to Bos.

Wes Bos

And then later on in the rendering, it will choose which files to actually render out based on that data that had been set earlier. So if you that's that's an example of a multi tenant application. It's just me. I'm the only tenant. But if you were to have multiple customers being, running on the same code base, you can use a middleware to determine which customer is this when you go through the whole process, and you can even do that with databases. If you have multiple databases running for each of your customers, you might need to set the database connection string in a middleware before you hit any of those database calls.

Wes Bos

Wes does it run? I think we covered that. It runs in the middle. Yeah.

Wes Bos

That's the way I think about it. Okay. Well, let's keep this in. Where does middleware run? Does it run-in the middle? Does it run on the edges? That's a good question. So, traditionally, they're with Express.

Wes Bos

It simply runs in the same application, and it just JS a function that runs before the rest of your your other functions run. Right? However, it's becoming more and more popular to run your middleware in a totally separate environment that's called an an edge function. It runs at the edge because if you're gonna stick a whole bunch of logic before your actual application runs, it better be fast as hell. Otherwise, you're gonna really extend the the load times of those specific handlers. So where a lot of these things now run is they run on the edge, and they run-in environments that are not typically full Node JS. So probably the most common one is running in a Cloudflare Worker.

Topic 6 15:32

Middleware evolving to run on edge for speed

Wes Bos

And the Cloudflare Worker will try to run it as close to the user as possible so you get the best response times, and it will run it in a paired down environment that doesn't necessarily have the whole Node.

Wes Bos

Js setup. Although, Cloudflare is pretty close to being Node. Js compatible as well right Node. And that's how the Vercel middleware and Next. Js middleware

Wes Bos

You'll also hit time out limitations as well in a lot of these edge, areas, so it might not make sense to wait connect to a database. You might not be able to do a whole database connection setup. We talked about that if you go back to the episode we did on serverless databases.

Wes Bos

We talked about sort of limitations around all of that, but often people will forego the whole database connection string, or they'll use a database where where you can use it in a middleware, and they'll just stick stuff in, like, a key value store or something that's really, really fast to connect to and and access.

Wes Bos

Yeah. For for the user one, I'm curious if you think that this I many, many years ago, I was like, is it okay to look up the user on every single request? And I came to the conclusion those yeah.

Wes Bos

You you could cache that for a little while if you really wanted to, but, it's totally fine, and it's very fast to do a quick database lookup of the currently logged in user based on their session, especially when you're need to update the user and and maybe permissions, things like that. It will be a pain in the butt if you have to cache data, and have to revalidate that.

Wes Bos

In every application I've ever done, I've just just query the current user on every single request, and it's never been issue for me. Have you done caching of that? Yeah. I've done caching of it, but with Redis. So,

Wes Bos

That's a good point. It's, like, what's in that query could significantly like, if you're querying the current user and all their courses and all their progress of every video and every transaction they have and you're sticking all of that, if if that's like, I don't know, like, 300 k of data, that has to go over the way or somewhere and then be stored in memory, and that certainly could could slow you down. So it is important. Also, like Scott said, you should probably throw some timers in there or use something to figure out where is the the time of this request being spent. You know? You could just say, oh, like, it's a 500 millisecond. That's kinda slow. But where is that 500 milliseconds being spent? And if that is in your user lookup, then it's probably worth throwing it into Redis. And the way that that would work is you you still keep your middleware for the user, but the early on in that user lookup middleware, you you check if it's in the cache and and the reddest cache, and that is fast as hell to be able to just quickly return the cache version rather than do a whole,

Wes Bos

Alright.

Wes Bos

Oh, last things that we have here. Next. Js middleware is 1 file only. So I love the express, and hono. Js does this as well where you you set up your your route. You say, alright, admin forward slash anything, and then you can have your populate user or check for auth or do they have access to this specific thing, and then that will then move on to the next row. I love doing that at a route Vercel. And the Next JS middleware is a 1 single file that will run on every single request, and you have to add the logic in yourself.

Wes Bos

There also is there's, like, matchers where you can say, alright. Only run this on this specific thing, but you're essentially reimplementing the entire router yourself.

Wes Bos

And I was like, why is there Scott, like, a middleware file that you could stick in the app router? Like, I wanna I wanna go to the admin folder and put a middleware Oh, yeah. That'd be file in there Oh, wow. And then have that run only on it. And, apparently, they had tried that, and it was very confusing, and I could see how it could get kind of muddy. I I was like, a very simple example. I was talking about it maybe sounds like a good idea, but, obviously, they had tried it. So that was kind of a bit of a bummer to me because I was like, oh, like, you you have this app router. Right? Like, I don't have to write a router. It's just folders.

Wes Bos

But but then if you want middleware, you do have to write your own router and you have to match the the URLs and you say if it starts with this, and you gotta make sure that that's not, like, injectable, you Node, like you had to make sure that, the user can't accidentally come up with a URL that matches your regexes. So I I thought that was a bit of a a pain in the butt to to have to do that. But I've I've been I Scott the fix. For that in in SvelteKit world. Layouts.

Wes Bos

That's at least how I accomplished that type of thing. Yeah. I that's what I wanted to do. I was like, I want this to work like the syntax website. Yeah. Right. Yeah. As far as I could tell, it it doesn't doesn't work like that, especially if you want it to run at the edge in in the middle rather than, be part of the the actual generation.

Wes Bos

And then also connect, connect style. We've talked about express, fastify, pretty much any Deno JS, Any framework you pick up will have this concept of middleware, or hopefully, we'll have this concept in the middleware, and,

Wes Bos

I I just remembered Node more thing I forgot to say is that, like, often these middleware you just said you can npm install them. The reason you can often NPM install them is because they are standard space. So they are either connect style, meaning that they have a request, a response, and the next function, and connect style will work with Fastify Express, the all kinds of different frameworks, or there'll be the new modern Vercel, which is the fetch or web request web response Wes you're simply just returning a fetch request or returning a response object, and those will work, or they'll be something that is somewhat a variant on that.

Wes Bos

And that's why you can just usually Npm install them all. And, like, you could go and NPM install, like, a rate limit. That's another really good use case as well as you you wanna stop somebody from hitting your sign up endpoint a 1000000 bazillion times, you could write a middleware. I have this in in my own application where it stops people from, hitting it too many times. It's a rate limit middleware, and you simply just need to Npm install it. And they work with more than just 1 specific framework.

Wes Bos

make the opacity

Wes Bos

Oh, that's good. Or every 1 in every 10 Wes, show them, but then Scott. I don't know. There's a lot of fun stuff you could do there, but that is middleware. Hopefully, you enjoyed that. We'll catch you later. Peace.

Share

Play / pause the audio
Minimize / expand the player
Mute / unmute the audio
Seek backward 30 seconds
Seek forward 30 seconds
Increase playback rate
Decrease playback rate
Show / hide this window