Routing refers to how application endpoints (URI) respond to client requests.
In the simplest Express application, routing is defined as an Express object app corresponding to various HTTP methods as app.get() to respond to GET requests and app.post() to respond to POST requests.
All routes are defined with a callback function. The application keeps listening to the requests on API and whenever there is any request with matching URI (also referred to as endpoint) and HTTP methods the defined callback is invoked.
Every callback function has access to req and res. Req is an object which contains all the information about the HTTP request that invoked this particular callback. In response to req, we utilize res to respond back to the client who made the request.
Sometimes more than one callback method is defined to handle authentication, authorization, or data validation; these are termed as middlewares. When there are multiple callbacks, every function except callback receives next() as a functional argument and must call next() when its execution is complete to hand over control to the next callback.
Here is simple app.js which demonstrates app.get()
and app.post()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const express = require("express"),
app = express();
app.get("/root", function (req, res) {
res.send("You are at root");
});
app.post("/root", function (req, res) {
res.send("You called post request to root");
});
//setting port for api
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log("Server running on port 8080");
});
In this app, the server is listening at port 8080 and responds with messages as per route. This is the simplest way to define a route, but it’s not very convenient when we are building a large-scale application.
Before we move on to further discussion on route implementation types, let’s understand types of methods given with app object first.
This is used to listen to all types of requests coming to /api such as GET, POST, PUT, DELETE and other HTTP methods. It is usually used to implement authentication checks on a particular route.
1
2
3
4
app.all('/api', function (req, res, next) {
console.log('Welcome to api section ...')
next() // passes control to next callback
})
Used to listen to HTTP DELETE requests, (which is used to request deletion of data). More about DELETE.
1
2
3
app.delete('/', function (req, res) {
res.send('DELETE request to root')
})
Used to listen to HTTP GET requests. More about GET.
1
2
3
app.get('/', function (req, res) {
res.send('GET request to root')
})
Used to listen to HTTP POST requests. More about POST.
1
2
3
app.post('/', function (req, res) {
res.send('POST request to root')
})
Used to listen to HTTP PUT requests. More about PUT.
1
2
3
app.put('/', function (req, res) {
res.send('PUT request to root')
})
Used to listen to HTTP PATCH requests. More about PATCH.
1
2
3
app.patch('/', function (req, res) {
res.send('PATCH request to root')
})
It returns a single instance of a single route. You can call all HTTP methods and optional middlewares as follows:
1
2
3
4
5
6
7
8
9
10
app.route('/path')
.all(function (req, res, next) {
// route specific middleware!
})
.get(function (req, res, next) {
res.json({});
})
.post(function (req, res, next) {
// post request ..
})
Let’s discuss route path/URI now.
Path and method together define an endpoint where requests can be made. Path can be a normal string or a regex string.
The hyphen (-) and dot (.) are interpreted literally while the characters +, -, *, ( ), :, ? are interpreted as regex parts.
A normal path is called as:
app.get(’/about’, function (req, res) {
res.send(‘about’)
})
.
Here the get method is defined for “/about” path.
Regex matching:
? character
“as?df” will match “asdf” and “adf”
+ character:
“as+df” will match “asdf” ,“assdf”, ”asssdf” and so on
* character:
“as*df” will match any string that starts with as and ends with df such as “ascdf”, “asxghdf” etc
()? character:
“as(ab)?df” will match “asabdf” and “asdf” because ? represents character before ? may or may not appear.
You can send parameters in route that can be accessed inside callback functions as req.prams.parameter_name.
Here is an example on how to get info from a user on the basis of ID we can write:
1
2
3
app.get('/users/:userId/', function (req, res) {
res.send(req.params.userId);
})
Such parameters mentioned above are optional parameters.
Parameter value can’t be null or undefined, otherwise the request will cause an error.
Using regex with params:
You can also use regex strings in place of simple parameter names for strict matching.
For example, the given path will match only if the provided userId contains only digits.
1
2
3
app.get('/users/:userId(\d+)/', function (req, res) {
res.send(req.params.userId);
})
The above endpoint will match URI “https://users/42” but will result in an error 404 for a request with URI “https://users/ab42”.
Similarly, more complex regex strings can be used to implement exact matching of parameters.
Express.Router module is used to create modular and mountable route handlers. We define all routes related to an entity in a separate file and then use that file to mount API routes over a path.This makes our project more manageable as we divide our routes into separate files on some criteria.
In the example given below we are defining all routes related to movie path and then we can mount this module by importing it in app.js (main server file) and using it by calling:
1 2
const movieRoutes = require("path_to_route_file") app.use("/api", movieRoutes);
The route file:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
var express = require('express')
var router = express.Router()
// middleware that is specific to this router
router.use(function timeLog(req, res, next) {
console.log('Time: ', Date.now())
next()
})
// define the home page route
router.get('/', function (req, res) {
res.send('Movies home page')
})
// define the about route
router.get('/about', function (req, res) {
res.send('About Movie')
})
//route to add movies
router.post('/', function (req, res) {
res.send('Added movie')
})
//route to delete a movie
router.delete('/:id', function (req, res) {
res.send(`Movie with {req.params.id} deleted`);
})
//route to update a movie
router.put('/:id', function (req, res) {
res.send(`Movie with {req.params.id} updated`);
})
module.exports = router;
In simple terms, app.all matches to all HTTP method requests (GET , POST, PUT, PATCH, DELETE ) at a given path and attaches the middleware to every HTTP method at a given path, while app.use is used to load specified middleware when API requests are made to the path mentioned with it.
App.all also accepts regex as its path while app.use doesn’t, but it matches all the routes that extend the base route.
Although both of these functions can be used to achieve the same results, app.use(“/api”) will match any request with /api as well as base extended /api/anything. But, the app.all(“/api/*”) will only match /api/anything path but not only /api.
App.use is generally used for configuring middleware at the highest level of an application.
Now you can build your express application, remember the route!