Full Stack Que
Full Stack Que
All the technologies in the MEAN Stack use JavaScript, making development
efficient and consistent across the entire application—from client to server to
database.
1. MongoDB:
o A NoSQL database that stores data in a flexible, JSON-like format
(BSON).
o Schema-less and suitable for handling large volumes of
unstructured data.
o It allows dynamic querying and easy scalability.
2. Express.js:
o A lightweight web application framework built on top of Node.js.
o It simplifies the process of creating APIs and managing server-side
routing and middleware.
o Known for its speed, flexibility, and minimalism.
3. Angular:
o A powerful frontend framework developed by Google.
o It supports two-way data binding, component-based architecture,
and client-side routing.
o Angular applications are modular, maintainable, and scalable.
4. Node.js:
o A JavaScript runtime environment that executes JavaScript code
outside the browser.
o Built on Chrome’s V8 engine and uses an event-driven, non-
blocking I/O model.
o Ideal for building scalable and high-performance backend
applications.
3. Explain Benefits of Using MEAN Stack
1. Angular (Frontend)
4. Workflow Overview
Chapter 2
In JavaScript, both let and const are used to declare variables with block
scope, meaning they are only accessible within the block where they are
defined. However, the key difference is that let allows reassignment of values,
whereas const does not. Use let when the value of a variable needs to
change during execution, such as in a loop or when tracking a dynamic value.
Use const when the value should remain constant after assignment, which
improves code readability and helps prevent accidental reassignments.
javascript
CopyEdit
let counter = 0;
counter = counter + 1; // This is valid
const PI = 3.14159;
// PI = 3.14; // This will throw an error: Assignment
to constant variable.
2. Rewrite the following function using arrow function syntax:
javascript
CopyEdit
function multiply(a, b) {
return a * b;
}
The arrow function syntax provides a concise way to define functions and
automatically binds the context (this). Here's the arrow function version:
javascript
CopyEdit
const multiply = (a, b) => a * b;
javascript
CopyEdit
const name = "Alice";
const age = 25;
const message = `My name is ${name} and I am ${age}
years old.`;
console.log(message); // Output: My name is Alice and
I am 25 years old.
This makes code cleaner and easier to read compared to traditional string
concatenation.
Array Destructuring:
javascript
CopyEdit
const [a, b] = [10, 20];
console.log(a); // 10
console.log(b); // 20
Object Destructuring:
javascript
CopyEdit
const user = { name: "Bob", age: 30 };
const { name, age } = user;
console.log(name); // Bob
console.log(age); // 30
javascript
CopyEdit
const arr1 = [1, 2, 3];
const arr2 = [4, 5];
const merged = [...arr1, ...arr2];
console.log(merged); // [1, 2, 3, 4, 5]
Spread syntax is useful for writing clean, concise code when combining or
copying data structures.
javascript
CopyEdit
export function add(x, y) {
return x + y;
}
javascript
CopyEdit
import { add } from './math.js';
console.log(add(5, 3)); // 8
7. What are symbols in JavaScript? How can they be used to create unique
keys in objects?
Symbols are a new primitive type introduced in ES6. Each symbol is unique
and immutable. Symbols can be used as object property keys to avoid name
collisions, especially in scenarios involving extension or metaprogramming.
javascript
CopyEdit
const id = Symbol('id');
const user = {
name: 'Tom',
[id]: 101
};
console.log(user[id]); // 101
Even if another symbol is created with the same description, it will not equal the
original, ensuring uniqueness.
8. Explain the purpose of iterators and generators in ES6. Provide an
example of a generator function.
Iterators are objects that define a sequence and potentially a return value upon
completion. They follow the iterator protocol, which includes a next()
method. Generators are special functions defined using function* syntax and
can pause/resume execution using yield.
javascript
CopyEdit
function* countToThree() {
yield 1;
yield 2;
yield 3;
}
const counter = countToThree();
console.log(counter.next().value); // 1
console.log(counter.next().value); // 2
console.log(counter.next().value); // 3
9. What are Map and Set in JavaScript? Provide examples of their usage.
Map is a collection of key-value pairs where keys can be any data type.
Set is a collection of unique values, with no duplicates.
Map Example:
javascript
CopyEdit
const map = new Map();
map.set('name', 'Jane');
map.set('age', 25);
console.log(map.get('name')); // Jane
Set Example:
javascript
CopyEdit
const set = new Set([1, 2, 2, 3]);
console.log(set); // Set {1, 2, 3}
Maps are useful for flexible key-value storage; Sets are ideal for lists of unique
items.
A pure function is one that, given the same input, always returns the same
output and has no side effects (it does not modify external state). This makes
pure functions predictable, testable, and easier to reason about—core principles
in functional programming.
javascript
CopyEdit
function add(a, b) {
return a + b; // No side effects
}
javascript
CopyEdit
function double(x) {
return x * 2;
}
const numbers = [1, 2, 3];
const doubled = numbers.map(double); // [2, 4, 6]
In this example, map() is a higher-order function, and double is passed to it.
The power of higher-order functions lies in their ability to operate on behavior,
not just data.
javascript
CopyEdit
function multiply(a) {
return function(b) {
return a * b;
};
}
const double = multiply(2);
console.log(double(5)); // 10
Here, multiply(2) returns a new function that multiplies any given number
by 2. Currying is helpful when building pipelines or reusable computation
structures, and it's a common pattern in modern JavaScript frameworks and
libraries.
Immutability refers to the principle that data should not be changed once
created. Instead of modifying the original object or array, you create a new one
with the updated data. This is a key aspect of functional programming because it
eliminates side effects, simplifies debugging, and improves predictability,
especially in concurrent or asynchronous systems. To create an immutable
array, for instance, you can use the spread operator:
javascript
CopyEdit
const arr = [1, 2, 3];
const newArr = [...arr, 4]; // arr remains unchanged
console.log(newArr); // [1, 2, 3, 4]
For objects:
javascript
CopyEdit
const person = { name: "Alice" };
const updatedPerson = { ...person, age: 25 };
console.log(updatedPerson); // { name: "Alice", age:
25 }
javascript
CopyEdit
const fetchData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Data received!");
}, 1000);
});
};
fetchData().then(data => {
console.log(data); // Output after 1 second: Data
received!
});
In this example, the fetchData function returns a promise that simulates data
fetching. When the operation completes, the resolve function is called, and
the .then() method handles the result. Promises make asynchronous code
easier to structure and read.
javascript
CopyEdit
const fetchData = () => {
return new Promise((resolve) => {
setTimeout(() => resolve("Data fetched!"), 1000);
});
};
In this example, await pauses the execution inside getData() until the
promise returned by fetchData() is resolved. This approach avoids callback
hell and provides a cleaner control flow for asynchronous tasks.
fetchData(function(result) {
console.log(result); // Output: Data loaded
});
While callbacks were the standard way of handling async operations in earlier
JavaScript, they can lead to deeply nested and hard-to-maintain code, known as
"callback hell." This led to the evolution of Promises and async/await as cleaner
alternatives.
Generators are functions that can pause and resume their execution using the
yield keyword. They are defined using the function* syntax. In
asynchronous programming, generators were used before async/await to
manage asynchronous flows with libraries like co. Generators allow you to
pause the function at yield and resume later, enabling controlled execution.
javascript
CopyEdit
function* asyncFlow() {
console.log("Step 1");
yield;
console.log("Step 2");
}
const gen = asyncFlow();
gen.next(); // Step 1
gen.next(); // Step 2
javascript
CopyEdit
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
console.log(`Hi, I'm ${this.name} and I'm
${this.age} years old.`);
}
}
const person1 = new Person("John", 28);
person1.greet(); // Hi, I'm John and I'm 28 years
old.
javascript
CopyEdit
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a sound.`);
}
}
class Dog extends Animal {
speak() {
console.log(`${this.name} barks.`);
}
}
const dog = new Dog("Rex");
dog.speak(); // Rex barks.
Here, Dog inherits from Animal and overrides the speak method,
demonstrating method overriding, a form of polymorphism.
javascript
CopyEdit
class Counter {
#count = 0;
increment() {
this.#count++;
console.log(this.#count);
}
}
const counter = new Counter();
counter.increment(); // 1
// counter.#count; // SyntaxError: Private field
'#count' must be declared in an enclosing class
javascript
CopyEdit
class Animal {
speak() {
console.log("Animal makes a sound.");
}
}
Here, both Animal and Cat have a method called speak(), but the version
in Cat overrides the one in Animal. This is polymorphism in action—calling
the same method on different objects yields different behavior based on the
object's actual class.
To install TypeScript using Node.js and npm (Node Package Manager), follow
these simple steps:
1. Ensure Node.js is installed on your system. You can verify it using the
command:
nginx
CopyEdit
node -v
nginx
CopyEdit
npm -v
2. Install TypeScript globally so it can be used from anywhere on your
system:
nginx
CopyEdit
npm install -g typescript
nginx
CopyEdit
tsc -v
This command confirms that the TypeScript compiler (tsc) is ready to compile
.ts files into JavaScript. You can now begin writing and compiling TypeScript
code.
25. Write a simple TypeScript file that includes variables, functions, and
classes.
typescript
CopyEdit
// app.ts
// Class definition
class Student {
constructor(public name: string, public grade:
number) {}
display(): string {
return `${this.name} is in grade ${this.grade}.`;
}
}
To run this, you compile it with tsc app.ts to get the corresponding
JavaScript file.
26. Provide examples for basic types, enums, interfaces, classes, and
generics in TypeScript.
typescript
CopyEdit
// Basic types
let count: number = 100;
let isDone: boolean = false;
let title: string = "TypeScript Basics";
// Enum
enum Direction {
Up,
Down,
Left,
Right
}
let dir: Direction = Direction.Up;
// Interface
interface Person {
name: string;
age: number;
}
// Class
class Employee implements Person {
constructor(public name: string, public age:
number, public position: string) {}
describe(): string {
return `${this.name} is a ${this.position}.`;
}
}
// Generics
function identity<T>(arg: T): T {
return arg;
}
These examples show the power and versatility of TypeScript's type system and
how it supports clean, maintainable code.
In TypeScript, modules are files that contain code that can be exported and
reused in other files by importing them. Modules promote code organization
and reuse, especially in large applications. You can export variables, functions,
interfaces, or classes from a module using the export keyword, and import
them elsewhere using import.
file: math.ts
typescript
CopyEdit
export function add(a: number, b: number): number {
return a + b;
}
export const PI = 3.14;
file: app.ts
typescript
CopyEdit
import { add, PI } from "./math";
console.log(add(5, 10)); // 15
console.log(PI); // 3.14
This modular approach helps split code into meaningful units and improves
maintainability and testability.
typescript
CopyEdit
function Logger(constructor: Function) {
console.log(`Class ${constructor.name} has been
created`);
}
@Logger
class Person {
constructor(public name: string) {}
}
When the Person class is defined, the Logger decorator runs and logs the
class creation. Decorators are widely used in Angular for metadata tagging of
classes like components, services, etc.
typescript
CopyEdit
function printValue(value: string | number) {
if (typeof value === "string") {
console.log(value.toUpperCase()); // Safe string
method
} else {
console.log(value.toFixed(2)); // Safe number
method
}
}
This example shows how TypeScript uses type narrowing to determine which
method is safe to use based on the runtime type of the variable.
Type guards are functions that let TypeScript determine the specific type of a
variable within a conditional block. A user-defined type guard is a function that
returns a boolean and has a return type in the format arg is Type. This
helps refine the type of the variable in a type-safe way.
typescript
CopyEdit
interface Cat {
meow(): void;
}
interface Dog {
bark(): void;
}
Chapter 3
bash
CopyEdit
ng generate component home
ng generate component about
ng generate component dashboard
Each command generates a new folder with four files: the TypeScript class,
HTML template, CSS styles, and test specification file. These components can
then be used inside other templates using their selectors.
4. How is routing and navigation implemented in AngularJS? Provide a
step-by-step explanation of the process.
Example:
typescript
CopyEdit
const routes: Routes = [
{ path: 'home', component: HomeComponent },
{ path: 'about', component: AboutComponent },
{ path: 'dashboard', component: DashboardComponent
}
];
html
CopyEdit
<a routerLink="/home">Home</a>
<a routerLink="/about">About</a>
<router-outlet></router-outlet>
html
CopyEdit
<form #userForm="ngForm">
<input name="username" ngModel required />
<input name="email" ngModel email />
<button
[disabled]="!userForm.valid">Submit</button>
</form>
Angular marks fields as valid or invalid automatically, and you can show error
messages accordingly. This system helps ensure that only properly validated
data is submitted, improving the reliability and integrity of the application.
html
CopyEdit
<p>{{ observableData | async }}</p>
html
CopyEdit
<p>{{ 1234.56 | currency:'USD' }}</p> <!-- Output:
$1,234.56 -->
html
CopyEdit
<p>{{ today | date:'fullDate' }}</p>
html
CopyEdit
<p>{{ 3.141592 | number:'1.2-2' }}</p> <!-- Output:
3.14 -->
These pipes are used in templates to format and display data more cleanly and
are essential for localization and UI enhancement.
Example:
javascript
CopyEdit
app.controller('MyController', function($scope) {
$scope.greeting = "Hello, AngularJS!";
$scope.sayHello = function() {
alert($scope.greeting);
};
});
In the HTML:
html
CopyEdit
<div ng-controller="MyController">
<p>{{greeting}}</p>
<button ng-click="sayHello()">Greet</button>
</div>
This structure keeps the logic organized and modular, aligning with the MVC
pattern.
Services in AngularJS are singleton objects that provide functionality and logic
shared across components or controllers. They are used for tasks like API
communication, data management, or utility functions. AngularJS supports
different ways to create services: using .service(), .factory(), or
.provider().
javascript
CopyEdit
app.factory('MathService', function() {
return {
square: function(a) {
return a * a;
}
};
});
javascript
CopyEdit
app.service('GreetingService', function() {
this.sayHello = function(name) {
return "Hello, " + name;
};
});
In general, factories are more flexible, while services are simpler and closer to
object-oriented programming style.
Filters in AngularJS (v1.x) are used to format the value of an expression for
display to the user. They can be applied in view templates using the pipe (|)
symbol. Common filters include:
html
CopyEdit
<p>{{ name | uppercase }}</p>
currency:
html
CopyEdit
<p>{{ amount | currency }}</p>
date:
html
CopyEdit
<p>{{ currentDate | date:'shortDate' }}</p>
html
CopyEdit
<li ng-repeat="item in items | filter:searchText">{{
item.name }}</li>
12. Briefly explain what AngularJS is and list three of its key features.
These features make AngularJS a powerful tool for developing modern web
applications.
Chapter 4
1. What is Node.js?
Node.js offers a variety of powerful features that make it a preferred choice for
modern web development. First and foremost, it is asynchronous and event-
driven, meaning that operations like reading files, querying databases, and
handling HTTP requests are non-blocking, improving performance under heavy
loads. Another major feature is fast execution, made possible by the V8 engine,
which compiles JavaScript to native machine code. Node.js also supports
single-threaded but highly scalable architecture, using an event loop to
handle concurrent connections. Additionally, Node.js comes with built-in
modules (such as fs, http, and path) and a vast ecosystem of third-party
modules accessible via NPM (Node Package Manager). It supports cross-
platform development and is ideal for building REST APIs, microservices, and
even desktop applications when used with frameworks like Electron.
Node.js brings several advantages that make it ideal for building fast and
scalable server-side applications. The foremost advantage is its asynchronous,
non-blocking nature, which allows handling multiple requests without waiting
for I/O operations to complete. This increases throughput and performance.
Secondly, JavaScript everywhere—both on the client and server side—means
unified development, allowing full-stack development using a single language.
The NPM ecosystem is another strength, providing access to millions of open-
source libraries that speed up development. Node.js is also lightweight and
highly scalable, making it perfect for real-time applications like chat servers,
online gaming, and collaborative tools. Its large community and strong
corporate backing by companies like Google and Microsoft contribute to its
continuous growth and improvement.
The traditional web server model, such as that used by Apache or IIS, follows
a multi-threaded approach. Each incoming request from a client (e.g., a
browser) is assigned its own thread on the server. If a server receives 1000
concurrent requests, it spawns 1000 threads, each requiring memory and
processing power. If a thread is waiting for a file read or database operation, it
remains blocked, consuming system resources. This model works well for
applications with low concurrency but struggles with performance and
scalability under heavy loads, as system resources become a bottleneck. In high-
traffic scenarios, spawning too many threads can cause delays and crashes due
to thread exhaustion.
Example:
javascript
CopyEdit
console.log("Start");
setTimeout(() => {
console.log("Inside timeout");
}, 2000);
console.log("End");
Output:
sql
CopyEdit
Start
End
Inside timeout
This shows that even though setTimeout is declared early, the event loop
allows the rest of the code to execute first while it waits, demonstrating non-
blocking behavior.
1. Core Modules: Built-in modules like fs, http, path, and os that
come with Node.js.
2. Local Modules: User-defined modules created within your application
using module.exports.
3. Third-party Modules: Modules installed via NPM such as express,
lodash, or mongoose.
Core modules are the built-in modules provided by Node.js that require no
installation. These modules are essential for many tasks, such as file handling
(fs), networking (http), and path resolution (path). Since they are compiled
into the Node.js binary, they load faster and are optimized for performance.
javascript
CopyEdit
const fs = require('fs');
A local module is a user-defined module created within the same project. You
define a local module by placing your code in a separate file and exposing the
desired functions or objects using module.exports. You then import the
module using require().
Example:
module.exports = { add };
app.js:
javascript
CopyEdit
const math = require('./math');
console.log(math.add(2, 3)); // Output: 5
This shows how code can be modularized and reused across different files using
local modules, improving code maintainability.
Structure:
pgsql
CopyEdit
myModule/
├── index.js
├── utils.js
index.js:
javascript
CopyEdit
const util = require('./utils');
module.exports = util;
utils.js:
javascript
CopyEdit
exports.sayHello = () => {
return "Hello!";
};
javascript
CopyEdit
const myModule = require('./myModule');
console.log(myModule.sayHello()); // Output: Hello!
Example:
javascript
CopyEdit
// math.js
module.exports.add = function(a, b) {
return a + b;
};
// app.js
const math = require('./math');
console.log(math.add(2, 3)); // Output: 5
1. Exporting an Object:
You can export an object with multiple properties or functions.
javascript
CopyEdit
// math.js
module.exports = {
add: function(a, b) { return a + b; },
subtract: function(a, b) { return a - b; }
};
javascript
CopyEdit
const math = require('./math');
console.log(math.add(2, 3)); // Output: 5
console.log(math.subtract(5, 3)); // Output: 2
2. Exporting a Function:
You can export a single function directly.
javascript
CopyEdit
// greet.js
module.exports = function(name) {
return `Hello, ${name}!`;
};
javascript
CopyEdit
const greet = require('./greet');
console.log(greet('John')); // Output: Hello,
John!
3. Exporting a Value:
You can assign a single value to module.exports, such as a number
or a string.
javascript
CopyEdit
// number.js
module.exports = 42;
javascript
CopyEdit
const num = require('./number');
console.log(num); // Output: 42
javascript
CopyEdit
const fs = require('fs');
In this example, "File read in progress..." is logged before the file reading
completes, showing the non-blocking behavior.
Example:
javascript
CopyEdit
function greeting(name, callback) {
console.log(`Hello, ${name}!`);
callback();
}
function sayGoodbye() {
console.log('Goodbye!');
}
Example:
javascript
CopyEdit
function getData() {
return new Promise((resolve, reject) => {
let success = true;
if (success) {
resolve("Data received successfully!");
} else {
reject("Error retrieving data");
}
});
}
getData()
.then(response => console.log(response)) //
Output: Data received successfully!
.catch(error => console.log(error)); // Error:
Error retrieving data
Example:
javascript
CopyEdit
const fs = require('fs');
fs.readFile('nonexistentfile.txt', 'utf8',
function(err, data) {
if (err) {
console.error('Error:', err); // Error handling
for async functions
return;
}
console.log(data);
});
In this example, if the file does not exist, an error is logged in the callback
function.
async and await are modern JavaScript features that simplify working with
Promises and asynchronous code. The async keyword is used to declare a
function as asynchronous, and within this function, you can use await to pause
the execution of the function until a Promise is resolved or rejected.
Example:
javascript
CopyEdit
async function fetchData() {
let data = await getData(); // Wait for the
promise to resolve
console.log(data);
}
fetchData();
function getData() {
return new Promise(resolve => {
setTimeout(() => resolve("Data received!"),
2000);
});
}
NPM (Node Package Manager) is the default package manager for Node.js,
used for managing JavaScript libraries, tools, and modules. It allows developers
to install, share, and manage dependencies in their Node.js projects. NPM
maintains an extensive registry of open-source packages that can be installed
using commands like npm install <package-name>. Additionally, npm
can be used to publish packages to the registry, manage package versions, and
automate script execution.
To install packages locally in a Node.js project, you use the npm install
<package-name> command. This installs the package in the
node_modules directory within your project folder, making it available to
your project. Local installations are saved as dependencies in the
package.json file.
Example:
bash
CopyEdit
npm install express --save
This command installs the express package locally and adds it to the
dependencies section of your package.json file.
To add a dependency manually, you can open your package.json file and
add the dependency under the dependencies section:
json
CopyEdit
{
"name": "my-app",
"version": "1.0.0",
"dependencies": {
"express": "^4.17.1"
}
}
Alternatively, to add dependencies using the terminal, you can run the following
command:
bash
CopyEdit
npm install express --save
1. Installing Dependencies:
o Use npm install to install dependencies from the
package.json file.
o For specific packages, use npm install <package-name>.
2. Saving Dependencies:
o By default, npm install saves installed packages in the
node_modules folder and updates package.json to reflect
the installed versions under the dependencies section.
3. Updating Dependencies:
o To update a specific dependency, use npm update
<package-name>.
o To update all dependencies, use npm update.
4. Removing Dependencies:
o To remove a package, use npm uninstall <package-
name>. This will remove the package from the node_modules
folder and the package.json file.
5. Checking for Vulnerabilities:
o Use npm audit to check for vulnerabilities in your
dependencies.
javascript
CopyEdit
const fs = require('fs');
const readableStream =
fs.createReadStream('example.txt', 'utf8');
readableStream.on('data', (chunk) => {
console.log(chunk);
});
2. Writable Streams: Streams that allow writing data. For example, writing
to files, HTTP requests, or logging systems.
Example:
javascript
CopyEdit
const fs = require('fs');
const writableStream =
fs.createWriteStream('output.txt');
writableStream.write('Hello, world!\n');
writableStream.end();
3. Duplex Streams: Streams that can read and write. Example: TCP socket
connections.
Example:
javascript
CopyEdit
const net = require('net');
const server = net.createServer((socket) => {
socket.write('Welcome to the server!');
socket.on('data', (data) => {
console.log(data.toString());
});
});
server.listen(8080);
javascript
CopyEdit
const zlib = require('zlib');
const fs = require('fs');
const input = fs.createReadStream('input.txt');
const output =
fs.createWriteStream('output.txt.gz');
const gzip = zlib.createGzip();
input.pipe(gzip).pipe(output);
Streams in Node.js emit various events during their lifecycle. Some of the key
events are:
Example:
javascript
CopyEdit
const fs = require('fs');
const readableStream =
fs.createReadStream('example.txt', 'utf8');
readableStream.on('data', (chunk) => {
console.log('Reading:', chunk);
});
readableStream.on('end', () => {
console.log('Stream ended.');
});
27. How can you read data from a file using streams?
To read data from a file using streams in Node.js, you can use the
fs.createReadStream() function. This method allows you to read large
files in chunks without loading the entire file into memory, making it more
memory-efficient.
Example:
javascript
CopyEdit
const fs = require('fs');
const readableStream =
fs.createReadStream('example.txt', 'utf8');
readableStream.on('data', (chunk) => {
console.log(chunk); // Output the chunk being read
});
readableStream.on('end', () => {
console.log('File reading completed.');
});
Example:
javascript
CopyEdit
const fs = require('fs');
const writableStream =
fs.createWriteStream('output.txt');
writableStream.write('Hello, world!\n');
writableStream.write('Writing data to file...\n');
writableStream.end();
Piping is the process of passing data from one stream to another. Node.js
provides the pipe() method to pass the readable stream's data to a writable
stream, simplifying the process and reducing the need for event listeners.
Example:
javascript
CopyEdit
const fs = require('fs');
const zlib = require('zlib');
In this example, the data from input.txt is read, compressed using gzip,
and then written to output.txt.gz.
Example:
javascript
CopyEdit
const fs = require('fs');
const zlib = require('zlib');
const { Transform } = require('stream');
In this example, the input stream is piped through the gzip transform stream
and then piped to the output writable stream.
Example:
javascript
CopyEdit
const fs = require('fs');
const zlib = require('zlib');
const { Transform } = require('stream');
In this case, the data flows through multiple streams in sequence: first the file is
read, then it’s compressed, and finally, it’s written to a new file.
Chapter 5
In Express.js, routing refers to defining the paths and handling HTTP requests
made to those paths. Routing defines how an application responds to client
requests for specific URLs.
Example of routing:
javascript
CopyEdit
const express = require('express');
const app = express();
The MVC pattern helps in organizing code by separating the business logic
(Model), user interface (View), and application flow (Controller), making
applications easier to maintain and scale.
javascript
CopyEdit
// Model (User.js)
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
name: String,
email: String,
});
const User = mongoose.model('User', UserSchema);
module.exports = User;
javascript
CopyEdit
// View (EJS template)
<h1>Hello, <%= user.name %>!</h1>
javascript
CopyEdit
// Controller (UserController.js)
const User = require('./models/User');
const express = require('express');
const router = express.Router();
Routing in web applications refers to the mechanism that defines how HTTP
requests (such as GET, POST, PUT, DELETE) are handled by the server. It
determines what code or function should be executed when a particular URL or
endpoint is accessed. Routing helps map URLs to specific actions, making it an
essential part of any web application. It is responsible for handling the incoming
requests and sending appropriate responses back to the client.
Example:
javascript
CopyEdit
const express = require('express');
const app = express();
app.listen(3000, () => {
console.log('Server running on port 3000');
});
In this example, ExpressJS defines a GET route for /home and a POST route
for /submit, handling different HTTP requests accordingly.
10. Explain the difference between route parameters and query parameters
in ExpressJS.
Route Parameters: These are parts of the URL that act as placeholders
for values. Route parameters are defined in the URL path and are
typically used for identifying a specific resource. They are accessible via
req.params.
Example:
javascript
CopyEdit
app.get('/user/:id', (req, res) => {
const userId = req.params.id; // Accessing
route parameter
res.send(`User ID is ${userId}`);
});
Query Parameters: These are part of the URL that comes after a
question mark and are typically used to filter or sort results. Query
parameters are accessed via req.query.
Example:
javascript
CopyEdit
app.get('/search', (req, res) => {
const searchTerm = req.query.term; //
Accessing query parameter
res.send(`Searching for ${searchTerm}`);
});
11. Describe the role of HTTP requests and responses in web development.
The interaction between HTTP requests and responses enables clients to retrieve
and submit data to servers.
12. How does ExpressJS handle different types of HTTP requests (GET,
POST, etc.)?
For example:
javascript
CopyEdit
const express = require('express');
const app = express();
app.listen(3000, () => {
console.log('Server running on port 3000');
});
Here, the application listens for GET, POST, PUT, and DELETE requests on
different routes and provides responses accordingly.
Example of a middleware:
javascript
CopyEdit
const express = require('express');
const app = express();
// Custom middleware
app.use((req, res, next) => {
console.log('Middleware executed');
next(); // Pass control to the next middleware
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
In this example, the middleware logs every incoming request before the request
reaches the route handler.
javascript
CopyEdit
app.use((req, res, next) => {
console.log(`Request method: ${req.method}, URL:
${req.url}`);
next();
});
javascript
CopyEdit
const bodyParser = require('body-parser');
app.use(bodyParser.json()); // Parse JSON bodies
app.use(bodyParser.urlencoded({ extended: true
})); // Parse URL-encoded bodies
javascript
CopyEdit
const morgan = require('morgan');
app.use(morgan('dev'));
javascript
CopyEdit
const cookieParser = require('cookie-parser');
app.use(cookieParser());
javascript
CopyEdit
const cors = require('cors');
app.use(cors()); // Allow all origins
Example:
javascript
CopyEdit
// Custom middleware for logging
app.use((req, res, next) => {
console.log(`Incoming request: ${req.method}
${req.url}`);
next(); // Pass to the next middleware or route
handler
});
javascript
CopyEdit
// Apply middleware to a specific route
app.get('/home', (req, res, next) => {
console.log('Home route accessed');
next();
}, (req, res) => {
res.send('Welcome to the homepage');
});
javascript
CopyEdit
app.get('/error', (req, res) => {
throw new Error('Something went wrong!');
});
// Error-handling middleware
app.use((err, req, res, next) => {
console.error(err.stack); // Log the error stack
res.status(500).send('Something went wrong!');
});
In this example, the application throws an error, which is caught by the error-
handling middleware, and a 500 error response is sent to the client.
19. Explain the use of the next function in error handling middleware.
In ExpressJS, the next function is used to pass control to the next middleware
in the chain. In error-handling middleware, the next() function is typically
used to forward the error to the next error handler, or to a general error handler
when no specific handler is defined. If an error is passed to next(), Express
will skip all remaining route handlers and go directly to the error-handling
middleware.
Example:
javascript
CopyEdit
app.use((req, res, next) => {
const err = new Error('Something went wrong!');
err.status = 500;
next(err); // Pass the error to the next
middleware
});
Here, the error is created and passed to the next() function, which is handled
by the global error handler.