Let's start by creating the world's simplest Express server:
import express from "express";
const app = express();
app.get('/hello', (req, res) => {
res.send('world'));
}
It creates a route /hello
that responds a body world
.
Now let's change the last line just a little bit by adding a 3rd param called next
:
app.get('/hello', (req, res, next) => {
res.send('world'));
next();
}
Congratz! ๐๐๐ You've just created an Express Middleware. That simple!
Middleware == Function with next()
A middleware is just a function with three params (req, res, next)
, where next
is a function that allows you to chain multiple functions. Let's see another example:
// returns true if server time is in AM
function isMorning() {...}
app.get("/hello",
// middleware #1
(req, res, next) => {
if (isMorning()) {
res.send("morning");
} else {
next();
}
},
// middleware #2: called when isMorning() === false
(req, res, next) => {
res.send("afternoon");
}
);
Here we are chaining two middleware functions to handle /hello
route. We use next()
to pass control from the first middleware to the second.
In real-world scenarios, middlewares are useful for sharing common code among routes.
Example 1: Require user authentication for certain routes.
Here we created a middleware that only calls next()
when user is authenticated. The middleware is shared by two routes. Note that when user is not authenticated, we don't call next()
, which will stop the chain.
function RequireUserAuthentication(req, res, next) {
if (req.user == null) {
res.status("401").send("User is unauthenticated.");
} else {
next();
}
}
app.get("/me/name", RequireUserAuthentication, (req, res, next) => {
res.send(req.user.name);
});
app.get("/me/avatar", RequireUserAuthentication, (req, res, next) => {
res.send(req.user.avatar);
});
What if we want to use RequireUserAuthentication on all routes instead of specific routes?
Example 2: Require user authentication for all routes.
We can use app.use(RequireUserAuthentication)
so the RequireUserAuthentication
middleware is 'injected' into all routes.
One thing to note is that middlewares are ordered. In code below, any routes defined before app.use(RequireUserAuthentication)
are not affected.
// This route is not affected by RequireUserAuthentication
app.get("/home", (req, res, next) => res.send(...));
// Require user auth for all routes defined after this line.
app.use(RequireUserAuthentication);
// Routes below are user sign-in required
app.get("/me/name", (req, res, next) => {
res.send(req.user.name);
});
app.get("/me/avatar", (req, res, next) => {
res.send(req.user.avatar);
});
Example 3: Parse JSON request body into object.
Sometimes it's useful to automatically convert request body into a JSON object, so we don't have to write this same logic for every single route:
// Converts request body into req.body as a javascript object
function JSONParser(req, res, next) {
if (req.headers['content-type'].startsWith('application/json')) {
const rawBody = readStreamIntoString(req);
req.body = JSON.parse(rawBody);
}
next();
}
// Apply JSONParser middleware to all routes defined after this line
app.use(JSONParser);
// Reads post name and content from req.body
app.get("/new/post", (req, res, next) => {
const postTitle = req.body.title;
const postContent = req.body.content;
...
});
// Reads username from req.body
app.get("/new/user", (req, res, next) => {
const userName = req.body.username;
...
});
Here we created a JSONParser
middleware that parses JSON request body into an object, and sets the object as req.body
. Later, the object is read from route /new/post
and any other routes defined after.
Example 4: Configurable Middlewares
Let's go fancy here by creating a 'factory' function to return middleware functions. A function for functions ๐ค
function BodyParser(options) {
if (options.type === "JSON") {
return (req, res, next) => {
if (req.headers["content-type"].startsWith("application/json")) {
const rawBody = readStreamIntoString(req);
req.body = JSON.parse(rawBody);
}
next();
};
} else if (options.type === "URL_ENCODED") {
return (req, res, next) => {
if (
req.headers["content-type"].startsWith(
"application/x-www-form-urlencoded"
)
) {
const rawBody = readStreamIntoString(req);
req.body = new URLSearchParams(rawBody);
}
next();
};
}
}
app.use(BodyParser({ type: "JSON" }));
app.use(BodyParser({ type: "URL_ENCODED" }));
In above code, we allow passing in an options
param to control which middleware function to return.
In fact, this is exactly what bodyParser does for parsing request bodies (of course with more sophisticated code).
What are some of your middleware functions used in production? Leave a comment below to share your favorites โค๏ธโค๏ธโค๏ธ!
Check out getd.io and leave some feedback on what features you'd like to see next โค๏ธโค๏ธโค๏ธ!
Top comments (15)
Nice post! ๐
Tip: you can add this article about
Chain of responsability
pattern so people that want to learn more about it can find easilyrefactoring.guru/design-patterns/c...
Nice Work..!
Can you please tell how can i upload my expressjs API's with mongoDB to live server with my domain hosting?
Thank you for reading!
Where are you deploying your web server and mongoDB?
for now i have it in my PC with localhost:3000
i am also curious about which server is needed for these Api's
lets suppose i have a linux base Hosting and i want to upload these api's there? is it possible? how?
There are many ways you can deploy your server, and this comment is not the best place to answer your question. Probably the easiest approach is to copy your js files to the server and node run from there. For mongodb you can either start your own instance on the server, or find some free servers online which are great for dev purpose. Good luck!
thanks for help.
Very nice post! Than you for sharing!
Maybe, there is a little bug in the functions returned by
BodyParser
, on last example. They aren't callingnext()
inside them. Is this correct? ๐คYep. Fixed. Good eye :D
Nice work! Contrate!!
offtopic, but why do people still use express when Koa is a thing? :D
Great post! Looking forward to more breakdowns!
This. is. brilliant!
Thanks for the simple and clear explanation on how the chaining works in express.
This is a treat of a post. Thank you โบ๏ธ
Good Post!
I'll refer your post my TIL blogging!