Let’s Decode Backend Development

Deeksha Sharma
15 min readNov 2, 2022

--

🔎What is Node.js?

Node.js is a javascript runtime environment. We can run javascript on the browser because the browser has an in-built V8 engine that is capable of executing Javascript.

But if you want to execute javascript outside your browser then node.js comes to your rescue.Node.js is a wrapper around the V8 engine.V8 engine and node both are written in C++, so they both can run on our machines.

In your vs code, you can run a javascript file by just writing node app.js.

What is a server?

The server is a device that is capable of providing responses to your requests. Your own computer can be a server. To create a server node.js is extremely important because Javascript has to be executed outside the browser for making a server.

How does the Internet work?

When you write www.medium.com in your browser then all this happens behind the scene:

www.medium.com is the domain name assigned to some IP Address.

So when you request for the medium website through your browser, it first goes to DNS resolver, which says that this domain name is associated with this IP address.

IP address basically tells you that this website is hosted on this server. After taking this IP address, your request goes to this server, i.e a computer with this IP address.

The server returns the response to the request(HTML, js, video, image files, etc) and you get this medium website opened on your browser.

HTTP Protocol is used in this request-response cycle.

HTTP Protocol is:

→ client-server-based protocol

→stateless

→application layer protocol

→uses TCP

→Adds server Status Codes

→Add request and response Headers.

Note: Suppose we made a frontend application using React.Localhost:3000 will be the URL address at which our web app (during development) is accessible. See Localhost means that our own computer is hosting this application meaning whenever we will be requesting this URL our computer will be the server.

Localhost:3000 → request made through the browser.

DNS will tell that localhost:3000 is pointing to the IP address of your own computer hence your own computer will be sending the response to this request.

🔎What are Port Numbers?

Port numbers are like doors to different applications on our devices. Your own computer will be having many applications like chrome, discord skype, etc. Now when we are making some requests through chrome then we wish to see the response on chrome itself not on discord. Right?

So, a port number uniquely identifies a network-based application on a computer. This number is assigned automatically by the OS, manually by the user, or is set as a default for some popular applications.

🔎What are Status Codes?

HTTP response status codes indicate whether a specific HTTP request has been successfully completed. Responses are grouped into five classes:

  1. Informational responses (100199)
  2. Successful responses (200299)
  3. Redirection messages (300399)
  4. Client error responses (400499)
  5. Server error responses (500599)

What is an API?

API is the acronym for Application Programming Interface, which is a software intermediary that allows two applications to talk to each other.

REST API

APIs that follow the REST architectural style are called REST APIs.Rules which an API should follow in order to become REST API are:

same response for all platforms.

Always JSON format.

Stateless.

Route Names should be nouns

HTTP Methods should be used for doing operations on routes.

💡How can I make my own device a server?

We can make a server using:

  • Node.js
  • Express

Let’s learn about making a server using Node.js first:

Node.js has built-in modules like:

http module => Allows Node.js to transfer data over the Hyper Text Transfer Protocol (HTTP).

fs(File system) module => Allows you to work with the file system on your computer.

So this is how we create a server using Node.js.Let’s learn Express now:

Creating a Server using Express

Express is the most popular minimalistic framework. It is built upon the built-in module HTTP of NodeJS to facilitate simple and easy interaction. Express as a whole is known as a framework, not just as a module.

Now you must be thinking that when we can make a server using Node.js then why do we need express? Here is the answer:

What is NPM?

NPM is a package manager for Node. js packages, or modules if you like. www.npmjs.com hosts thousands of free packages to download and use. The NPM program is installed on your computer when you install Node.js.

Note: Whenever we send data from the frontend to the backend data will always be there in the request body.

✒Mounting in Express

Chainable Route Handlers

This will help in reducing redundancy and typos.

app.route('/book')
.get((req, res) => {
res.send('Get a random book')
})
.post((req, res) => {
res.send('Add a book')
})
.put((req, res) => {
res.send('Update the book')
})

express.Router Class

Use the express.Router class to create modular, mountable route handlers.

