Using Express Middleware

John Au-Yeung - Jan 24 '21 - - Dev Community

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

Middleware functions are functions that have access to the request and response objects, and the next function for call the next middleware.

In this article, we’ll look at what Express middleware does and how we can use them.

Characteristics of Middleware

Middleware functions can run any code, make changes to the request and response object, end the request-response cycle, and call the next middleware in the stack.

Application-Level Middleware

We can run middleware that are used when any routes are called by passing it to the app.use method as a callback.

If we want to a middleware function to be called only when a request of one request method is called, then we can pass it to the app.METHOD method as a callback. Where METHOD is get , post , put , delete , etc.

For example, we can write a middleware function and pass it into app.use to log the request method of a request as follows:

const express = require('express')  
const app = express()  
app.use(express.json())  
app.use(express.urlencoded({ extended: true }))
app.use((req, res, next) => {  
  console.log(req.method);  
  next();  
});

app.get('/', (req, res) => {  
  res.json();  
})

app.listen(3000, () => console.log('server started'));
Enter fullscreen mode Exit fullscreen mode

Then we should get GET logged when we make a GET request to / .

We can restrict the middleware function to be run only on GET requests and to the path / by writing:

const express = require('express')  
const app = express()  
app.use(express.json())  
app.use(express.urlencoded({ extended: true }))
app.get('/', (req, res, next) => {  
  console.log(req.method);  
  next();  
});

app.get('/', (req, res) => {  
  res.json();  
})

app.post('/', (req, res) => {  
  res.json();  
})

app.listen(3000, () => console.log('server started'));
Enter fullscreen mode Exit fullscreen mode

Then we only see GET logged when we make GET requests to the / path.

Running a Series of Middleware Functions

We can use the next method to call the next middleware function in the series, so we can use it to chain middleware function calls together.

For example, if we have:

const express = require('express')  
const app = express()  
app.use(express.json())  
app.use(express.urlencoded({ extended: true }))
app.use(  
  (req, res, next) => {  
    console.log('middleware 1 called');  
    next();  
  },  
  (req, res, next) => {  
    console.log('middleware 2 called');  
    next();  
  }  
);

app.get('/', (req, res) => {  
  res.json();  
})

app.listen(3000, () => console.log('server started'));
Enter fullscreen mode Exit fullscreen mode

Then we see:

middleware 1 called  
middleware 2 called
Enter fullscreen mode Exit fullscreen mode

from the console.log output of each middleware.

If we send a response in our middleware, then the next one won’t get called. For example, if we have:

const express = require('express')  
const app = express()  
app.use(express.json())  
app.use(express.urlencoded({ extended: true }))  
app.get('/',  
  (req, res, next) => {  
    next()  
  },  
  (req, res, next) => {  
    res.send('Second middleware');  
  }  
)

app.get('/',  (req, res, next) => {  
  res.end();  
})

app.listen(3000, () => console.log('server started'));
Enter fullscreen mode Exit fullscreen mode

Then we get the output ‘Second middleware’ when we make a request to / .

Our route handler in:

app.get('/', function (req, res, next) {  
  res.end();  
})
Enter fullscreen mode Exit fullscreen mode

wasn’t called.

We can call next as follows:

next('route');
Enter fullscreen mode Exit fullscreen mode

to go straight to our route handler, bypassing the other middleware functions down the line.

For example, if we have:

const express = require('express')  
const app = express()  
app.use(express.json())  
app.use(express.urlencoded({ extended: true }))  
app.get('/:id',  
  (req, res, next) => {  
    if (req.params.id === '0') {  
      next('route');  
      return;  
    }  
    next();  
  },  
  (req, res, next) => {  
    res.send('Second middleware');  
  }  
)

app.get('/:id', (req, res, next) => {  
  res.end(req.params.id);  
})

app.listen(3000, () => console.log('server started'));
Enter fullscreen mode Exit fullscreen mode

Then when we make a request to /0 , then we get 0 . Otherwise, we get ‘Second Middleware’ outputted.

Router Level Middleware

Router level middleware works the same way as app-level middleware, but they’re bound to the instance of the express.Router() instead of the express instance.

For example, we can use it as follows:

const express = require('express')  
const app = express()  
const router = express.Router();
app.use(express.json())  
app.use(express.urlencoded({ extended: true }))

router.use((req, res, next) => {  
  req.requestTime = new Date();  
  next();  
})

router.get('/', (req, res, next) => {  
  res.json(req.requestTime);  
})

app.use('/', router);

app.listen(3000, () => console.log('server started'));
Enter fullscreen mode Exit fullscreen mode

Then when we make a request to the / route we get back the timestamp as the output.

Chaining middlewares and skipping routes work the same way as app-level middleware. For example, we can write the following to chain middlewares as follows:

const express = require('express')  
const app = express()  
const router = express.Router();

app.use(express.json())  
app.use(express.urlencoded({ extended: true }))

router.use(  
  (req, res, next) => {  
    console.log('middleware 1 called');  
    next();  
  },  
  (req, res, next) => {  
    console.log('middleware 2 called');  
    next();  
  }  
)

router.get('/', (req, res, next) => {  
  res.json();  
})

app.use('/', router);

app.listen(3000, () => console.log('server started'));
Enter fullscreen mode Exit fullscreen mode

Then we see:

middleware 1 called  
middleware 2 called
Enter fullscreen mode Exit fullscreen mode

from the console.log output of each route middleware.

We can use next('route') to skip to the route as follows:

const express = require('express')  
const app = express()  
const router = express.Router();
app.use(express.json())  
app.use(express.urlencoded({ extended: true }))

router.get('/:id',  
  (req, res, next) => {  
    if (req.params.id === '0') {  
      next('route');  
      return;  
    }  
    next();  
  },  
  (req, res, next) => {  
    res.send('Second middleware');  
  }  
)

router.get('/:id', (req, res, next) => {  
  res.end(req.params.id);  
})

app.use('/', router);
app.listen(3000, () => console.log('server started'));
Enter fullscreen mode Exit fullscreen mode

Then when we make a request to /0 , then we get 0 . Otherwise, we get ‘Second Middleware’ outputted.

Conclusion

Using Express middlewares is simple. We can either pass them into app.use to make the middleware run for all request methods, or app.METHOD to make them run for the given method.

We can call next to call the next middleware, and call next('route') to call the route handler directly from a middleware.

Everything applies to both route and app-level middleware, except that they bind to the express.Router() and express() respectively.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .