express middleware

pre reqs

  • We're going to assume you know the basics of Express
  • Node latest LTS (v12.16.2)
  • Postman
  • Yarn

getting started

in a world...

...without middleware...

yarn run v1.22.0
$ yarn build && nodemon ./lib/index.js
$ rimraf ./lib && babel src -d lib
Successfully compiled 4 files with Babel.
[nodemon] 2.0.3
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node ./lib/index.js`
Application listening on port 3000
$ yarn dev

womp womp...

Phase 2???

Listen for Requests

Resolve the output

middleware functions

const genericMiddleware = (
  req /* Request Object */,
  res /* Response Object */,
  next /* Next Callback */)
=> {
  // do something
  // then use the response object and end the chain
  //   OR
  // call the next() callback to execute the next middleware.
}

our first middleware

// new file: ./src/server/middleware/simpleLogger.js

const simpleLogger = (req, res, next) => {
  console.log(`Request Type: ${req.method} received at ${Date.now()}`);
  next(); 
}

export default simpleLogger; 
// ./src/server/index.js
import express from "express";
import simpleLogger from './middleware/simpleLogger'; // new!

const launchServer = (port) => {
  const app = express();

  app.use(simpleLogger); // new!

  return app.listen(port, () => {
    console.log(`Application listening on port ${port}`);
  });
};

export default launchServer;

yay

$ yarn dev
yarn run v1.22.0
$ yarn build && nodemon ./lib/index.js
$ rimraf ./lib && babel src -d lib
Successfully compiled 6 files with Babel.
[nodemon] 2.0.3
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node ./lib/index.js`
Application listening on port 3000
Request Type: GET received at 1587070701458

application level middleware

  • middleware bound to the "app" object returned by express();
  • app.use (applies to all methods)
  • app[method] applies to specific methods. 
    • app.get
    • app.post
    • app.patch
    • app.put
    • app.delete
  • checkout
  • copy
  • head
  • lock
  • merge
  • mkactivity
  • mkcol
  • move
  • m-search
  • notify
  • options
  • purge
  • report
  • search
  • subscribe
  • trace
  • unlock
  • unsubscribe

application paths

for either app.use or app[method]; 

const launchServer = (port) => {
  const app = express();

  app.use(simpleLogger); 
  
  /* NEW STUFF! */
  
  app.get('/hello-world', (req, res) => {
    res.send('Hello World!')
  })
  
  /* END NEW STUFF */

  return app.listen(port, () => {
    console.log(`Application listening on port ${port}`);
  });
};

export default launchServer;

localhost:3000/hello-world

bit basic?

types of middleware

  • application middleware - for the entire app
  • router middleware - for a specific Router instance (advanced Express, but basically the same as application middleware)
  • Built-in middleware (by Express itself)
  • Error-Handling Middleware (a special case)
  • Third Party Middleware (one of the above, but written as an npm package)

error handling middleware

for when things go a little bit oopsy-doodle

app.use(function (err, req, res, next) {
  console.error(err.stack)
  res.status(500).send('Something broke!')
})

Note that this takes FOUR arguments, not three, with the first argument being "err"

Also, you define error-handling middleware after all your other app.use() and other route calls. 

throwing an error

app.get('/', function (req, res, next) {
  fs.readFile('/file-does-not-exist', function (err, data) {
    if (err) {
      next(err) // Pass errors to Express.
    } else {
      res.send(data)
    }
  })
})

If next is passed *ANY* parameter other than the string "route", Express will regard the current request as being an error, and will skip any remaining non-error handling routing and middleware functions. 

third party middlewares

  • body-parser - parses HTTP request body
  • cors - allows cross-origin-resource sharing
  • morgan - HTTP request logger
  • passport - Authentication/Authorisation library
  • session - establish server-based sessions
  • timeout - set a timeout period for http request processing

advanced middleware

paths can have multiple routes

  // handler for the /user/:id path, which prints the user ID to the console
  app.get('/user/:id', function (req, res, next) {
    console.log('ID:', req.params.id)
    next()
  })
  
  // handler for the /user/:id path, which prints the user ID to the browser
  app.get('/user/:id', function (req, res, next) {
    res.send(`User is ${req.params.id}`)
  })

advanced middleware

Routes can have more than one middleware

  // handler for the /user/:id path, which prints the user ID to the console
  app.get(
    "/user/:id",
    (req, res, next) => {
      console.log("ID:", req.params.id);
      if (["bozo", "pennywise", "krusty", "ronald"].includes(req.params.id)) {
        console.warn("CLOWN DETECTED");
        next(); // go to the next function in this function chain;
      } else {
        next("route"); // go to the next matching route, skip the other functions;
      }
    },
    (req, res, next) => {
      res.send(`Hey! I thought I told you clowns to get out of here!`);
    }
  );

  // handler for the /user/:id path, which prints the user ID to the browser
  app.get("/user/:id", (req, res, next) => {
    res.send(`User is ${req.params.id}`);
  });

questions?

Express Middleware

By brianboyko

Express Middleware

  • 468