Backend + Rest API
What is NodeJS?
Ans: NodeJS is a cross-platform JavaScript runtime environment, it’s built on Chrome’s V8 engine to run JavaScript code outside the browser, for easily building fast and scalable applications.
Authentication VS Authorization

In the authentication process, the identity of users is checked for providing the access to the system.
While in authorization process, the person’s or user’s authorities are checked for accessing the resources.
In the authentication process, users or persons are verified.
While in this process, users or persons are validated.
It is done before the authorization process.
While this process is done after the authentication process.
It needs usually the user’s login details.
While it needs the user’s privilege or security levels.
Authentication determines whether the person is user or not.
While it determines What permission does the user have?
Generally, transmit information through an ID Token.
Generally, transmit information through an Access Token.
Popular Authentication Techniques-
Password-Based Authentication
2FA/MFA (Two-Factor Authentication / Multi-Factor Authentication)
Popular Authorization Techniques-
Role-Based Access Controls (RBAC)
OAuth 2.0 Authorization
The authentication credentials can be changed in part as and when required by the user.
The authorization permissions cannot be changed by user as these are granted by the owner of the system and only, he/she has the access to change it.

What is CORS?
Cross-Origin Requests
When a web page makes a request to a different domain (origin) than the one that served the web page, it's called a cross-origin request. This can happen with APIs, images, scripts, iframes, and more. Each origin is defined by its scheme (http/https), host (domain), and port.
Examples:
Same Origin:
Page URL:
https://example.com/page
Request URL:
https://example.com/api/data
Cross-Origin:
Page URL:
https://example.com/page
Request URL:
https://api.example.com/data
Different subdomain (
api.example.com
vsexample.com
)
CORS (Cross-Origin Resource Sharing)
CORS is a security feature implemented by browsers to control how resources on a web page can request resources from another domain.
Key Concepts:
Same-Origin Policy: By default, web pages can only make requests to the same origin they were loaded from. CORS relaxes this restriction.
Preflight Request: For certain requests (like those with methods other than GET/POST or with custom headers), the browser sends an OPTIONS request first to check if the actual request is safe to send.
CORS Headers:
Access-Control-Allow-Origin
: Specifies which origins are allowed to access the resource.Access-Control-Allow-Methods
: Lists the HTTP methods that can be used when accessing the resource.Access-Control-Allow-Headers
: Lists the headers that can be used when making the actual request.
Example:
GET /api/data HTTP/1.1
Host: api.example.com
Origin: https://example.com
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://example.com
What is SameSite Attribute?
The SameSite
attribute is used in cookies to control their behavior in cross-site requests, providing protection against CSRF (Cross-Site Request Forgery) attacks.
SameSite
Attribute Values:
SameSite=Strict
: Cookies are only sent in requests originating from the same site. Cross-site requests do not include the cookie.SameSite=Lax
: Cookies are sent with same-site requests and top-level navigation (e.g., following a link from another site). It provides a balance between security and usability.SameSite=None
: Cookies are sent in all contexts, including cross-site requests, but must also have theSecure
attribute, meaning they are only sent over HTTPS.
Example:
Set-Cookie: sessionId=abc123; SameSite=Strict
Summary CORS & SameSite
Cross-Origin Requests: When a web page requests resources from a different domain.
CORS: A set of headers that tell the browser which cross-origin requests are permitted.
SameSite: A cookie attribute that restricts how cookies are sent with cross-site requests to protect against CSRF attacks.
Why Use app.use(cors());
app.use(cors());
Enable Cross-Origin Requests: By default, web browsers enforce the Same-Origin Policy, which restricts web pages from making requests to a different origin (domain, protocol, and port) than the one that served the web page. This policy enhances security but can be restrictive for modern web applications that often need to interact with external APIs or resources hosted on different domains.
Allow External Web Applications to Access Backend APIs: When you have a frontend application (e.g., a Reactor Angular app) hosted on a different origin than your backend server, the browser will block these cross-origin requests unless the backend server explicitly allows them. Using
app.use(cors());
enables the backend server to permit requests from different origins, making it accessible to clients hosted on different domains.Facilitate API Consumption: If you are building a public API or a service consumed by multiple clients across different origins, you need to configure CORS. This configuration ensures that your API can be accessed by these clients, promoting interoperability and wider usage of your services.
Configure Security Policies: With the CORS middleware, you can specify which origins, methods, and headers are allowed, providing a fine-grained control over your API security. For example, you can restrict access to only trusted domains, allow specific HTTP methods (GET, POST, etc.), and control which headers clients can use in their requests.
Without CORS they cannot communicate data?
Correct, without enabling CORS (Cross-Origin Resource Sharing), the frontend and backend hosted on different origins (even if they are on the same machine but different ports) cannot communicate directly due to the Same-Origin Policy enforced by web browsers. This policy is a critical security feature that prevents malicious websites from making unauthorized requests to another site.
Example Scenario
Frontend:
http://localhost:5173
Backend:
http://localhost:3000
When your frontend application tries to make a request to your backend API, the browser will block this request because the origins are different (localhost:5173
vs localhost:3000
).
Same-Origin Policy
The Same-Origin Policy restricts how resources on one origin can interact with resources on another origin. An origin is defined by the scheme (protocol), host (domain), and port. Two URLs have the same origin if they have the same scheme, host, and port.
How CORS Solves This Issue
CORS allows servers to specify who can access their resources and how those resources can be accessed. By including CORS headers in the response, the server can permit cross-origin requests from trusted origins.
Enabling CORS in Express
To enable communication between your frontend and backend in this setup, you need to configure your Express backend to allow requests from http://localhost:5173
.
Here’s how you can do it using the cors
middleware:
Install the
cors
package:npm install cors
Configure CORS in your Express application:
const express = require('express'); const cors = require('cors'); const app = express(); // Configure CORS to allow requests from the frontend origin const corsOptions = { origin: 'http://localhost:5173', // Allow only this origin methods: 'GET,POST,PUT,DELETE', // Allow specific HTTP methods allowedHeaders: 'Content-Type,Authorization' // Allow specific headers }; app.use(cors(corsOptions)); app.get('/api/data', (req, res) => { res.json({ message: 'This is CORS-enabled for http://localhost:5173' }); }); app.listen(3000, () => { console.log('Server running on port 3000'); });
Example API Request
When your frontend code makes a request like this:
fetch('http://localhost:3000/api/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
The browser will check the CORS headers in the response from the backend. If the backend includes the appropriate CORS headers, the browser allows the request. Otherwise, it blocks the request.
What is JWT (JSON Web Token)?
A JWT, or JSON Web Token, is like a digital ID card. It's a compact, secure way of transmitting information between two parties (such as a client and a server). This token is made up of three parts:
Header: Contains information about the type of token and the algorithm used to create the signature (like a "type" and "method" on an ID card).
Payload: Contains the actual data or claims. These claims are statements about an entity (typically the user) and additional data. It might include information like user ID, roles, or other metadata.
Signature: This is created by taking the encoded header and payload, adding a secret key, and hashing them together. It ensures that the token hasn’t been tampered with.

How is JWT used?
Here's a simple way to understand how JWT is used in a typical web application:
User Login:
The user logs in with their username and password.
The server verifies the credentials. If they are correct, the server creates a JWT.
Token Creation:
The server encodes the user information into the payload.
The server creates a signature using the header, payload, and a secret key.
The final JWT looks like this:
header.payload.signature
.
Token Storage:
The server sends the JWT back to the client (usually in the HTTP response).
The client stores the JWT, often in a cookie or local storage.
Subsequent Requests:
For each subsequent request to the server, the client includes the JWT (often in the HTTP header or as a cookie).
The server receives the request and verifies the JWT's signature.
If the JWT is valid, the server processes the request.
Benefits of JWT
Stateless Authentication:
The server doesn’t need to keep track of active sessions. It just needs to verify the token each time a request is made.
Compact:
JWTs are small and can be easily sent through URLs, POST parameters, or inside HTTP headers.
Self-Contained:
JWTs carry all the necessary information about the user, reducing the need for repeated database lookups.
Disadvantages of JWT
Token Size: JWTs can become large if they carry extensive user data, leading to increased network traffic. You should strike a balance between token size and necessary information.
Limited Token Expiry Control: Once issued, JWTs remain valid until they expire. Revoking a JWT before expiration requires additional complexity, such as token blacklisting.
Security Risks: If the secret key used to sign JWTs is compromised, attackers can create forged tokens. It’s crucial to safeguard this key.
Example in a Web Application
1. User logs in:
// Example login endpoint in Express.js
app.post('/login', (req, res) => {
const user = { id: 1, name: 'Alice' }; // Example user
const token = jwt.sign({ user }, 'secret_key', { expiresIn: '1h' });
res.cookie('token', token, { httpOnly: true });
res.send('Logged in successfully');
});
2. User accesses a protected route:
// Example protected route in Express.js
app.get('/protected', (req, res) => {
const token = req.cookies.token; // Get token from cookie
if (!token) return res.status(401).send('Access denied');
try {
const decoded = jwt.verify(token, 'secret_key');
res.send('Protected content');
} catch (err) {
res.status(400).send('Invalid token');
}
});
Security Considerations
HttpOnly and Secure flags: When setting the cookie, use the
HttpOnly
flag to prevent JavaScript from accessing the token and theSecure
flag to ensure the cookie is only sent over HTTPS.CSRF Protection: Use appropriate measures to prevent Cross-Site Request Forgery (CSRF) attacks, such as synchronizer tokens or double-submit cookies.
Token Expiry: Always set an expiration date for JWTs to reduce the risk of token theft.
Summary
JWT is like a digital ID card: It securely transmits user information between the client and server.
Three parts of JWT: Header (type and algorithm), Payload (user information), Signature (ensures integrity).
Used in authentication: After login, the server gives a JWT to the client. The client uses it for subsequent requests to access protected resources.
Benefits: Stateless, compact, self-contained, and easy to use in web applications.
How to Access the Token in the Frontend
When a JWT is created on the server side after user authentication, it can be passed to the frontend and stored in the browser for later use. Here's a step-by-step explanation of how to handle this process.
1. Token Creation on the Server
After a successful login, the server creates a JWT and sends it back to the client. This can be done in different ways, such as including the token in the HTTP response body or setting it as a cookie.
Example: Sending Token in Response Body
const jwt = require('jsonwebtoken');
app.post('/login', (req, res) => {
const user = { id: 1, name: 'Alice' }; // Example user
const token = jwt.sign({ user }, 'secret_key', { expiresIn: '1h' });
res.json({ token });
});
2. Storing the Token in Local Storage
Once the token is received on the frontend, it can be stored in the browser's local storage for later use.
Example: Saving Token in Local Storage
// Assuming you have a function to handle login form submission
async function handleLogin(event) {
event.preventDefault();
const loginData = {
username: document.getElementById('username').value,
password: document.getElementById('password').value
};
const response = await fetch('http://your-server.com/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(loginData)
});
const data = await response.json();
if (data.token) {
localStorage.setItem('token', data.token); // Store token in local storage
console.log('Token stored:', data.token);
} else {
console.error('Login failed');
}
}
// Example HTML form
/*
<form id="loginForm">
<input type="text" id="username" name="username" placeholder="Username">
<input type="password" id="password" name="password" placeholder="Password">
<button type="submit">Login</button>
</form>
*/
// Add event listener to the form
document.getElementById('loginForm').addEventListener('submit', handleLogin);
3. Using the Token in Subsequent Requests
For subsequent requests to protected routes, the token can be retrieved from local storage and included in the HTTP headers.
Example: Accessing Protected Route
async function accessProtectedResource() {
const token = localStorage.getItem('token');
if (!token) {
console.error('No token found, please log in');
return;
}
const response = await fetch('http://your-server.com/protected', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}` // Include token in the Authorization header
}
});
if (response.ok) {
const data = await response.json();
console.log('Protected data:', data);
} else {
console.error('Failed to access protected resource:', response.status);
}
}
// Example button click event to access protected resource
document.getElementById('accessProtectedButton').addEventListener('click', accessProtectedResource);
// Example HTML button
/*
<button id="accessProtectedButton">Access Protected Resource</button>
*/
Summary
Token Creation: The server generates a JWT after successful login and sends it to the client, usually in the response body.
Token Storage: The client receives the token and stores it in local storage using
localStorage.setItem()
.Token Usage: For subsequent requests to protected routes, the token is retrieved from local storage and included in the HTTP headers using the
Authorization
header.
This approach ensures that the JWT is securely stored and easily accessible for authenticating future requests without requiring the user to log in again.
Real-Life use case in Project?
Frontend
const handleLogin = async (event) => {
event.preventDefault();
console.log(email, password);
try {
const response = await axios.post("http://localhost:3000/auth/login", {
email,
password,
});
if (response.status === 200) {
console.log(response.data);
const token = response.data.access_token; // Access the token from the response data
localStorage.setItem("access_token", token); // Store the token in local storage
setModalTitle("UserLogin Success !!");
setModalText("Congratulations, User Logged in Successfully !");
setModalVisible(true);
setOpen(true);
} else {
setModalTitle("Failed !!");
setModalText("UserLogin Failed");
setOpen(true);
}
} catch (error) {
setModalTitle("Failed !!");
setModalText("UserLogin Failed");
setOpen(true);
}
};
import { KJUR } from "jsrsasign";
const token = localStorage.getItem("access_token");
let userRole = null;
if (token) {
const decodedToken = KJUR.jws.JWS.parse(token);
userRole = decodedToken.payloadObj?.role;
console.log(userRole);
}
Backend
router.post("/login", async (req, res, next) => {
const { email, password } = req.body;
console.log(email, password);
try {
const validUser = await User.findOne({ email });
if (!validUser) return next(errorHandle(404, "User not found!"));
const validPassword = bcryptjs.compareSync(password, validUser.password);
if (!validPassword) return next(errorHandle(401, "Wrong credentials!"));
const token = jwt.sign(
{ id: validUser._id, email: validUser.email, role: validUser.role },
JWT_SECRET
);
const { password: pass, ...rest } = validUser._doc;
res
.cookie("access_token", token, { httpOnly: false })
.status(200)
.json({ access_token: token });
} catch (error) {
next(error);
}
});
What is Express?
Express is a minimal and flexible Node.js web application framework that provides a robust set of features to develop web and mobile applications. It simplifies the process of creating a server and handling HTTP requests and responses. Here are some key aspects of Express:
Middleware: Express uses a series of middleware functions to handle HTTP requests. Middleware functions can perform tasks such as logging, authentication, parsing request bodies, and more. They are executed sequentially, making it easy to build complex applications by composing simple functions.
Routing: Express provides a powerful routing mechanism that allows you to define how your application responds to client requests for specific URLs and HTTP methods (GET, POST, PUT, DELETE, etc.). Routes can be parameterized to capture dynamic values from the URL.
Basic Example
Here's a basic example of setting up an Express server and handling a simple GET request:
Install Express:
npm install express
Create a Server:
const express = require('express'); const app = express(); const port = 3000; // Define a route for GET requests to the root URL app.get('/', (req, res) => { res.send('Hello World!'); }); // Start the server app.listen(port, () => { console.log(`Server is running on http://localhost:${port}`); });
Advanced Features
Middleware Functions: Middleware functions are essential in Express for processing requests. Here’s an example of a logging middleware:
app.use((req, res, next) => { console.log(`${req.method} request for ${req.url}`); next(); });
Handling JSON and Form Data: Express can parse incoming JSON and URL-encoded data using built-in middleware:
app.use(express.json()); app.use(express.urlencoded({ extended: true }));
Routing with Parameters: You can capture parameters from the URL and use them in your request handling logic:
app.get('/user/:id', (req, res) => { res.send(`User ID: ${req.params.id}`); });
Error Handling: Custom error handling middleware can be defined to catch and respond to errors in your application:
app.use((err, req, res, next) => { console.error(err.stack); res.status(500).send('Something broke!'); });
Express is widely used in the Node.js ecosystem due to its simplicity, flexibility, and extensive middleware ecosystem, making it a go-to choice for building web applications and APIs.
How did you handle request and response in Express?
Handling request and response in Express.js involves understanding the basic structure and middleware functionality of the framework. Here’s a step-by-step guide on how to manage requests and responses in an Express application:
Setting Up Express: First, you need to set up an Express application. This involves installing Express and creating a basic server.
npm install express
const express = require('express'); const app = express(); const port = 3000; app.listen(port, () => { console.log(`Server is running on port ${port}`); });
Defining Routes: Routes are defined to handle different HTTP methods like GET, POST, PUT, DELETE, etc. Each route corresponds to a URL pattern and an associated callback function that executes when the route is matched.
// Define a route for GET requests to the root URL app.get('/', (req, res) => { res.send('Hello World!'); }); // Define a route for POST requests to /submit app.post('/submit', (req, res) => { res.send('Form submitted'); });
Accessing Request Data: You can access various parts of the request object to retrieve data sent by the client.
req.params
: Parameters in the URL pathreq.query
: Query string parametersreq.body
: Data sent in the request body (requires middleware likebody-parser
)
// URL parameters app.get('/user/:id', (req, res) => { const userId = req.params.id; res.send(`User ID: ${userId}`); }); // Query parameters app.get('/search', (req, res) => { const query = req.query.q; res.send(`Search query: ${query}`); }); // Request body (requires middleware) app.use(express.json()); // for parsing application/json app.use(express.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded app.post('/login', (req, res) => { const username = req.body.username; const password = req.body.password; res.send(`Username: ${username}, Password: ${password}`); });
Sending Responses: You can send various types of responses using methods on the
res
object.res.send()
: Sends a response of various types (HTML, JSON, plain text, etc.)res.json()
: Sends a JSON responseres.redirect()
: Redirects the client to a different URLres.status()
: Sets the HTTP status code
app.get('/json', (req, res) => { res.json({ message: 'Hello, JSON!' }); }); app.get('/redirect', (req, res) => { res.redirect('/another-route'); }); app.get('/not-found', (req, res) => { res.status(404).send('Page not found'); });
Middleware: 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. Middleware can be used to perform operations like logging, authentication, parsing request bodies, etc.// Example of middleware to log request details app.use((req, res, next) => { console.log(`${req.method} ${req.url}`); next(); }); // Example of middleware for authentication const authenticate = (req, res, next) => { if (req.headers.authorization) { next(); } else { res.status(401).send('Unauthorized'); } }; app.get('/secure', authenticate, (req, res) => { res.send('This is a secure route'); });
Error Handling: Error handling middleware is defined with four parameters:
err
,req
,res
,next
. This middleware is used to handle errors that occur during request processing.// Error handling middleware app.use((err, req, res, next) => { console.error(err.stack); res.status(500).send('Something broke!'); });
Putting it all together, an Express application handles requests and responses by defining routes, accessing request data, sending responses, using middleware for various tasks, and handling errors efficiently.
What is Middleware?
In the context of Express (and many other web frameworks), middleware refers to 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. These middleware functions can perform a variety of tasks, such as:
Logging requests
Parsing request bodies
Handling authentication
Managing sessions
Serving static files
Error handling
Middleware functions can be thought of as a series of steps that the request goes through before getting a response. Each middleware function can either end the request-response cycle or pass control to the next middleware function using next()
.
Basic Middleware Example
Here's a simple example to illustrate the concept of middleware in an Express application:
Setup: First, install Express and create a basic server:
npm install express
const express = require('express'); const app = express(); const port = 3000; // Custom middleware function that logs each request const requestLogger = (req, res, next) => { console.log(`${req.method} ${req.url}`); next(); // Pass control to the next middleware function }; // Use the requestLogger middleware for all routes app.use(requestLogger); // Define a simple route app.get('/', (req, res) => { res.send('Hello World!'); }); // Start the server app.listen(port, () => { console.log(`Server is running on http://localhost:${port}`); });
In this example, the requestLogger
middleware function logs the HTTP method and URL of each incoming request. The next()
function is called to pass control to the next middleware function in the stack. If next()
is not called, the request-response cycle will be terminated, and no further middleware or routes will be executed.
Built-in Middleware
Express comes with some built-in middleware functions, such as:
express.json()
: Parses incoming requests with JSON payloads.express.urlencoded()
: Parses incoming requests with URL-encoded payloads.express.static()
: Serves static files such as HTML, CSS, and JavaScript.
Here’s an example using built-in middleware:
const express = require('express');
const app = express();
const port = 3000;
// Built-in middleware to parse JSON bodies
app.use(express.json());
// Built-in middleware to serve static files from the 'public' directory
app.use(express.static('public'));
app.post('/data', (req, res) => {
res.json(req.body); // Echoes back the JSON body sent in the request
});
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});
Middleware for Specific Routes
Middleware can also be applied to specific routes or route groups:
const express = require('express');
const app = express();
const port = 3000;
// Custom middleware function for a specific route
const authMiddleware = (req, res, next) => {
if (req.headers.authorization === 'mysecrettoken') {
next(); // Authorized, proceed to the next middleware or route handler
} else {
res.status(401).send('Unauthorized');
}
};
// Apply authMiddleware to the /secure route
app.get('/secure', authMiddleware, (req, res) => {
res.send('This is a secure route');
});
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});
In this example, the authMiddleware
function checks for an authorization header and only allows access to the /secure
route if the correct token is provided. If not, it sends a 401 Unauthorized response.
Error-Handling Middleware
Error-handling middleware functions have four arguments: err
, req
, res
, and next
. These are used to catch and handle errors that occur in the application.
const express = require('express');
const app = express();
const port = 3000;
// Define a route that causes an error
app.get('/error', (req, res) => {
throw new Error('Something went wrong!');
});
// Error-handling middleware
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Internal Server Error');
});
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});
In this example, the error-handling middleware catches any errors that occur in the application and sends a 500 Internal Server Error response.
Middleware functions in Express provide a powerful way to handle various aspects of request processing, from logging and authentication to parsing request data and handling errors.
What are the ACID Properties?
SESSION variable কথায় কাজ করে ? database or memory? explain কর।
Last updated