TIL/2021–01–25

Renzo Regio
9 min readJan 28, 2021

Day 45: Treehouse Full Stack JavaScript Techdegree

Express Basics Continuation

Today I learned about Express Middleware.

What is express Middleware?

We’ve already used two middlewares: body-parser and cookie-parser

An express application receives requests and sends responses

Middleware acts upon the request and then packaging up a response to send back to the end user

There can be as many pieces of middleware as you want to have before sending a response

The basic structure of middleware is a function with three parameters:

(req, res, next) => {

}

Inside the function, middleware can read and modify the request and response objects.

For example, the cookie-parser modifies the response object placing the cookie contents into it

Next is a function that must be called when the work is done — this triggers the middleware function after the current one to execute

To run middleware in response to requests, pass it into app.use(middleware) — this will run the middleware function for every request

For example:

app.use(bodyParser.urlencoded({ extended: false }));

app.use(cookieParser());

To only use it for a specific route, pass the route argument before the middleware function

app.use(‘/users’, (middleware))

You can also limit middleware to only use get requests, this is done by using get instead of use

app.get(middleware)

— —

Middleware sequence and routing

There is a sequence, earlier middleware functions runs before the later ones

app.use((req, res, next) => {

console.log(“one”);

next();

});

app.use((req, res, next) => {

console.log(“two”);

next();

});

app.use((req, res, next) => {

console.log(“three”);

next();

});

As you can see, it runs from top to bottom

And we can even have more than one function

app.use(

(req, res, next) => {

console.log(“one”);

next();

},

(req, res, next) => {

console.log(“one and a half”);

next();

}

);

app.use((req, res, next) => {

console.log(“two”);

next();

});

app.use((req, res, next) => {

console.log(“three”);

next();

});

You can either call one app.use and have many middleware functions inside like the one above or have more than one app.use method

Let’s make the middleware get triggered by the route we visit — meaning it will only run in that specific route

If you specify a route, it will only run/execute in that route

Note: if route is not specified, it will run in all routes

For this:

/hello = one, one and a half, three

/two = two

app.use(

“/hello”,

(req, res, next) => {

console.log(“one”);

next();

},

(req, res, next) => {

console.log(“one and a half”);

next();

}

);

app.use(“/two”, (req, res, next) => {

console.log(“two”);

next();

});

app.use(“/hello”, (req, res, next) => {

console.log(“three”);

next();

});

/hello

/two

You can also pass information between functions

Let’s use the req object to pass data from the first middleware function to the next middleware function

The idea is to pass data from one middleware function to the next, so .message does not mean anything — we created it by ourselves. We can also do .potato and it would do the same thing

app.use((req, res, next) => {

req.message = “This message came from the first middleware”;

next();

});

app.use((req, res, next) => {

console.log(req.message);

next();

});

And it works!

We just modified the request object for the first time — that is one of the powers of middleware. Allowing us to gather and compute data and send it back to the client. Like body-parser, it creates a body property on the request object and returns the object retrieved from the form

— -

How is middleware added to an express application? By passing the function app.use()

What objects does middleware have access to through its parameters? Request and response objects

Middleware functions always executes in a specific sequence

When is next called in express middleware? When the middleware’s work is done

— -

The next function and a closer look at middleware

Next

  • Signals the end of middleware functions
  • Basically if we don’t add next() to signal the end of the middleware function, the app will hang and it will not proceed to the next middleware function.
  • The app is waiting indefinitely for next to be called
  • Express relies on the next() function to know when to move forward

But next isn’t the only way to end a middleware function

When we send a response to the client, we are basically ending the function as well

Just like having res.redirect or res.send or res.render

You end middleware by either calling next() or sending a response

If you don’t do either of these two then the app will hang and it won’t proceed with any other code

If you notice, almost the entire app is made out of middleware

So basically the flow is that it checks each middleware from the top and works its way down. Then once it reaches the app.get or app.post (“route”) with the routes it checks if the current route matches the app.get or app.post. If it does then it proceeds with that code if not then it will skip and move to the next. *once it finds the matching route then it executes that middleware. Once there is a response, the request-response cycle ends and a response is sent to the client.

This process happens for every request express receives

The third party middleware that we use are closures (an outer function that returns an inner function)

— — -

Handling errors in an express app

Applications always needs to handle errors like our first error when we started the course (there was no get route for our app matching the URL that we requested)

In express you can use the next() function to signal an error in your app. By passing an object as a parameter to next, express knows that there is an error to handle — express will then immediately terminate and handle the error

Create an error object and place it inside the next() function

