Middleware is commonly used to implement shared functional requirements in web applications. It is an integral part of web applications, just like libraries, as it helps developers avoid code duplication and encourages separation of concerns.
This guide focuses on middleware used in the context of client and server applications communicating over HTTP, as discussed in this blog post. The author describes middleware as code that wraps around a web application object to do tasks before and/or after the web application is called. These tasks typically include exporting observability data, performing application-wide authentication and authorization, and caching results.
Axios:
Interpreter
export const requestInterceptor = function (requestConfig) { requestConfig.startTime = Date.now(); console.log("url=%s method=%s", requestConfig.url, requestConfig.method); return requestConfig; }; export const responseInterceptor = function (response) { console.log( "url=%s method=%s status=%s latency=%s", response.config.url, response.config.method, response.status, Date.now() - response.config.startTime ); return response; }; export const errorInterceptor = function (error) { if (error.response) { console.log( "url=%s method=%s error=%s status=%s", error.response.config.url, error.response.config.method, error.response.data, error.response.status ); } else if (error.request) { console.log(error.request); } else { console.log("Error", error.message); } return Promise.reject(error); };
Usage
import axios from "axios"; const axiosGithub = axios.create(); axiosGithub.interceptors.request.use(requestInterceptor, errorInterceptor); axiosGithub.interceptors.response.use(responseInterceptor, errorInterceptor);
Express
Application-level middleware
function logOriginalUrl (req, res, next) { console.log('Request URL:', req.originalUrl) next() } function logMethod (req, res, next) { console.log('Request Type:', req.method) next() } const logStuff = [logOriginalUrl, logMethod] app.get('/user/:id', logStuff, (req, res, next) => { res.send('User Info') })
Router-level middleware
const express = require('express') const app = express() const router = express.Router() // a middleware function with no mount path. This code is executed for every request to the router router.use((req, res, next) => { console.log('Time:', Date.now()) next() }) // a middleware sub-stack shows request info for any type of HTTP request to the /user/:id path router.use('/user/:id', (req, res, next) => { console.log('Request URL:', req.originalUrl) next() }, (req, res, next) => { console.log('Request Type:', req.method) next() }) // a middleware sub-stack that handles GET requests to the /user/:id path router.get('/user/:id', (req, res, next) => { // if the user ID is 0, skip to the next router if (req.params.id === '0') next('route') // otherwise pass control to the next middleware function in this stack else next() }, (req, res, next) => { // render a regular page res.render('regular') }) // handler for the /user/:id path, which renders a special page router.get('/user/:id', (req, res, next) => { console.log(req.params.id) res.render('special') }) // mount the router on the app app.use('/', router)
Error-handling middleware
app.use((err, req, res, next) => { console.error(err.stack) res.status(500).send('Something broke!') })
Other resources
Go
- Middleware Patterns in Go, a discussion of server-side middleware patterns
- Writing HTTP Client Middleware in Go, a blog post by the author that shows how to avoid external HTTP API interactions using client-side middleware
- The book Practical Go by the author, which describes in detail how to implement middleware in client and server applications
- Github/go-fault, which demonstrates how to use server-side middleware for chaos engineering
Javascript
- Axios library documentation on Interceptors
- Writing Middleware and Using Middleware from the Express documentation, which are useful for learning more about middleware in Express
Python
- Requests-Middleware, a package that implements various middleware for the Requests library