Express JS- Part2
Express JS- Part2
Cont..
CRUD Operations
Create:
Insert Document – Demo
Mongoose library offers several functions to
perform various CRUD (Create-Read-
Update-Delete) operations.
Inserting a document into the
collection
To insert a single document to MongoDB,
use the create() method. It will take
the document instance as a parameter.
Insertion happens asynchronously and any
operations dependent on the inserted
document must happen by unwrapping the
promise response.
We can insert a new document into our
myNotes Collection using the below code:
exports.newNotes = async (req, res) => {
try {
const noteObj = {
notesID: 7558, name: 'Mathan', data:
'Mongo Atlas is very easy to configure and
use.',
};
const newNotes = await
NotesModel.create(noteObj);
console.log(newNotes);
}
catch (err) {
console.log(err.errmsg);
}
In line 1, a new async function newNotes
is created with request and response
objects as parameters.
In line 3, a new object is created with the
name noteObj with all the necessary keys
that need to be inserted.
In line 8, we are referring to
the NotesModel which we had created
previously, and then making use of the
create() method to insert the document
into the collection. This will insert the
documents into the collection based on
the schema and will return the
documents which got inserted to the
const newNotes.
Try catch block will ensure all the
exceptions are handled in the
code.
In line 9, after successful
insertion, you will get the
following output in the console:
Insert multiple documents – Demo
document2]);
Let us now insert more documents into our myNotes
collection using the below code:
exports.newNotes = async (req, res) => {
try {
const noteObj = [
{notesID: 7555, name: 'Mathan', data:
'This includes macOS package notarization and a change
in configuration... ', },
{notesID: 7556, name: 'Sam', data: 'AMD
processor support in android studio.', },
{notesID: 7557, name: 'Mathan', data: 'MERN',
},
];
const newNotes = await NotesModel.create(noteObj);
console.log(newNotes);
}
catch (err) {
console.log(err.errmsg);
}
In line 1, a new async function newNotes is
created with request and response objects
as parameters.
In line 3, a new array of objects is created
with the name noteObj with all the
necessary keys that need to be inserted.
In line 16, we are referring to the same
NotesModel which we had created earlier.
We can now make use of the
create() method to insert the documents
into the collection. It will insert the
documents into the collection based on the
schema and will return the documents
which got inserted to the const newNotes.
Try catch block will ensure all the
exceptions are handled in the code.
In line 17, after successful
insertion, you will get the following
output in the console:
Read document(s)
Documents can be retrieved through find,
findOne, and findById methods.
Let us now retrieve all the documents that we
have inserted into our myNotes Collection.
exports.getNotes = async (req, res) => { try {
const notes = await NotesModel.find({},
{ _id: 0, __v: 0 });
if(notes.length > 0) {
console.log(notes);
}
}
catch (err) {
console.log(err.errmsg);
}
};
In line 1, a new async function getNotes
is created with request and response
objects as parameters.
In line 3, we are referring to the same
NotesModel which we had created
earlier and made use of the
find() method to fetch the document(s)
from the collection. The retrieved
documents will be available in the const
notes.
In line 4, we are ensuring the data by
checking the length and displaying the
returned data in the console.
Try catch block will ensure all the
exceptions are handled in the code.
On successful retrieval of the
documents, you will get the following
output in the console:
Retrieving data based on the condition
Let us try to retrieve notes details based on
notesID from the myNotes Collection using
the below code:
exports.getNotes = async (req, res) =>
{ try {
const notes = await
NotesModel.find({ notesID: 7555 },
{ _id: 0, __v: 0 });
if (notes.length > 0)
{ console.log(notes); }
}
catch (err) {
console.log(err.errmsg); }
};
In line 1, a new async function getNotes is
created with request and response objects as
parameters.
In line 3, we are referring to the same
NotesModel which we had created earlier and
made use of the find() method to fetch the
document(s) from the collection by providing
the condition(s) based on which documents
should be retrieved. The retrieved documents
will be available in the const notes.
In line 4, we are ensuring the data by
checking the length and displaying the
returned data in the console.
Try catch block will ensure all the exceptions
are handled in the code.
On successful retrieving of the
document, you will get the
following output in the console:
Update document(s)
We will update the details of the myNotes collection
using the below code:
exports.updateNotes = async (req, res) => {
try {
const noteObj = {
name: 'Mathan',
data: 'Updated notes',
};
const notes = await NotesModel.findOneAndUpdate(
{ notesID: 7555 },
noteObj,
{
new: true, //to return new doc back
runValidators: true, //to run the validators which
specified in the model
}
if(notes != null) {
console.log(notes);
}
}
catch (err) {
console.log(err.errmsg);
}
};
In line 1, a new async function
updateNotes is created with request and
response objects as parameters.
In line 3, a new array of objects is created
with the name noteObj with all the
necessary keys that need to be updated.
In line 8, we are referring to the same
NotesModel which we had created earlier
and made use of
the findOneAndUpdate() method to
find and update one document in the
collection. It will update the document in
the collection based on the schema and
will return the document which got
updated to the const notes.
The parameters of
findOneAndUpdate() method are:
The condition based on which the
document should be updated.
The new values to be updated.
Additional options to return the
updated document and to validate
the values based on the schema
definition.
Try catch block will ensure all the
exceptions are handled in the
code.
In line 18. On successful updating
of the document, you will get the
following output in the console.
Delete document(s)
The below code shows how to delete
the contents of myNotes collection:
exports.deleteNotes = async (req,
res, err) => {
const delDet = await
NotesModel.deleteOne({
notesID: 7555 });
console.log(delDet);};
In line 1, a new async function
deleteNotes is created with the
request, response, and error objects
as parameters.
In line 2, we are referring to the
same NotesModel which we had
created earlier and made use
of deleteOne() method to delete
the document from the collection
based on the condition and will
return the details about the delete
operation performed, which is
initialized to the const delDet.
In line 3, on the successful
deletion of the document, you will
get the following output in the
console.
API Development
Introduction
We are going to learn how to
create a set of APIs with all CRUD
operations.
This project covers the following:
Create a web server
Routing
Middleware
Database connectivity to MongoDB
using mongoose
Atypical folder structure with all
the necessary folders will look like
the below image:
The following describes the folder
structure of the project:
Controller – Business logic and
Database operations of the
application.
Model – MongoDB schema and model.
Routes - All the routes that
are created for the application.
Utilities – Helper files for the
application. E.g. Custom validators,
loggers, etc.
app.js – The starting point of the
application.
app.js file
Application execution will start from the app.js file.
All the necessary imports and middlewares need to
be configured in this file.
In the app.js, include the following code:
const express = require('express');
const bodyparser = require('body-parser');
const myReqLogger =
require('./Utilities/requestLogger');
const route = require('./Routes/routing');
const app = express();
app.use(bodyparser.json());
app.use(myReqLogger);
app.use('/', route);
const port = process.env.PORT || 3000;
app.listen(port, () => { console.log(`App running on
port ${port}...`);
});
API Routes
In the Routes folder, create a file with the name
"routing.js".
This file contains route definitions for all the APIs of
the application.
const express = require('express');
const routing = express.Router();
const notesController =
require('../Controller/myNotes');
routing.get('/notes', notesController.getNotes);
routing.post('/notes', notesController.newNotes);
routing.put('/notes/:id',
notesController.updateNotes);
routing.delete('/notes/:id',
notesController.deleteNotes);
routing.all('*',
notesController.invalid); module.exports = routing;
Model
In the Model folder, create a file with the name
"myNotesSchema.js".
Logic to connect to MongoDB using mongoose, schema, and the
model creation logic will be included in this file.
const mongoose = require('mongoose');
mongoose .connect('mongodb://localhost:27017/IntellectNotes',
{ useNewUrlParser: true, useCreateIndex: true,
useFindAndModify: false, useUnifiedTopology: true, })
.then(() => console.log('DB connection successful!'));
//Schema
const myNotesSchema = new mongoose.Schema(
{ notesID: { type: Number, unique: true, required: [true,
'Required field'], },
name: { type: String, required: [true, 'Required field'], },
data: { type: String, },
},
{ timestamps: { createdAt: true, updatedAt:
true, }, }); //Model
const NotesModel = mongoose.model('mynotes', myNotesSchema);
module.exports = NotesModel;
Controllers
In the Controller folder, create a file with the name
"myNotes.js".
This file contains business logic for all the APIs of the application.
Custom validators will be imported and accessed based on the
requirement.
All the controllers will send the response back to the client based
on the request received. Error handling also is taken care of
accordingly.
const NotesModel = require('../Model/myNotesSchema');
const validators = require('../Utilities/validator');
exports.getNotes = async (req, res) => {
try { const notes = await NotesModel.find({}, { _id: 0, __v:
0 });
if (notes.length > 0) {
res.status(200).json({ status: 'success', results:
notes.length, data: { notes, },
}); }
else { res.status(400).json({ status: 'success', data:
{ message: 'No notes available in the repo', },
}); } }
catch (err) {
res.status(404).json({ status: 'fail', message:
err, }); }};
exports.newNotes = async (req, res) => {
try {
if (validators.ValidateName(req.body.name)) {
const newNotes = await NotesModel.create(req.body);
res.status(201).json({ status: 'success',
data: { newNotes, },
}); }
else { res.status(400).json({ status: 'error',
results: 'Enter valid name', }); } } catch (err) {
res.status(404).json({ status: 'fail', message:
err.errmsg, }); }}; exports.updateNotes = async
(req, res) => { try { const notes = await
NotesModel.findOneAndUpdate( { notesID:
req.params.id }, req.body, { new: true, //to
return new doc back runValidators: true, //to run
the validators which specified in the model } );
if (notes != null)
{ res.status(200).json({ status: 'success',
data: { notes, }, }); } else {
res.status(400).json({ status: 'success',
data: { message: `No notes available with
ID ${req.params.id} `, }, }); } } catch
(err) { res.status(404).json({ status: 'fail',
message: err, }); }}; exports.deleteNotes =
async (req, res) => { const delDet = await
NotesModel.deleteOne({ notesID:
req.params.id }); if (delDet.deletedCount === 0)
{ res.status(404).json({ status: 'fail',
message: 'No notes available for this ID', }); }
else { res.status(200).json({ status:
'success', message: `Notes with $
{req.params.id} ID
deleted`, }); }}; exports.invalid = async (req,
res) => { res.status(404).json({ status: 'fail',
message: 'Invalid path', });};
Utilities
In the application, we will need specific helper files.
For instance, we need to have a logger that should
keep track of all the requests that comes into the
application or some custom validators which we can
make use in the controller.
Inside the Utilities folder create the following files
with the content.
requestLogger.js
const fs = require('fs');const { promisify } =
require('util'); const appendFile =
promisify(fs.appendFile); async function
requestLogger(req, res, next) { try { const
logMessage = `${new Date()} - ${req.method} - $
{req.url} \n`; await
appendFile('RequestLogger.log', logMessage);
next(); } catch (err)
{ next(err); }} module.exports = requestLogger;
validator.js
exports.ValidateName = function
(name) { if (name.trim().length >
0) { return true; } return
false;};
Project flow
The following figure visually
explains the application data flow.
Project Execution
API –1
URL – http://localhost:3000/notes
Method - GET
Description – API to fetch all the notes.
API –2
URL – http://localhost:3000/notes
Method - POST
Description – API to add new notes.
API –3
URL – http://localhost:3000/notes/7558
Method - PUT
Description – API to update existing notes.
API –4
URL – http://localhost:3000/notes/7558
Method - DELETE
Description – API to delete existing notes.
API –5
URL – http://localhost:3000/mynotes
Method - ALL
Description – default API to handle invalid
routes.
Why Session management?
Every user interaction with an application is an
individual request and response. The need to
persist information between requests is
important for maintaining the ultimate
experience for the user for any web application.
Session management is useful in the scenarios
mentioned below:
We need to maintain that a user has already
been authenticated with the application.
To retain various personalized user information
that is associated with a session.
Let us now understand how we can set up
sessions securely in our application to lessen
risks like session hijacking.
Session Management is a technique used
by the webserver to store a particular
user's session information. The Express
framework provides a consistent interface
to work with the session-related data. The
framework provides two ways of
implementing sessions:
By using cookies
By using the session store
Introduction to cookies
Cookies are a piece of information
sent from a website server and
stored in the user's web browser
when the user browses that
website. Every time the user loads
that website back, the browser
sends that stored data back to the
website server, to recognize the
user.
Configuration
The Express framework provides a cookie
API using cookieParser middleware.
The middleware cookieParser parses the
cookies which are attached to the request
object.
To install the cookieParser middleware, issue the
below command in the Node command prompt.
npm install cookie-parser
The above middleware can be configured in
Express application as shown below:
const express = require('express');
const cookieParser = require('cookie-parser');
const app = express();
app.use(cookieParser());
The cookie-parser middleware is imported and
then associated with the application object using
Setting and reading cookies
Setting cookies:
routing.get('/user/:name',
notesController.user1);
exports.user1 = async (req, res)
=> {
res.cookie('name',
req.params.name);
res.send('<p>Cookie set:<a
href="/user"> View here </a>');
};
When the user navigates to URL "
/user/<username>", the cookie is set with
the name username and the value is
retrieved using the params property of
the request object. Thus for setting
cookies, the general syntax is as shown
below:
res.cookie('cookieName', value, { expires:
new Date(), maxAge: 99999 });
The arguments of the above method are
the name of the cookie
cookie value
the options to be used while configuring
cookie, an optional object.
Some of the options that can be
specified while configuring a
cookie are shown below:
Reading cookies:
To access a particular cookie,
use the cookies property of
the request object.
exports.user2 = async (req, res) =>
{
res.send(req.cookies.name);
};
Whenever the user visits the "/user"
route, the value of the cookie is
fetched
using request.cookies property and
displayed to the user.
Updating and deleting cookies
Updating cookies:
A cookie can be updated by re-creating it with
new properties. For example, if we need to
update a cookie named username:
res.cookie('username', new_value)
Deleting cookies:
It is possible to remove a cookie with
the response object's clearCookie() method.
This method accepts the name of the cookie
which we want to delete.
app.get('/user', myController.myMethod);
exports.myMethod = async (req, res) => {
res.clearCookie('username');
res.send(req.cookies.username);
};
Types of cookies
Session cookies
The cookies which exist for a
browser session are defined as Session
cookies and they are rejected whenever
the browser is closed.
While configuring a cookie, if the 'expires'
option is not specified with any value,
then a session cookie is created by
default.
res.cookie('name', 'John')