Request Parameters

Variable to pass additional info to the server

Params: /user/:id => (req.params)

Query Params: /user? id=1&name= “user1" => (req.query)

📝Middlewares

Middleware functions are functions that have access to the request object (req), the response object (res), and the next middleware function in the application’s request-response cycle. The next middleware function is commonly denoted by a variable named next.

The functions which run as soon as the request enters our server till the response leaves the server are all middleware functions.

MongoDB

MongoDB is a No SQL database. It is an open-source, cross-platform, document-oriented database written in C++.

We can use MongoDB in two ways:

MongoDB Community Server=>Deploy MongoDB cluster on your own device. You don’t need internet connectivity for making queries in this.

MongoDB Atlas=>Deploy and scale a MongoDB cluster in the cloud

📍Let’s learn commands in cmd for MongoDB Community first:

mongosh=>starts the mongo db shell in cmd.

db=>gives the current database.

show dbs=>shows all the databases.

show collections=> show all the collections in the current database.

🎯Inserting Documents

📌Inserting single document in current database: db.collectionName.insertOne({……your entries…….})

📌Inserting many documents in current database: db.collectionName.insertMany([{……entry1…….}, {……entry2…….}])

🎯Updating Documents

This will help in updating the desired fields in a document

📌Updating a single document

db.collectionName.updateOne({id:14}, {$set:{rating:8, pages:20}})

Rating and pages are the fields that we want to update in the document identified by id 14.

📌Updating multiple documents

db.collectionName.updateMany({author:”abc”}, {$set:{author:”def”}})

🎯Deleting Documents

This will help in deleting the documents in the current collection.

📌Deleting a single document

db.collectionName.deleteOne({id:14})

📌Deleting multiple documents

db.collectionName.deleteMany({author:”abc”})

🎯Find Function

Returns all the documents from the current collection: db.collectionName.find()

This returns the first 20 documents. Just type “it” in cmd and hit enter to get the next 20 documents.

🎯Using Filters with Find Function

db.collectionName.find({propertyName: value})

📌We can specify which properties of documents we want to see in the output.

db.collectionName.find({propertyName: value}, {property1:value1, property2: value2})

📌To fetch only the first occurring document:

db.collectionName.findOne({propertyName: value})

Example: db.books.find({author:”abc”, rating:9}, {title:1, year:1})

🎯Counting the documents in Current Collection

count() gives the count of documents. You can use it by method chaining.

db.collectionName.find().count()

db.collectionName.find({property:value}).count()

🎯Sorting and Limiting Data

This will fetch only the number of documents you mention inside the limit function.

db.collectionName.find().limit(num)

This will sort the documents on the basis of the property you mention:

1 denotes ascending order

-1 denotes descending order

db.collectionName.find().sort({proprerty:1/-1})

Example: Combining sort and limit function

db.books.find().sort({title:1}).limit(3)

🎯Using Operators in Queries

📌Greater Than/Less Than or Greater Than EqualTo/ Less ThanEqualTo

db.collectionName.find({property:{$gt:integer}})

gt=>Greater Than

gte=>Greater Than or Equal To

lt=>Less Than

lte=>Less Than or Equal to

📌OR Operator

db.collectionName.find($or:[{p1:v1}, {p2:v2}])

In the OR operator we pass an array of filters.

📌IN or NOTIN Operator

db.collectionName.find({propertyName:{$in:[v1, v2, v3]}})

📌Increment Operator

db.collectionName.updateOne({id:12, {$inc:{pages:2}}})

This says that in the document identified by id 12, increment the pages Count by 2.

📌Push Operator

It helps in adding elements to an array

db.collectionName.updateOne({id: 12}, {$push:{counts:{$each:[1,2,3]}}})

This says that in the document identified by id 12 there is an array named counts, In that counts array push all these numbers i.e 1,2, and 3.

📌Pull Operator

It helps in deleting elements from an array

db.collectionName.updateOne({id:12}, {$pull:{counts:1}})

This will delete 1 from the counts array.

