# Week 6: Middleware

  • AMA (15 min)
  • What is middleware
    • Types of middleware
    • Common use cases
  • Logging
  • Router modules
  • Image Handling
  • Validation
  • Exercise
  • More resources

# What is middleware

Middleware functions are used to encapsulate functionality that you want to apply to multiple routes without repeating the code in every route handler. They run before the route handlers are evaluated and may be chained together. Each middleware function can either end the request or pass control to the next function in the pipeline, until the final route handler is reached.

Middleware in ExpressJS and Node.js is a powerful feature that allows you to manipulate the incoming request and outgoing response in your application. It acts as an intermediary between the request and response, allowing you to perform tasks such as authentication, logging, and data validation.

In ExpressJS, middleware functions are added to the middleware stack using the app.use method. These functions take three arguments: req, res, and next. The req argument is the incoming request object, the res argument is the outgoing response object, and the next argument is a function that is called to move to the next middleware function in the stack.

# Types of middleware

An Express application can use the following types of middleware:

  • Application-level middleware
  • Router-level middleware
  • Error-handling middleware
    • different function signature
  • Built-in middleware
    • express.json()
    • express.urlencoded()x
    • express.static
  • Third-party middleware

# Common use cases

  • router modules
  • validation
  • image handling
  • error handling
  • enforcing security best practices

# Logging

Let's create a simple middleware function that logs information about the incoming request. To do this, we'll use the console.log method to log the request method, path, and date and time of the request.

const logMiddleware = (req, res, next) => {
  console.log(`${req.method} request made to ${req.path} at ${new Date()}`);
  next();
};

app.use(logMiddleware);

We can then use this middleware function in our application by adding it to the middleware stack using the app.use method. This will ensure that the function is called for every incoming request.

# Router Modules

Middleware refers to the functions that are executed before a request reaches the route handler. These functions can perform tasks such as logging, authentication, and data validation. They are added to the middleware stack using the app.use method in ExpressJS and are executed in the order they are added.

Routes refer to the endpoints in your application that handle incoming HTTP requests. They are defined using the methods on the ExpressJS app object, such as app.get, app.post, app.put, and app.delete. The route handler is a function that is executed when a matching request is received.

Here is an example of how middleware and routes can work together in an ExpressJS application:

const express = require('express');
const app = express();

// Define a middleware function that logs the incoming request
const logMiddleware = (req, res, next) => {
  console.log(`${req.method} request made to ${req.path} at ${new Date()}`);
  next();
};

// Use the log middleware
app.use(logMiddleware);

// Define a route that returns "Hello World"
app.get('/', (req, res) => {
  res.send('Hello World');
});

// Start the server
app.listen(3000, () => {
  console.log('Server started on port 3000');
});

In this example, we define a middleware function that logs the incoming request, and use it in our application using the app.use method. We also define a simple route that returns "Hello World".

When a request is made to the "/" endpoint, the logMiddleware function will be executed first, logging the incoming request to the console. The request will then be passed to the route handler, which will return "Hello World" as the response.

This is just a simple example of how middleware and routes can work together in an ExpressJS and Node.js application. In a real-world application, you would likely have more complex middleware functions and routes, and may also use additional libraries such as Mongoose for data storage.

# Image Handling

In a software developmet for Web/Mobile images plays a crucial role so let's learn how to handle image uploads in an ExpressJS application using middleware. We'll be using the multer (opens new window) library to handle the image file upload process.

First, you need to install multer (opens new window) in your project. You can do this by running the following command in your terminal:

npm install multer

Next, you need to require multer in your application and set up a storage engine for the uploaded files. There are several storage engines available in multer, but in this tutorial, we'll be using the diskStorage engine, which will store the uploaded files on the server's disk.

const multer = require('multer');
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, 'public/uploads');
  },
  filename: (req, file, cb) => {
    cb(null, `${Date.now()}-${file.originalname}`);
  },
});