app.use((req, res, next) => {

console.log(req.message);

const err = new Error(“There was an error…”);

next(err);

});

And it will show up like this if an error does occur. In this case, the error I made was I made a path that does not match any get route in our app

— — -

Error handling middleware

We just learned how to tell express when there’s an error in an app. Now let’s tell express how to handle that error.

Error middleware

(err, req, res, next) => { }

We just added the error object — basically where express will pass the error object itself

When an object is passed to the next call, express jumps to the first middleware it can find with four parameters

On our app.js let’s add an error handler after the last middleware function

The error object has properties that hold the data about the error

We can render a template and pass in the err object so that it will give the template access to the error data

Error.message = the error message

error.status = for http status

error.stack = stack trace

app.use((err, req, res, next) => {

res.locals.error = err;

res.render(“error”);

});

On our error.pug

extends layout

block content

h1= error.message

h2= error.status

p= error.stack

And it works! But as you can see there is no http status message (404 because http status codes are not native to JavaScript error object )

To fix that :

The status code for a general error is 500 — normally if there is an error in the code on the server it will throw a status 500 — server couldn’t fulfill the request because something was unexpected

To give the template the access to that code, we can set 500 to be the status property of that error right after we create it

app.use((req, res, next) => {

console.log(req.message);

const err = new Error(“There was an error…”);

err.status = 500;

next(err);

});

The err object is just like any other javascript objects and properties can be set on it

app.use((err, req, res, next) => {

res.locals.error = err;

res.status(err.status);

res.render(“error”);

});

And it now works!

— —

Handling 404 errors

A 404 signals that the user requested a route that does not exist

When a user gets a request, it will go from one app.use call to the next until it finds the matching path — if it gets to the end without finding a route and there are no errors, express’s native handler will send a 404 back to the client with some plain text

If we catch a request before the end of the line we can render a better page to the user

Let’s write a middleware that handles 404 errors in our app

We want to access the request at the end of our app and before the error handler

This error will only be responsible for creating the error object and handing it off to the handler

Since express reads the code from top to bottom, any request that makes it that far will run the 404 error — because there is no path or route that matches the current path. Since we also placed the 404 error just after all the paths/routes.

The error handler itself will send the page out to the user in that event

app.use((req, res, next) => {

const err = new Error(“Page not found…”);

err.status = 404;

next(err);

});

app.use((err, req, res, next) => {

res.locals.error = err;

res.status(err.status);

res.render(“error”);

});

Total code

const express = require(“express”);

const bodyParser = require(“body-parser”); // returns “body” object to the browser — to access req.body.__

const cookieParser = require(“cookie-parser”); // returns an “cookies” object to the browser — to access req.cookies.___

const app = express();

app.use(bodyParser.urlencoded({ extended: false }));

app.use(cookieParser());

app.set(“view engine”, “pug”);

app.use((req, res, next) => {

req.message = “This message came from the first middleware”;

next();

});

app.use((req, res, next) => {

console.log(req.message);

next();

});

app.get(“/”, (req, res) => {

const name = req.cookies.username;

if (name) {

res.render(“index”, { name });

} else {

res.redirect(“/hello”);

}

});

app.get(“/cards”, (req, res) => {

res.render(“card”, {

prompt: “Who’s buried in grant’s tomb?”,

});

});

app.get(“/hello”, (req, res) => {

const cookies = req.cookies.username;

if (!cookies) {

res.render(“hello”); //reads the cookies saved on the browser and returns it to the template

} else {

res.redirect(“/”);

}

});

app.post(“/hello”, (req, res) => {

res.cookie(“username”, req.body.username); //Will send a cookie to the browser after we submit the form

res.redirect(“/”); //redirects the user to the root / welcome page

});

app.post(“/goodbye”, (req, res) => {

res.clearCookie(“username”); //clears the “username” cookie

res.redirect(“/hello”); //redirects back to the /hello route

});

app.use((req, res, next) => {

const err = new Error(“Page not found…”);

err.status = 404;

next(err);

});

app.use((err, req, res, next) => {

res.locals.error = err;

res.status(err.status);

res.render(“error”);

});

app.listen(3000, () => {

console.log(“The application is running on localhost:3000”);

});

— -

Locals can be set directly on the response object, on the locals property? True

res.local.name

What happens if we don’t implement our own error handler in the express app? Express handles error with default malware (not user friendly)

How is error handling middleware differentiated from other middleware? It has four parameters as opposed to three

Where is the best place to create a middleware that creates a 404 error?

After all the routes have been declared

What does a 404 error mean? Express couldn’t match the requested route to any of its route handlers

--

--