Express JS
Express JS
Why Express?
Organizations from different domains,
are using Node.js as a preferred
runtime environment, for their web
applications.
Some web- development tasks
tasks needs a lot of coding in Node.js.
Implementing Routing for different
paths and implementation of route
handlers for different HTTP verbs
POST, Get.
Serving HTML, CSS static files.
Sending dynamic response using
Express.js is the most popular
framework for creating web
applications in Node.js.
It is lightweight and provides easy
connectivity with different
databases.
Using Express, we can handle
requests and manage routes.
Express is a perfect framework for
performing high-speed
input and output operations.
Advantages of using Express:
Has Robust API's which will help us
to easily configure routes for sending
and receiving the data between browser
and database.
Takes less time and fewer lines of
code thereby simplifying the application
development and making it simple to
write safe and modular applications.
Have been used in
different domains like business, finance,
news, social, etc.
What is Express?
Express.js is widely used for building
server-side applications by using node js.
Features:
Easy routing: Express makes it easy to
define routes for your application and
handle HTTP requests and
responses.
Middleware support: Express provides a
flexible middleware architecture that
allows you to easily add functionality
to your application, such as
authentication, logging, and error
handling.
Template engines: popular options
like Handlebars and Pug, allowing
you to easily generate dynamic HTML
pages, by parsing the template file
and replaces the placeholders with
actual data to generate a final
output.
Scalability: Express is designed to be
lightweight and flexible, making it a
great choice for building scalable
applications that can handle a large
number of users and requests.
Large ecosystem: Express has a
large and active community, with a
wide range of plugins and modules
available that can be used to add
additional functionality to your
application.
Overall, Express.js provides
developers with a simple, efficient,
and flexible way to build web
applications with Node.js, making it
a popular choice for building both
small and large-scale web
applications.
Express in web application stack
Express places itself on the server-
side in the complete application stack
and provides a complete server-side
solution for application development.
Express works well with any client-
side technology like Angular, React,
etc. and any database like MongoDB,
MySQL can be used.
Express Development Environment
To develop an Express
application on a local machine, a
development environment with the
installation of the below packages
are to be set up.
Node.js
Express
Express-generator
Step 1:
Install Node.js from the software
house/software center.
Follow the instruction to check the Node
version installed in the machine.
Open Node command prompt and give the
following command.
node -v
Step 2:
Installation of Express is straightforward if you
have Node.js installed on your machine.
Express can be installed using the node
package manager (npm).
Now install Express using the following
command:
Step 3:
Express Generator is a command-line tool
that helps you quickly create a basic
skeleton of an Express application.
For installing the express-generator tool
globally, use the following command.
npm install express-generator -g
Once the generator is installed, it
is extremely simple to create the
application using the 'express' command.
Once the installation is complete, you
can create a new Express application
using the following command:
express
<<application_name>>
It will automatically generate a folder
with the supplied application name.
Inside the folder, there will be a set of
folders and files which are created
by the generator tool.
Development Environment- steps
1. Install Express.
2. Install Express-generator
3.
To create a new Express application
named 'myApp'.
This
will create a new directory called
myApp with a basic Express application
4. Observe the new project
structure generated.
Install dependencies: Navigate to the
newly created directory myapp and
install the required dependencies by
running:
cd myapp
npm install
This will install all the necessary
dependencies specified in the
package.json file.
Folder description:
bin: Contains the configuration file for
the environment.
public: Contains the static files which
we can use in the application.
routes: Contains all the routes created
in the application.
views: Contains the view templates,
default jade template files, responsible
for presenting data to the user in a way
that is easy to understand and interact
with..
app.js: Contains application-level
configurations and the starting point of
the application.
package.json: The package.json file is
usually present in the root directory of a
Node.js project.
This file helps npm to identify the project
and handle its dependencies.
It consists of other metadata, vital to
end-users, such as the description of the
project, its version, license information,
other configuration data, etc.
Code linting
We will be writing the logic in the
application based on the requirements
provided, but as a developer, we must
take care of the standards of the code.
We will explore ESLint and learn how to
ensure the coding standards are followed
in the code.
Install eslint module global to the machine.
npm install eslint -g
After installing the node modules
enable ESLint from the extensions
of VSCode.
Basic Configuration:
eslint --init
The following 3 options will be
available for the user to choose
from.
myController.aboutMethod);
Routing using Application Instance
InExpress.js, you can handle routes using
the application instance by calling the
relevant HTTP method on the app object
and specifying the route and callback
function to be executed for that route.
Example:
const express = require('express');
const app = express();
// Handle GET request for the root route
app.get('/', (req, res) => {
res.send('Hello World!');
});
// Handle POST request for the
/login route
app.post('/login', (req, res) => {
// handle login logic here
});
// Handle PUT request for the
/users/:id route
app.put('/users/:id', (req, res) => {
const userId = req.params.id;
// handle update user logic here
});
//Handle DELETE request for
the /users/:id route
app.delete('/users/:id', (req, res)
=> {
const userId = req.params.id;
// handle delete user logic here
});
app.listen(3000, () => {
console.log('Server listening on
port 3000');
});
In this example, we define routes for
handling HTTP GET, POST, PUT, and
DELETE requests.
For each route, we call the relevant
HTTP method on the app object and
specify the route path and a callback
function that will be executed when
the route is accessed.
The callback function takes two
arguments: the request object (req)
and the response object (res), which
can be used to handle the request
and send a response back to the
client.
Note that the order in which you define
your routes is important in Express.js.
Routes are processed in the order in
which they are defined, so if you have
multiple routes that match the same
path, the first one that matches will be
used.
It's generally recommended to define
more specific routes before less specific
ones to avoid unexpected behavior.
Method 2: Router class of Express
Let's use the express.Router class.
Router class helps in grouping the route
handlers together for a site and allows
them to access them using a common
route-prefix.
The below code explains how to define
routes in an Express application.
route.js file:
const express = require('express');
const router = express.Router();
router.get('/', myController.myMethod);
router.get('/about',
myController.aboutMethod);
Now to use the route module
created above, we need to import it
and then associate it with the
application object. In app.js file
include the below code:
const express = require('express');
const router =
require('./routes/route');
const app = express();
app.use('/', router);
This application will now be able to
route the paths defined in the route
Defining a route
A route can be defined as shown below:
router.method(path,handler)
router: express instance or router
instance
path: is the route where request runs
handler: is the callback function that gets
triggered whenever a request comes to a
particular path for a matching request
type.
Route Method:
The application object has different
methods corresponding to each of the
HTTP verbs (GET, POST, PUT, DELETE).
These methods are used to receive HTTP
Below are the commonly used route methods
and their description:
Examples:
routing.get("/notes", notesController.getNotes);
routing.post("/notes",
notesController.newNotes);
routing.all("*", notesController.invalid);
Here, notesController is the custom js
file created to pass the navigation to
this controller file.
Inside the controller, we can create
methods like getNotes, newNotes,
updateNotes, etc. to perform various
database related queries like reading
from the database, inserting into the
database, etc.
Route Paths
Route path is the part of the URL which
defines the endpoint of the request.
For the URL "http://localhost:3000/" - ‘/’ is
the endpoint.
For the
URL "http://localhost:3000/about" - ‘/abou
t’ is the endpoint.
We can handle the endpoints as follows, in an
Express application.
router.get('/',myController.myDefaultMethod);
router.get('/about',myController.aboutMethod);
In the above example, we have strings like '/'
and '/about' as part of the route path. The
path can also be string patterns or regular
expressions.
String-based routes: Pass a
string pattern as the first
parameter of the routing method.
router.get('/ab*cd',
myController.myDefaultMethod);
The above route definition
responds to all the paths starting
with "ab" and ending with "cd".
For eg: "/abxyzcd", "/abbcd". The
request will be handled by the
route handler, myDefaultMethod
written in myController.
Regular expression routes:
Pass a regular expression object as
the first parameter to the routing
method.
router.get('/
x/',myController.myMethod);
The above route definition
responds to any path with an 'x' in
the route name.
In a general web application, string-
based routes are used mostly and
regular expression-based routes are
used only if required absolutely.
Handling Routes
Route handler can be defined as functions that
get executed every time the server receives a
request for a particular URL path and HTTP
method.
The route handler function needs at least two
parameters: request object and response
object.
Request object:
The HTTP request object is created when a client
makes a request to the server. The variable
named req is used to represent this object.
router.get('/
user/:username',myController.getMethod);
Here inside the getMethod of Controller, username
is the route parameter. We access it using the
below syntax.
req.params.< parameter_name >
exports.getMethod = async (req, res) => {
const name = req.params.username;
res.send(`Welcome ${name}`);
};
If more than one parameter is
passed as part of the request URL,
then the required information can
be extracted as shown below.
router.get('/
user/:username/:id',myController.ge
tMethod)
exports.getMethod = async (req,
res) => { const username =
req.params.username;
res.send(`Welcome ${username}
with id ${req.params.id}`);
};
Query Parameters
Query strings are the data appended
as part of the request URL. Query
strings start with a question mark
and the name-value pair in it is
separated by an &(ampersand).
For example, in the below URL,
'http://localhost:3000/login?
username=john&email=john
%40i.com&login=Login',
the querystring is
"username=john&email=john
%40i.com&login=Login"
Consider the below example HTML
form which submits the data to the
login path using HTTP get method.
<!DOCTYPE html>
<html lang="en">
<head>
<title>Login Page</title>
</head>
<body>
<form name="loginform" action=
“http://localhost:3000/login”
method="get">
Enter your name:
<input type="text"
name="username" value="">
<br>
Enter the email:
<input type="email" name="email"
value=""> <br>
<input type="submit"
name="submit1" value="Register">
</form>
</body>
In Routing.js, we can handle this
request as shown below:
const express = require('express');
const routing = express.Router();
const notesController= require
('../Controller/myNotes');
routing.get('/login',
notesController.loginMethod);
Since the form submits data using
HTTP get method, the data, i.e., the
username and email will be
appended as part of the query
string.
While handling the route, we can
extract these in the Controller, as
shown below:
exports.loginMethod = async (req,
res) => {
const name =
req.query.username;
res.send(`You have registered in
The server grabs the query string
name-value pair.
request.query.<querystring name-
value pair>
Why Middleware?
Consider the scenario where the following
tasks are to be designed in the application.
Authenticate or authorize requests
Log the requests
Parse the request body
End a request-response cycle
The above jobs are not the core concerns of
an application. But they are the cross-
cutting concerns that are applicable to the
entire application.
In the Express framework, these cross-
cutting concerns can be implemented
using middleware.
What is a Middleware?
A function that will have all the access
for requesting an object, responding to
an object, and moving to the next
middleware function in the application
request-response cycle.
A function defined as a middleware
can execute any task mentioned
below:
Any code execution.
Modification of objects - request and
response.
Call the next middleware function.
How Middleware works?
If we want to log the request method and
request URL before the handler executes.
Below is the route definition for which we want
to add the middleware.
app.get('/login', myController.myMethod);
exports.myMethod = async (req, res, next)
=> { res.send('/login');
};
The middleware for the above requirement
could be written as below.
const mylogger = async (req, res, next) => {
console.log(new Date(), req.method, req.url);
next();
};
The arguments of this function are:
req: an object containing
all the information about the
request.
res: an object containing
all the information about the
response sent from server to client.
next: tells Express when the
middleware is done with the
execution.
Inside the function, we have logic to
log the request method and request
URL along with the date.
The next() method ensures that after
the execution of middleware logic, the
handler is executed.
Now we can modify the route definition
to add the middleware
using app.use() method.
app.use(mylogger);
app.get('/login',
myController.myMethod);
Now, whenever any request is coming to
the path '/login', mylogger middleware
function will get executed, and then the
corresponding handler function will be
invoked.
Any middleware can be loaded in an Express
application using the app.use() method.
The app.use() method accepts one string
parameter path, which is optional, and one
function parameter callback which is the
mandatory middleware function.
app.use(PATH, CALLBACK)
Whenever any request comes to a
particular "path", the middleware callback
function will get triggered.
To associate a middleware directly with the
handler function use app.use() method.
app.use('/login', (req, res, next) =>
{ console.log(new Date(), req.method,
req.url); next();});
Chaining of Middleware
We can create a chain of middlewares before the
request reaches the handler.
Consider a middleware which logs the request time
and another middleware which logs the request URL
as shown.
const logtime = async (req, res, next)
=>{ console.log('Request received at ' +
Date.now()); next();
};
const logURL = async (req, res, next)
=>{ console.log('Request URL is ' + req.url); next();
};
app.use(logtime);
app.use(logURL);
Both middlewares are associated with
the application object
using app.use() method.
Now whenever an HTTP request
arrives at the application, it goes
through these two middlewares.
Both middlewares in the chain can
modify the request and response
object based on the requirement.
Middleware Ordering
The order of execution of middleware depends on the
order in which the route handler functions and other
middleware functions are declared in the application.
Example: The middleware is added between two
route definitions.
app.get('/', myController.getMethod);
app.use(myLogger);
app.post('/', myController.postMethod)
In the above code, the middleware will never run for
the route handler for the GET request, as it is
declared after the route definition. The middleware
will get executed only for the POST handler as it is
declared before the route definition for the POST
handler.
Types of Middlewares
The express framework provides
the following five different types of
middlewares.
Application Level Middleware
Application-level middlewares are
functions that are associated with the
application object. These middleware
function will get executed each time an
application receives a request.
The middleware that we have discussed
earlier was also an application-level
middleware.
app.use("/", route);
Application Level Middleware- Demo
Highlights:
Usage of middleware
Application-level middleware
Demo steps:
1. Modify the app.js file in the application by
adding middleware to it.
const express = require('express');
const router = require('./Routes/routing');
const app = express();
// Customer logger middleware
const mylogger = function (req, res, next) {
console.log(`Req method is ${req.method}`);
console.log(`Req url is ${req.url}`);
next();
};
// using app object make use of logger
middleware function
app.use(mylogger);
app.use('/', router);
app.listen(3000);
console.log('Server listening in port 3000');
2. The routing.js file content.
const express = require('express');
const router = express.Router();
const myController =
require('../Controller/myController');
router.get('/', myController.myMethod);
router.get('/about',
myController.aboutMethod);
module.exports = router;
3. In Controller, add the below-mentioned
code.
exports.myMethod = async (req, res,
next) => {
res.send('<h1>Welcome</h1>');
};
exports.aboutMethod = async (req, res,
next) => {
res.send('<h1>About Us Page</h1>');};
4. Run the application using 'node app'
and observe the output.
On changing the URL to
'http://localhost:3000/about', we get the
below message:
The middleware output can also be seen
in the console as shown below:
Router Level Middleware
Router-level middlewares are
functions that are associated with
a route. These functions are linked
to express.Router() class instance.
Let us see how to use the router-
level middleware in our
application.
Highlights:
Usage of middleware
Router level middleware
Demo steps
1. Modify routing.js file by adding a middleware
in it.
const express = require('express');
const router = express.Router();
const myController =
require('../Controller/myController');
router.use((req, res, next) =>
{ console.log(`Req method is $
{req.method}`); console.log(`Req url is $
{req.url}`); next();
});
router.get('/', myController.myMethod)
router.get('/about',
myController.myaboutMethod)
module.exports = router;
2. Add the below code to the Controller:
exports.myMethod = async (req, res) =>
{ res.send('Welcome');
};
exports.myaboutMethod = async (req, res)
=> {
res.send('About us');
};
3. The app.js file in TestApp.
const express = require('express');
const router = require('./Routes/routing');
const app = express();
app.use('/', router);
app.listen(3000);
console.log('Server listening in port 3000');
4. Open a command prompt and
start the server. Observe the output
Demo 2: Router Level Middleware
Ifwe want to use middleware for only a
specific route, we can use the below
code:
Highlights:
Usage of middleware for specific routes
Router level middleware
1. Modify routing.js file by adding a
middleware in it.
const express = require('express');
const router = express.Router();
const myController =
require('../Controller/myController');
Demo steps:
router.use('/about', (req, res, next) => {
console.log(`Req method is $
{req.method}`);
console.log(req.originalUrl); next();
});
router.get('/', myController.myMethod)
router.get('/about',
myController.myaboutMethod);
module.exports = router;
2. In app.js add the below-
mentioned code:
const express = require('express');
const router =
require('./Routes/routing');
const app = express();
app.use('/', router);
app.listen(3000);
3. In Controller, we have the below
code.
exports.myMethod = async (req, res)
=> { res.send('Welcome to Router
Demo');
};
exports.myaboutMethod = async (req,
res) => {
res.send('About us');
};
4. Run the application and observe the
below output.
Inthe console we can observe the
middleware is not logged:
});
if(err.status){ res.status(err.status); }
else{ res.status(500) }
res.json({
status:'error', message:
err.message })
}}
module.exports = errorLogger;
4. Save and run the app. Observe
the JSON data as output:
If we try to insert the below employee
record into the collection(DB table), it
will be inserted.
{empName : "John", employeeId :
324123, Location : "France"}
But if we carefully observe, the below record did
not exactly match the format as we
discussed. After inserting the above record, the
collection will be looking as shown below:
as shown below:
Here, we can observe that the field
which did not match the name in the
collection will not be inserted. If we
had set a required constraint for the
empId field, then the document will
not be inserted at all.
Let us now try to insert the
below employee record into the
collection. Notice here that the below
record did not match the value as
expected, for the field empId.
{ empName : "John", empId :
"324123", location : "France"}
Without using Mongoose, if you try
to insert the above record, the
record will be inserted successfully
and the collection will be looking
as shown below:
If we try to insert the above
record with the help of Mongoose,
then the collection will be looking
as shown below:
We can observe that the value for the
type-cast.
useCreateIndex:true,
useFindAndModify: false,
useUnifiedTopology: true} is to suppress
the below warnings thrown by MongoDB.
To create a collection using mongoose, we
must start by creating a Mongoose schema.
Introduction to Mongoose Schema
A schema defines document properties
through an object, where the key name
corresponds to the property name in the
collection. The schema allows you to define
the fields stored in each document along
with their validation requirements and
default values.
To create a schema, we need to use the
following lines of code:
const mongoose = require('mongoose');
const schema = new
mongoose.Schema({ property_1: Number,
property_2: String
});
In a schema, we will define the data types of the
properties in the document. The most commonly
used types in the schema are:
String: To store string values. For e.g: employee
name
Number: To store numerical values. For e.g:
employee Id
Date: To store dates. The date will be stored in
the ISO date format. For e.g: 2018-11-
15T15:22:00.
Boolean: It will take the values true or false
and is generally used for performing validations.
ObjectId: Usually, ObjectId is assigned by
MongoDB itself. Every document inserted in
MongoDB will have a unique id which is created
automatically by MongoDB and this is of type
ObjectId.
Array: To store an array of data, or even a sub-
document array. For e.g: ["Cricket","Football"]
Creating Schema – Demo
myNotes wants to store notes id, name,
and data for multiple users.
The code to create the required schema
is given below:
const myNotesSchema = new
mongoose.Schema( {
notesID: { type: Number, },
name: { type: String, },
data: { type: String, },
},
{ timestamps: { createdAt: true,
updatedAt: true, },
});
Line 1: Using the Schema class
provided by the Mongoose library, we
can create the schema with all the
necessary fields.
Note: It is a standard practice to have
timestamps - (created at & updated
at) field for each document inserted
into the collection. This can be done
by adding a timestamp option to the
Schema.
To ensure data entered in the
collection is as per the requirement,
we can configure validations to the
schema.
Validation types and Defaults
Validation through mongoose validator
Mongoose provides a way to validate data
before you save that data to a database.
Data validation is important to make sure
that "invalid" data does not get persisted in
your application. This ensures data integrity.
A benefit of using Mongoose, when inserting
data into MongoDB is its built-in support for
data types, and the automatic validation of
data when it is persisted.
Mongoose’s validators are easy to configure.
When defining the schema, specific
validations need to be configured.
The following rules should be kept in mind while
adding validation:
Validations are defined in the Schema
Validation occurs when a document attempts to
be saved
Validation will not be applied for default values
Validators will not be triggered on undefined
values. The only exception to this is
the required validator
Customized validators can be configured
Mongoose has several built-in validators
The required validator can be added to
all SchemaTypes
Number schema
type has min and max validators
Strings have enum and match validators
Let us see how to add a validator to a Schema.
Validation types and default
values
Required
Ifyou want any field to be
mandatory, use the required
property.
const schema =
mongoose.Schema({ name:
{ required: true, },
});
Types
You can declare a schema type
using the type directly, or an
object with a type property.
Example: To declare schema type
using an object with a
type property.
const schema =
mongoose.Schema({ property_1:
{ type: String, },
property_2: { type: Number, },
});
Default
Your schema can define default
values for certain properties. If you
create a new document without that
property set, the default value
provided to that property will be
assigned.
The below code snippet shows how to
add a default to schema type:
const schema = mongoose.Schema({
property_1: { default: "Client" },
property_2: { default: 1 },
});
Custom validations
In Mongoose we can specify custom
validators that are tailored specifically to
fields if the built-in validators are not
enough. They are provided as values of
validate property.
The below code snippet shows how to add
a custom validator to the schema type.
const schema =
mongoose.Schema({ property_1: {
validate: (value) => {
/*Validation code*/
}
},
});
Demo: Adding Validation, types and
default to myNotes Schema
const myNotesSchema = new mongoose.Schema( {