In the code above, we set up the diskStorage engine by passing an object to its constructor. The object has two properties: destination and filename. The destination property is a function that takes three arguments: req, file, and cb. The function specifies where the uploaded files should be stored on the server. In this case, the files will be stored in the public/uploads directory. The filename property is another function that takes three arguments and specifies how the uploaded files should be named. In this case, the files will be named with a timestamp and the original file name.

Once the storage engine is set up, we need to create a middleware function that will handle the file upload process. This function will take care of parsing the incoming request, extracting the file, and storing it on the server.

const upload = multer({ storage });

app.post('/upload', upload.single('image'), (req, res) => {
  res.status(200).send({
    message: 'Image uploaded successfully',
    image: `uploads/${req.file.filename}`,
  });
});

In the code above, we create a upload middleware function using the multer function and passing in the storage engine we set up earlier. We then use the upload.single method to specify that we're only allowing a single file to be uploaded. The method takes two arguments: the name of the file input in the form and the middleware function that will handle the file upload process. In this case, the middleware function will return a 200 OK response with a message indicating that the image was uploaded successfully, as well as the URL of the uploaded image.

With these steps, you have a basic setup for handling image uploads in an ExpressJS application using middleware. You can extend this setup by adding more functionality, such as image resizing, cropping, and validation, using additional middleware functions.

# Validation

Validation is an important aspect of building a robust and secure web application in ExpressJS and Node.js. It ensures that incoming data from the client is valid before processing it further.

Here's an example of how to use middleware for data validation in ExpressJS:

const validateMiddleware = (req, res, next) => {
  const { name, age } = req.body;
  if (!name || !age) {
    return res.status(400).send({ error: 'Name and age are required' });
  }
  next();
};

app.post('/users', validateMiddleware, (req, res) => {
  // Add the new user to the database or in an array
});

In this example, we use destructuring to extract the name and age properties from the req.body object. We then check if these properties are present and return a 400 Bad Request response if they are not. If the properties are present, the function calls next to move to the next middleware function in the stack or to the route handler if there are no more middleware functions.

It's also possible to use external libraries, such as Joi (opens new window) or Express Validator (opens new window), for more complex validation logic. These libraries provide a convenient way to write validation rules and return informative error messages in case of validation failure.

Middleware validation is a crucial aspect of building a secure and reliable web application. It helps ensure that incoming data from the client is valid before processing it further, and helps prevent security issues and unexpected behavior in the application.

# Exercise

  • Create a new project in your mad9124 repo under week6 and install express and nodemon.
  yarn init 
  yarn add express
  yarn add -D nodemon
  • Add your start and dev scripts to package.json:
{
  // package.json
  ...
  "scripts": {
    "dev": "nodemon src/index.js",
    "start": "node src/index.js"
  }
}
  • Create the src/index.js file and add the express app basics.
  'use strict'

  const expres = express();
  const app = express();

  // Application level middleware ehre

  // Routes here
  app.get('/', (_req, res) => {
    res.send('Server Runnimg..')
  })

  const PORT = process.env.PORT || 3000;
  app.listen(PORT, () => {
    console.log(`App listening on port ${PORT}`);
  })
  • Create a new middleware function that will validate the incoming user data. This function should check if the name and email properties are present in the req.body object. If either of these properties is missing, return a 400 Bad Request response with a message "Name and email are required".
// src/middleware/validateUserData.js
const validateUserData = (req, res, next) => {
  // your code here
}

module.exports = validateUserData;
  • Create a new route for adding (Post request) a user. This route should use the validation middleware created in step 2 and add the user to an in-memory database (an array). If the validation passes, return a 201 Created response with the newly created user object.
  // src/routes/user.js
  const { Router } = require('express');
  const userRouter = Router();
  
  userRouter.post('/', ...)
  • Make sure to import and export correctly to and from each module
  • Start the Express server and test the user creation route using a Postman.
  • Once you are confident it is running as expected, commit your changes, push to github, and submit with a link to your github repo in Brightspace!

# More resources

Last Updated: 2/13/2023, 9:38:25 PM