🎯Querying the Arrays

Suppose we have a genres array in a document.

📌First thing we want is to fetch all the documents who are having “fantasy” string in their genres array:

db.collectionName.find({genres:”fantasy”})

📌Second thing we want to fetch all the documents who are having only the specified string in their genres array.No other string should be there apart from our specified string.

db.collectionName.find(genres:[“fantasy”])

db.collectionName.find(genres:[“magic”, “fantasy”])

📌Third thing we want is that all the mentioned strings should be compulsorily there in the genres array of these fetched docs. But the catch here is that genres can have as many strings as it wants apart from our specified strings.

db.collectionName.find(genres:{$all:[“fantasy”, “magic”]})

📌Fourth thing is that Suppose we have an array of objects like:

reviews = [{id:1, name:”abc”}, {id:2, name:”def”},{id:3, name:”abc”}]

Now we want to fetch all the documents who are having name property as abc:

db.collectionName.find({“reviews.name”: “abc”})

Note: When we are accessing the object property notice we have used double quotes on reviews.name.So Remember that!!

📜Interacting with MongoDB using our Application Code

For this, you have to install MongoDB drivers depending on the language you are using to build your application.

Connecting our application to MongoDB

Now making requests from our application:

What is Mongoose?

Mongoose is an Object Data Modeling (ODM) library built on top of the MongoDB native driver for Node.js projects. It makes connecting MongoDB and Node.js Applications easier for developers.

npm i mongoose is the command to install.

Advantages of Mongoose Module

  1. Collection validation of the MongoDB database can be done easily.
  2. A predefined Structure can be implemented in the collection.
  3. Constraints can be applied to documents of collections using Mongoose.
  4. The mongoose module is built on the top of MongoDB driver and provides an easy abstraction of the query and defines a query.

🖍Using MongoDB Atlas with Mongoose

Steps for using MongoDB Atlas:

  • Go to https://www.mongodb.com/cloud/atlas/register and register here.
  • Create a cluster there.
  • Now in security settings:
  • Create a user for database access
  • Now in Network Access, you have to provide an IP Address through which you can access this cluster. So here we will write the IP address of the computer on which our application will be hosted.

Give it a read to learn more about Mongoose: https://mongoosejs.com/docs/

💡Connecting the application to the MongoDB Atlas using mongoose

Hooks in Mongoose

✨Pre Hook

If you want to perform some operations before saving the data in the database.

✨Post Hook

If you want to perform some operations after adding the data to the database.

📌//We want this to run before saving the data in database
//similarly you can use this on remove instead of save.
userSchema.pre('save', function(){
//We do not want to save confirm password field as it is a redundant data.Since we have applied check so obviously it will be having same value as password
//in mongoose if you do anything equals to undefined then that thing will not be saved in database.
this.confirmPassword=undefined;
//this is holding the object that has to be added in the database.
console.log("Before saving in Database", this)
})
📌//We want this to run after saving the data in database
//similarly you can use this on remove instead of save.
userSchema.post('save', (doc)=>{
//doc is the thing which is saved in databse
console.log("After saving in Database", doc)
})

Note: Id field is added by mongoose to the document and the version field is added by MongoDB

Always all the pre-hooks will run first and after that only post-hooks will run.

🔐Hashing

Hashing is used for security purposes. Hashing is the process of transforming any given key or a string of characters into another value.

Before storing the password in the database we hash it so that hackers can’t access it.

What we do is that we use salt and a hashing function. We put our salt and password into this hashing function and it gives the hashed password as output which we store finally in our database. We can also call this process encryption. Since we are using salt here so it’s impossible for hackers to decrypt it because they are not having access to our salt.

This package can be used for doing hashing in our projects:https://www.npmjs.com/package/bcrypt

