Skip to content

Instantly share code, notes, and snippets.

@zcaceres
Created May 22, 2017 16:41
Show Gist options
  • Save zcaceres/90e29e274fe92eac1cb2a251bf6a1420 to your computer and use it in GitHub Desktop.
Save zcaceres/90e29e274fe92eac1cb2a251bf6a1420 to your computer and use it in GitHub Desktop.

Middleware

Middleware refers to modules that are chained 'between' when your Express app is instantiated and when your server is launched with app.listen(myPortNumber).

We'll stick to two easy examples that illustrate the usefulness of middleware: logging and static resources.

Registering and Using Middleware

Using middleware is simple. Use Node.js to require the package that you'd like to use. Then add the middleware to your app by calling it with .use(). The order that you call your middleware may matter, so look in the docs for your middleware to make sure it's well-positioned.

Here's how we would deploy the logging middleware Morgan.

First, npm install morgan. We'll use the same express configuration that we did before.

        const express = require('express'); // makes Express available in your app.
        const app = express();  // Creates an instance of Express, which allows us to begin routing.
        const morgan = require('morgan'); // Require our middleware


        app.use(morgan('dev')); // use our middleware. Check docs for the mode ('dev') you'd like to use.


        app.listen(3000);  // Starts up our server on port 3000.
        module.exports = app;  // Allows other files to access our Express app.

That's it!

Static Resources

One extremely useful bit of middleware is Express' static resource routing method.

As we saw before, all of your assets must be routed properly in your application. Without static resource routing, commonly-used assets like a logo must be routed relative to every page that uses them. What a nightmare!

Thankfully, Express allows us to set a folder as our 'static' directory. This directory, usually called 'public', holds commonly-shared files like a logo image. Here's how we would .use() static routing.

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

      app.use(morgan('dev'));

      app.use('/static', express.static(__dirname + '/public')); // Sets up static resource routing to our /public directory

      app.listen(3000);
      module.exports = app;

Once configured, your HTML can reference any file in your 'public' directory by putting '/static' in front of the route. A logo could be referenced as '/static/kittensLogo.png' on every page your website. No more painful manual routing for your shared files.

You might notice that our call to .use() looks similar to any other route. It is. When our application makes a request to a file at '/static', our express.static router takes over and serves up the file from our public directory. This means that the path '/static' is just a convention. Since .use() operates just like any other route, so you can specify whatever you want.

Body Parsing

If you call, our HTTP request object sometimes contains a body (payload) of information from the user. For various reasons that are beyond the scope of this article, parsing the data from this body can be annoying.

Thankfully, Body Parser exists. Body Parser can be configured to automatically parse JSON and URL encoded strings in HTTP. As with other middleware, set up is simple.

First npm install body-parser. Then, using our original Express configuration:

      const express = require('express');
      const app = express();
      const morgan = require('morgan');
      const bodyParser = require('body-parser'); // Require body-parser

      app.use(morgan('dev'));
      app.use(express.static(__dirname + '/public'));

      // BODY PARSER
      app.use(bodyParser.urlEncoded()); // Tells Express to use body-parser to parse url encoded strings
      app.use(bodyParser.json()); // Tells Express to use body-parser to parse JSON


      app.listen(3000);
      module.exports = app;

Once you have Body Parser configured, you'll be able to access the .body property on your request object. The body is itself an object, which contains whatever came through the request. For example, if a user were to send form data for a field title 'username', we could access their input by using request.body.username. You can then use the body to do whatever you want in your route.

      // Our route to handle requests to /kitten
      kittens.get('/', function (request, response, next) {
        let myBody = request.body;
        response.render(myKittenPage, myBody)
      });

Error-Handling Middleware

Since your Express app relies on asynchronous functionality, you will need to handle your errors. Errors are typically handled with a .catch() at the end of a promise chain.

Here's how that would look:

app.get('/path', function (req, res) {
  AsyncFunction()
  .then(function(resultsOfAsync) {
    // do stuff
  })
  .then(function(moreResultsOfAsync) {
    // do more stuff
  })
  .catch(console.error) // If an error is thrown, our catch block will process it and error log it in the console.
})

This approach will work fine. But as your program grows, you'll find yourself writing many catch blocks. What happens if you want to do something a bit more customized with your error? Perhaps you want to color it in your console, do additional processing, or have some fallback response. You'll need to re-create this code in every .catch() – not exactly a DRY approach.

Express allows us to centralize error handling by defining error-handling middleware.

First, we can head to our main app (the same file we used in previous examples). We'll define our single, error-handling middleware here.

      const express = require('express');
      const app = express();
      const morgan = require('morgan');
      const bodyParser = require('body-parser');

      app.use(morgan('dev'));
      app.use(express.static(__dirname + '/public'));

      app.use(bodyParser.urlEncoded());
      app.use(bodyParser.json());

      // ERROR-HANDLING MIDDLEWARE
      app.use(function(err, req, res, next) { // A function with these four parameters will always be treated as error-handling middleware by Express.
        console.error(chalk.red.underline.bold(err)); // turns our error red, underlines it, and bolds it
        res.send(404); // sends a 404 to our user
      });

      app.listen(3000);
      module.exports = app;

We call app.use() so that Express will use our error handling on every route – if there's an error. We can define anything we want within our function(err, req, res, next). To create error-handling middleware, we just need to create a function with four parameters inside our app.use. Express will always see this as error-handling. Our first parameter will be our error.

There's two big benefits to this approach. First, we can do so much within our error handler, since we have access to req, res, and next.

Second, we can rewrite all of our .catch() blocks in an extremely simple way:

app.get('/path', function (req, res) {
  AsyncFunction()
  .then(function(resultsOfAsync) {
    // do stuff
  })
  .then(function(moreResultsOfAsync) {
    // do more stuff
  })
  .catch(next) // If an error is thrown, send it to our Error-Handling Middleware!
})

Now, we can use .catch(next) in every Promise chain. Our error-handing middleware will catch the error and do whatever we defined to handle it.

That wraps up our exploration of Express.js. Happy building!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment