MiddleWare Express.js

PHOTO EMBED

Fri Mar 08 2024 06:07:08 GMT+0000 (Coordinated Universal Time)

Saved by @Marcelluki

Improving Code Using Middleware

Optional Video
In the previous lessons, we created routes inside of our Express application, and we wrote code to process user requests:

router.get('/users/:id', (req, res) => {
  // request processing begins here
  const { id } = req.params;

  if (!users[id]) {
    res.send({ error: `This user doesn't exist` });
    return;
  }

  const { name, age } = users[req.params.id];
  // the request is sent. request processing ends here
  res.send(`User ${name}, ${age} years old`);
});
The code above is perfect for a simple task like this. However, we will often find ourselves faced with much more complex tasks. For example, we might need to implement error logging or a user authentication system. If we try to describe all our logic inside of routers, our code will get longer, and eventually it will become impossible to manage.

Middleware functions are the perfect solution when dealing with these types of problems. Middleware functions execute during the lifecycle of a server request. These functions allow us to write our request processing code inside a separate module, which in turn allows for shorter code in the router itself.

Additionally, middleware is a great place to move code that will be repeatedly executed. For example, let's imagine a scenario where we need to repeatedly query a database. For each request, the server must check whether or not the requested data exists in the database. Instead of writing the code to do this inside of each router inside the application, we can simply describe this logic inside our middleware functions.

Creating a Middleware Function
Let's split our router code into separate functions. The first function will check whether or not the user actually exists in the database. If this user does exist, the second function will send back that user's ID token.

Let's start off with a response:

// move the code for the server's response into a separate function
const sendUser = (req, res) => {
  const { name, age } = users[req.params.id];
  res.send(`User ${name}, ${age} years old`);
};
Now let's move on to the next function, our middleware. This function should:

Check whether the user exists
Send an error message if the user doesn't exist
Call the function that sends the user's name and age, if the user is found. This will be the sendUser() function that was coded earlier.
Let's translate this list into actual JavaScript:

// check whether the user exists
const doesUserExist = (req, res, next) => {
  if (!users[req.params.id]) {
    res.send(`This user doesn't exist`);
    return;
  }

  next(); // call the next function
};

const sendUser = (req, res) => {
  const { name, age } = users[req.params.id];
  res.send(`User ${name}, ${age} years old`);
};
If the user exists, we'll call whichever function was passed to doesUserExist() as the third argument. Let's create a request handler where we can call this function:

router.get('/users/:id', doesUserExist); // here it is
router.get('/users/:id', sendUser);
In the example above, we have two requests that both share the same path, '/users/:id'. Let's imagine that a user navigates to /users/2. First, the doesUserExist() middleware will be executed. Inside of this middleware function, we call next() and the code inside of the second handler, sendUser() will begin to execute. This allows us to chain together as many middleware functions as we need. Without calling next() we would be limited to one middleware for that path.

Let's take a look at this code one more time. Here's the doesUserExist() function:

const doesUserExist = (req, res, next) => {
  if (!users[req.params.id]) {
    res.send(`This user doesn't exist`);
    return; // if no user, we'll exit the function and nothing else will happen
  }

  next(); // if the engine reaches next(), it'll look for the next handler of the same request
};
Let's suppose we need yet another middleware to check a user's access level. We can write this function and execute the next() callback inside its body. After doing that, we'll insert our new handler in between the previously existing middleware functions:

// a middleware function already in use
router.get('/users/:id', doesUserExist);
// a new independent middleware function
router.get('/users/:id', doesUserHavePermission); 
// another middleware function already in use
router.get('/users/:id', sendUser);
It's really important to remember that the callback in our middleware functions should be named next(). In theory, it could be named anything, but in order to make your code easy for other engineers to read, we recommend following this convention.

Also, we can add a series of middleware functions to one request handler for the same path. So, the example above can be rewritten like this:

router.get('/users/:id', doesUserExist, doesUserHavePermission, sendUser);
Connecting Middleware
In the examples above, we passed middleware functions as the second argument inside of router.get(). However, sometimes we want to implement some middleware globally throughout the app. In order to do this, we'll use the use() method. We'll just pass one parameter, which will be the middleware itself, like so:

app.use(middleware);
This is how it looks inside of index.js:

// index.js

const express = require('express');
const routes = require('./routes.js');

const { PORT = 3000 } = process.env;
const app = express();

const logger = (req, res, next) => {
  console.log('The request has been logged!');
  next();
};

app.use(logger);
app.use('/', routes);

app.listen(PORT, () => {
  console.log(`App listening on port ${PORT}`);
});
content_copyCOPY

https://tripleten.com/trainer/web/lesson/4b5486c8-4d5a-425f-b7bf-8d9af7e84c9f/task/c2f63f67-a42e-4614-bfaf-1a29192f8ff1/?from