userSchema.pre('save', async function(){//genertaing the saltlet salt = await bcrypt.genSalt();//hashing the passwordlet hashedString= await bcrypt.hash(this.password, salt);this.password = hashedString;})

🍪Cookies

Cookies are small blocks of data created by a web server while a user is browsing a website and placed on the user’s computer or other devices by the user’s web browser. Cookies are placed on the device used to access a website, and more than one cookie may be placed on a user’s device during a session.

When a user first login on to a website then the server creates the cookie and stores some info in it and sends this cookie along with the response. This cookie gets stored in the user's browser.

HTTP is a stateless protocol so if cookies had not been there then we will have to log in again and again while scrolling through feeds in social media apps.

function setCookies(req, res){
//set-cookie is telling that we are setting the cookie and isLoggedIn is the key-value pair for cookie
res.setHeader('Set-Cookie', 'isLoggedIn=true');
res.json('cookies has been set');
}

🖊This will show you all the cookies in the browser console:document.cookie

We prefer to use a third party package for cookies:

npm i cookie-parser

const cookieParser=require('cookie-parser')//We will be using a cookie parser through a middleware function so that all reuqest and response objects can access cookies
app.use(cookieParser());
function setCookies(req, res){
//set-cookie is telling that we are setting the cookie and //isLoggedIn is the key-value pair for cookie
//without using 3rd party package
res.setHeader('Set-Cookie', 'isLoggedIn=true');
//using cookie parser package
📍res.cookie('isLoggedIn', true, {maxAge:24*60*60*1000, secure:true, httpOnly:true});
res.cookie('isPrimeMember', true);
res.json('cookies has been set');
}

Note down these things here:

maxAge==>denotes the time period for which we want cookies to be stored in the browser. The default time period is one session(i.e until I close that tab). Accepts input in milliseconds.

secure==>A cookie with the Secure attribute is only sent to the server with an encrypted request over the HTTPS protocol. It’s never sent with unsecured HTTP (except on localhost), which means man-in-the-middle attackers can’t access it easily. Insecure sites (with HTTP: in the URL) can’t set cookies with the Secure attribute.

httpOnly==>A cookie with the httpOnly attribute is inaccessible to the JavaScript Document. cookie API; it's only sent to the server. This flag should always be there as it increases security.

function getCookies(req, res){
//getting the cookies from request object
// let cookies = req.cookies;
//or
let cookies = req.cookies.isLoggedIn;
console.log(cookies);
res.send('cookies received');
}

🔒Protected Route

Now we will see how can we use cookies along with the login function to make a protected route:

//user logged in or not
//users will be displayed to you only when you are logged in
function protectRoute(req, res, next){
console.log(req);
//checking if a user is logggedin through cookies
if(req.cookies.isLoggedIn){
next();
}
else{
return res.json({message: 'operation not allowed'});
}
}
module.exports = protectRoute;
async function loginUser(req, res){
try{
let data = req.body;
if(data.email){
let user = await userModel.findOne({email: data.email});
if(user){
if(data.password == user.password){
//server is making a cookie when user loggedin for first time
res.cookie('isLoggedIn', true, {httpOnly:true});
return res.json({message:"User has Logged In", userDetails:data})
}
else{
return res.json({message:"Wrong Credentials!"})
}
}
else{
return res.json({message:"You need to sign up!"})
}
}
else{
res.json({message:"Please enter emailId"})
}
}
catch(err){
return res.status(500).json({mesage:err.message})
}
}
/***************************/userRouter
.route('/')
.get(protectRoute, getUsers) //protectRoute is middleware function

🔦JWT

JWT, or JSON Web Token, is an open standard used to share security information between two parties — a client and a server. Each JWT contains encoded JSON objects, including a set of claims. JWTs are signed using a cryptographic algorithm to ensure that the claims cannot be altered after the token is issued.

Working of JWT:

JWT contains three things:-

->Header==>algorithm(it does encryption)

->Payload==>a unique Id (we will be providing this)

->Signature==>secretKey+algo(in header)+payload

The backend has this secretKey and this secretKey is not accessible to anyone other than the backend.

📍When the front end makes the request to the backend for the first time, then the server creates a JWT and sends this JWT along with a response to the front end.

📍When this frontend again makes the request to the backend then the frontend also carries JWT to the backend.

📍Now since HTTP is a stateless protocol so backend has no idea that in the previous request it had sent JWT to this frontend or not.

📍Now when the request comes for a second-time backend will take the payload and header from the JWT and will make a signature using the secret key it has.

📍Now backend will verify that if newyCreatedSignature matches the signature stored in JWT(frontend has sent with this request), then this is a valid user, otherwise, it’s not a valid user.

JWT is encrypted so it increases security.

If anyone does any changes to the payload and header then the signature will not match and JWT will identify that it’s not an authenticated user!

This package is used for implemnting JWT in applications:

npm i jsonwebtoken

Session Storage v/s JWT

You can see in the case of session storage we store things in server memory while in the case of JWT we do not store anything on the server. JWTs provide a means of maintaining the session state on the client instead of doing it on the server.

One case to understand the importance of JWT will be:

Suppose a bank has two servers, then if you want that if you do a login request on any one server, you should get logged in on another as well automatically. This thing can only be achieved with JWT as we will provide the same secret key to both servers.

secret.js 
*********
//Just type in some alphanumeric string
module.exports = {
JWT_KEY: 'asghdgjh12fwdf5fe'
};
auth.js
*******
const jwt = require('jsonwebtoken');
const {
JWT_KEY
} = require('./../../secret.js');
async function loginUser(req, res) {
try {
let data = req.body;
if (data.email) {
let user = await userModel.findOne({
email: data.email
});
if (user) {
if (data.password == user.password) {
//id that is auto generated by mongo db for each document-->we have taken this as our payload
let uid = user['_id'];
// Sign with default (HMAC SHA256) otherwise we have to mention algo as { algorithm: 'RS256'}
let token = jwt.sign({
payload: uid
}, JWT_KEY);
res.cookie('login', token, {
httpOnly: true
});
return res.json({
message: "User has Logged In",
userDetails: data
})
} else {
return res.json({
message: "Wrong Credentials!"
})
}
} else {
return res.json({
message: "You need to sign up!"
})
}
} else {
res.json({
message: "Please enter emailId"
})
}
} catch (err) {
return res.status(500).json({
mesage: err.message
})
}
}protectRoute.js
****************
const jwt = require('jsonwebtoken');
const {JWT_KEY} = require('./../../secret.js');
//user logged in or not
function protectRoute(req, res, next) {
if (req.cookies.login) {
let isVerified = jwt.verify(req.cookies.login, JWT_KEY);
if (isVerified)
next();
else {
return res.json({
message: "User is not verified"
})
}
} else {
return res.json({
message: 'operation not allowed'
});
}
}
module.exports = protectRoute;

This JWT token is separated by three dots “a.b.c” where:

a=>represents header,

b=>represents payload,

c=>signatures

You can decode this JWT using https://jwt.io/

🎲Software Architecture

The architecture of a system describes its major components, their relationships (structures), and how they interact with each other.

🧱MVC

The Model-View-Controller (MVC) architecture is one of the most popular software development patterns.

The logic behind MVC architecture utilizes the design principle of separation of concerns. This principle aims to separate an application into district sections, where each section addresses a specific and separate issue.

MVC consists of three parts:

📀Model==> This stores the data logic and dictates how you store and retrieve your data.

📀View==>The View of MVC architecture is the user interface (UI) of your application.

📀Controller==>You can think of the Controller as a bridge between the Model and View components. This is the Brain of this architecture as it has all the logic.

When a user supplies data through your user interface (the View), the View passes that data to the Controller. The Controller uses that data to update the database (through the Model). The Controller also pulls data from the database (through the Model) and returns it to the View.

You can understand MVC better with this example.

Imagine you went to a restaurant:

You (You told the waiter that this kind of food you want)==> View

Waiter(Takes your order and conveys that to cook. Also brings your prepared order) ==> Controller

Cook(Takes the ingredients from the fridge and prepare your order) ==> Model

Fridge(stores the ingredients)==>Data

So this sums up this blog. Happy Coding!!

--

--