Introductions
ExpressJs is a javascript framework that is used with NodeJs mainly for web and mobile applications. This goes with saying that express.js is used for building web or mobile backend services and served through routes.
It is widely used by NodeJs backend developers and has a plethora of methods available to be used, and seeing that it is fast and unopinionated, you can build a large variety of applications that scales easily.
Prerequisites
- A basic knowledge of javascript and NodeJs
- NodeJs is already installed on your system You can install by going to node.js site
Initialised node project to test the given examples and practice.
If you haven't initialised a project:
- create a folder by using the UI or from the cmd run
mkdir new-folder
in the directory you wish to save the folder. - change the directory to the new folder's directory with
cd new-folder
. Normally you should use a name that reflect the project you want to create, but we'll use new-folder in this article. - Inside the new folder, in the cmd run
npm init -y
. The -y flag help say yes to all the prompt making it a bit faster. A package.json is created on initialization.- Edit the package.json by adding
"start": "node server.js"
right below the test script, divided by a comma. This goes to tell the cmd/bash to run the codes in the file we're to create with node.js. So each time we want to run our program in server.js we just runnpm run start
in the cmd. - Now create a file named server.js in the new-folder manually or through bash
touch server.js
- Edit the package.json by adding
- create a folder by using the UI or from the cmd run
- ExpressJs is installed in your project
You can do that by running
npm install express --save
using the node package manager. Check express for more details.
Middlewares
So what are middlewares? Middlewares are basically functions but these function have access to the request object, the response object and the next middleware. The next middleware is an inbuilt middleware that helps pass on the execution of a request from one middleware to another, thus giving way for the possibility of more than one middleware attached to a particular stack, we could also call the next a callback function. The response object is the object express returns to the client and is usually modified before sent. The request object is the object received from the client side and contains different kinds of information to be accessed and processed.
We have different types of middlewares and their use cases:
Built-in middlewares
These middlewares already exist in express.js for our usage. An example is the express.json which parses incoming requests to json, helping us to access the body which is parsed in json in the client side. Another popular example is the express.static, this middleware helps serve static files like css, js, html, etc. How do we use middlewares? You use the by calling the use function from an initialised express object. An example is
app.use(express.json())
.Third-party middleware
I decided to talk about this next because it is the next most used. Some very good people build packages that we want to use in our application to make it easier to build than creating from scratch. We could add them as a middleware if we want express to use them each time a client sends a request as they attach their features to the request/response object. A very popular one is the helmet package for security.
Application-level middleware
Now if you have a specific, unique feature you have in mind that you want to execute on each client side request, you can write the function and use the app-level middleware. One important thing you must take note of when using this type of middleware is the importance of using next at the end, neglecting next means it gets stuck at that particular function without going forward to the callbacks. An example is this:
app.use(function time(req, res, next){ console.log(Date.now()) next() })
Another example is:
function time(req, res, next){ console.log(Date.now()) next() } app.get('/', time, (req,res,next)=>{ res.send('Welcome!') })
Under this application-level middleware is another branch we call the error handling middleware. It catches errors and exceptions and directs them to the callback function which uses four(4) arguments. An example is:
app.use(function (error, req, res, next) { console.error(error) res.status(500).send('Something is wrong, please try again!') })
Router-level middlewares
This middleware takes a peculiar turn such that it is bound to an instance of the router and not the whole application. We generate a router by
express.Router()
.
So let's say we assign this router to a name router, we can then add middlewares that affect only that router rather than the whole application. This paves way to diversity and exclusions easily. After each router has been created and built it must be bound to the application by using an application-level router.
For example let's say we build a router to handle routes to user authentication both login, register and password reset, and we added a middleware that checks the device from which the request was sent and if not recognised by a database send an email notification to the user that an unknown device has logged in or if registering a welcome email. We won't want to put it as a top level app middleware, and adding it route by route might become stressful later on with increase in more authentication routes created for specific purposes. We can just add it as a top level router middleware and apply it to all routes in the router instance at once.
So how do we join it to the main application? Let's look at an example:
var express = require('express')
var app = express()
var router = express.Router()
// a middleware function with no mount path. This code is executed for every request to the router
router.use(function (req, res, next) {
console.log(Date.now())
next()
})
// handler for the /user/:id path, which renders a special page
router.get('/id', function (req, res, next) {
let random = Math.floor(Math.random()*1000)
console.log(random)
res.send(random)
})
// mount the router on the app
app.use('/', router)
//listen on port 5000
app.listen(process.env.PORT || 5000, () => console.log('Server is running'));
module.exports = app
In this example on request to '/id', a log is made showing the timestamp, then a random number is generated and also sent back to the client.
We attach the router to the app as shown in the last line. We have to make the router take a route that gets added to the routes in the router instance, at the last line we used app.use('/', router)
meaning no special route is added. we could decide to do app.use('/generate', router)
, this would mean to access the id generator we have to use the route we have to use '/generator/id'.
Callback
Callbacks or callback function or handler functions generally mean a function that is run after an asynchronous function have been completed. NodeJs is a non blocking runtime meaning it makes use of async functions a lot. So when you use a route like app.get, you add a callback function to it, which could also be a middleware. Callbacks are not only limited to routes, you could use async functions as you wish and use callbacks to execute results.
An example is:
const fs = require("fs");
fs.readFile("./test.txt", "utf8", function(err, data) {
if(err) {
// handle the error
} else {
// process the file text given with data
}
});
NodeJs naturally provides the err and data parameter, though more parameters can be added. It is safe to say node.js was made for callbacks.
Callback hell
Sometimes due to needed use cases and inappropriate use of async and callbacks, we get a complex app with many and almost endless callbacks, this is what we call callback hell. So how do we solve this?
These methods could help in solving callback hell
- Define every function not as an anonymous callback but separate named function.
- Use of javascript promises
- Use of third party library Async.js
Next
You could learn more about express.js and middlewares in the documentation here and read more on callbacks.
Thank you.