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'));
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'));
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'));
Then we see:
middleware 1 called
middleware 2 called
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'));
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();
})
wasn’t called.
We can call next
as follows:
next('route');
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'));
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'));
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'));
Then we see:
middleware 1 called
middleware 2 called
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'));
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.