Unit 5 - Integration

A complete coverage of all topics from CodeTantra notes of Unit 5 - Integration.

Author
Materio
Listen

Introduction

The MEAN Stack is a powerful combination of technologies - MongoDB, Express.js, Angular, and Node.js used to build full-stack JavaScript applications. In a MEAN application, every layer uses JavaScript, making development faster and more consistent.

We already know the components of the MEAN stack, let's have a quick review again.

Components of the MEAN Stack

LayerTechnologyDescription
FrontendAngularBuilds dynamic, responsive user interfaces.
Backend FrameworkExpress.jsHandles APIs, routes, and server-side logic.
Runtime EnvironmentNode.jsExecutes JavaScript on the server.
DatabaseMongoDBStores application data in flexible JSON-like documents.

Workflow in a MEAN Application

Let's look at how data flows through these layers step by step:

  1. User Interaction (Angular): A user performs an action on the Angular frontend, for example, viewing a list of products or adding one to the cart.
  2. HTTP Request (Express API): Angular sends an HTTP request (GET, POST, PUT, DELETE) to the backend API built using Express.js.
  3. Server Logic (Express + Node): The Express server processes the request, for example, fetches data from MongoDB or saves new data.
  4. Database Operation (MongoDB): The server queries MongoDB using Mongoose and retrieves or updates the required data.
  5. Response Sent Back to Angular: Express returns the data or response to Angular through JSON.
  6. UI Update (Angular): Angular receives the response and updates the user interface showing updated products, cart items, etc.

Flow Summary

User Action -> Angular (Frontend) -> Express.js (API) -> MongoDB (Database) -> Express.js (Response) -> Angular (UI Update)

MCQ

Question: In a MEAN application, which component handles the connection and communication with the database?

  • Angular
  • Express.js
  • Node.js
  • MongoDB

Answer: Express.js

Client-Server Communication

In a real-world full-stack application, Angular acts as the frontend client, while Express works as the backend server.

The communication between them happens through HTTP requests and responses, just like how your browser communicates with any website on the internet.

How Client-Server Communication Works

Let's understand this communication step by step.

  1. Angular (Client)

    • Angular runs in the browser.
    • It is responsible for displaying the UI and collecting user input.
    • When the user performs some action (like submitting a form), Angular sends an HTTP request to the backend server.
  2. Express (Server)

    • Express runs on the server (for example, http://localhost:3000, http://backend:3000, or a remote server).
    • It listens for incoming HTTP requests like GET, POST, PUT, and DELETE.
    • When it receives a request, it processes the data (for example, fetches from MongoDB) and sends back a response such as JSON data or a message.
  3. The Communication Bridge

    • Angular uses the HttpClient module to send requests.
    • Express uses routes to define endpoints that handle those requests.
    • This communication usually happens in JSON format, which is a lightweight data format used to send data between the client and server.

MCQ

Question: Which of the following correctly explains how Angular communicates with an Express server?

  • Angular directly accesses the server's database.
  • Express sends HTTP requests to Angular components.
  • Both Angular and Express communicate using WebSockets only.
  • Angular sends HTTP requests to the Express server, which processes them and sends responses back.

Answer: Angular sends HTTP requests to the Express server, which processes them and sends responses back.

MongoDB as the Persistent Data Layer

MongoDB is the database in the MEAN stack. It provides persistent storage for application data so information like products, users, carts, and orders remains available across restarts and sessions.

In an integrated project, Express interacts with MongoDB, and Angular accesses data via Express APIs.

What "Persistent Data Layer" Means

  • Persistent: Data survives process restarts and system reboots; it is stored on disk, not just in memory.
  • The data layer is responsible for storing, retrieving, updating, and deleting application data reliably.

How MongoDB Fits into the MEAN Flow

  1. Angular (client) sends an HTTP request to an Express endpoint, for example GET /api/products.
  2. Express (server) receives the request and asks MongoDB for data using Mongoose or the MongoDB driver.
  3. MongoDB returns the requested documents or confirms write operations.
  4. Express transforms or validates data if needed and sends a JSON response back to Angular.
  5. Angular receives the response and updates the UI.

Why MongoDB Is a Good Fit

  • Document model (JSON-like): Stores data as BSON documents with minimal transformation between client and database.
  • Flexible schema: Add or change fields without complex migrations.
  • Good Node.js integration: Libraries like Mongoose provide schema and validation on top of MongoDB.
  • Scales horizontally: Suits applications that grow in users and data volume.

MCQ

Question: In the MEAN stack, what does the term "Persistent Data Layer" refer to, and how does MongoDB fulfill this role?

  • It refers to temporary storage that clears when the server restarts; MongoDB stores data only in memory.
  • It means data is stored permanently on disk and survives restarts; MongoDB provides this persistence using BSON documents.
  • It refers to the caching layer used for faster responses; MongoDB stores short-term session data only.
  • It means data is stored in JSON files on the frontend managed by Angular.

Answer: It means data is stored permanently on disk and survives restarts; MongoDB provides this persistence using BSON documents.

Socket.IO for Real-Time Updates

To handle real-time updates, the server needs a way to maintain live connections with clients. While Express handles HTTP routes, it does not natively support WebSockets, so we use Socket.IO, a library that simplifies real-time event handling.

What Is Socket.IO?

Socket.IO is a JavaScript library that enables real-time, bi-directional communication between web clients and servers.

It works on top of WebSockets and adds automatic reconnection, event handling, and broadcasting features.

You can use it easily with your existing Express app.

How It Works

  1. The Express app starts as usual.
  2. You create a server using Node's http module.
  3. You attach Socket.IO to that server.
  4. When a client connects, you can:
    • Listen for events from the client, like message or cartUpdate.
    • Send events back to the client instantly, like orderStatus.

Step 1: Install the Packages

npm install express socket.io

Step 2: Create a Basic Express + Socket.IO Server

import { Server } from "socket.io";
import http from "http";
import express from "express";

const app = express();
const server = http.createServer(app);

// Initialize Socket.IO with CORS enabled
const io = new Server(server, { cors: { origin: "*" } });

// Handle client connection
io.on("connection", (socket) => {
  console.log("Client connected:", socket.id);

  // Send a message to the client
  socket.emit("welcome", "Connected to Socket.IO server");

  // Receive message from client
  socket.on("messageFromClient", (data) => {
    console.log("Client says:", data);
  });
});

server.listen(3000);

Here:

  • It creates a Socket.IO instance on top of an Express HTTP server.
  • It listens for connection events.
  • It sends (emit) and receives (on) real-time messages.

Step 3: Run the Server

node server.js

After running the command, you will be able to watch:

Server running on port 3000
Client connected: <socket-id>

MCQ

Question: Which Node.js module is used to create the base server before attaching Socket.IO?

  • fs
  • os
  • http
  • net

Answer: http

Connecting Angular with WebSockets

Now that the backend is ready with Socket.IO, the next step is to connect the Angular frontend to that WebSocket server.

WebSocket connections allow your Angular app to receive updates instantly without needing to refresh or call an API repeatedly.

For this, we use the ngx-socket-io library, which makes it simple to integrate Socket.IO in Angular.

Step 1: Install ngx-socket-io

Run this command in your Angular project:

npm install ngx-socket-io

Step 2: Configure it in app.module.ts

Add the Socket.IO configuration with the backend URL where your Express server is running.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { SocketIoModule, SocketIoConfig } from 'ngx-socket-io';

const config: SocketIoConfig = {
  url: 'http://localhost:3000',
  options: {}
};

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, SocketIoModule.forRoot(config)],
  bootstrap: [AppComponent]
})
export class AppModule {}

Here:

  • The Socket.IO module and config are imported from ngx-socket-io.
  • The module is added to the imports list.

Step 3: Use Socket Service in a Component

import { Component, OnInit } from '@angular/core';
import { Socket } from 'ngx-socket-io';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
  message = '';

  constructor(private socket: Socket) {}

  ngOnInit() {
    // Listen for message from the server
    this.socket.on('welcome', (data: string) => {
      this.message = data;
    });
  }
}

Here:

  • socket.on() is used to receive real-time messages from the backend.
  • message stores the received data to show in the UI.
  • This demonstrates real-time communication between frontend and backend using WebSockets.

Step 4: Display Message in HTML

<div class="container">
  <h2>Socket.IO Connection</h2>
  <p>{{ message }}</p>
</div>

MCQ

Question: Why do we use ngx-socket-io in an Angular application?

  • To manage HTTP requests more efficiently.
  • To improve Angular routing.
  • To perform database operations in Angular.
  • To easily integrate Socket.IO for real-time communication.

Answer: To easily integrate Socket.IO for real-time communication.

HTTP Model and WebSockets

In a typical web app, the browser (client) sends a request, and the server sends back a response. This is called the HTTP model. Once the response is received, the connection closes.

However, for real-time updates, this is not enough.

We need a way for the server to send data to the client immediately without waiting for a new request.

What Are WebSockets?

WebSockets allow the client and server to keep a persistent, open connection.

This means both sides can send and receive messages at any time.

It is like keeping a phone call active. Both people can talk whenever they want, instead of sending separate letters back and forth.

HTTP vs WebSocket

FeatureHTTPWebSocket
Communication TypeRequest-responseFull duplex (two-way)
ConnectionOpens and closes per requestPersistent
Data FlowClient to server onlyClient and server
Real-Time UpdatesNot suitableIdeal
Use CasesStatic data, forms, APIsChats, notifications, live updates

Real-World Examples

  • Live Chat: Instantly see messages from others without refreshing.
  • Order Tracking: Get notified when your order status changes.
  • Stock Dashboards: Data updates the moment something changes.

MCQ

Question: In a typical web app using the HTTP model, when does the connection between the client and server close?

  • After every request and response.
  • It remains open all the time.
  • Only when the browser closes.
  • After multiple requests are sent.

Answer: After every request and response.

Keeping the UI in Sync

When using WebSockets, your backend can send updates to the frontend at any time. But the real challenge is how to make sure the UI stays in sync with these changes.

For example:

  • If stock quantity changes on the server, all connected users should see the updated number.
  • If a new order is placed, admins should instantly see it in their dashboard.

Working Mechanism

  • The backend emits events whenever data changes.
  • The frontend listens for those events using socket.on().
  • The Angular UI updates instantly to reflect the new data without refreshing or calling APIs again.

Real-Time Stock Update Example

Step 1: Backend Emits the Event

io.emit('stockUpdate', { product: 'Laptop', stock: 12 });

This sends the latest stock data to all connected clients.

Step 2: Angular Listens and Updates

ngOnInit() {
  this.socket.on('stockUpdate', (data: any) => {
    this.product = data.product;
    this.stock = data.stock;
  });
}

Here:

  • The ngOnInit() lifecycle hook runs as soon as the component is initialized.
  • this.socket.on('stockUpdate', ...) tells Angular to listen for an event named stockUpdate from the server.

Step 3: Update the UI

<div class="container">
  <h2>Live Stock Updates</h2>
  <p *ngIf="product">Product: {{ product }}</p>
  <p *ngIf="stock">Available Stock: {{ stock }}</p>
</div>

Whenever the backend emits a stockUpdate event, the Angular app immediately receives the new data. The UI then updates automatically through Angular's data binding, ensuring that all users instantly see the same up-to-date stock values without any manual refresh.

MCQ

Question: What is the main challenge when working with WebSockets in a web application?

  • Sending large files through sockets.
  • Making sure the UI stays in sync with backend updates.
  • Setting up multiple HTTP routes.
  • Handling static assets in Express.

Answer: Making sure the UI stays in sync with backend updates.

Real-Time Notification Feature

Now that both the backend and frontend are connected using Socket.IO, it is time to implement a real feature that shows real-time communication in action.

Real-time features make web applications interactive, dynamic, and engaging, such as:

  • Live Chat
  • Order Notifications
  • Real-Time Stock Updates

How It Works

  1. Server emits an event, for example newNotification.
  2. All connected clients instantly receive that event.
  3. The Angular frontend updates the UI immediately to show the message with no refresh needed.

Step 1: Update the Backend (Server Side)

In your Express + Socket.IO server, emit a real-time event to all clients.

io.emit('newNotification', 'A new order has been placed!');

This sends a newNotification event to all connected clients.

Step 2: Listen in Angular (Frontend Side)

import { Component, OnInit } from '@angular/core';
import { Socket } from 'ngx-socket-io';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
  notification = '';

  constructor(private socket: Socket) {}

  ngOnInit() {
    // Listen for real-time notifications from backend
    this.socket.on('newNotification', (data: string) => {
      this.notification = data;
    });
  }
}

Here:

  • this.socket.on() is used to listen for real-time events or messages sent from the backend through the WebSocket connection.

Step 3: Display in HTML

<div class="container">
  <h2>Real-Time Notification</h2>
  <p *ngIf="notification">{{ notification }}</p>
</div>

The backend sends out a message event called newNotification, and the Angular app listens for it using socket.on(). Whenever this event is emitted, the message instantly appears in the browser without any page refresh or API call.

MCQ

Question: In a real-time system, what happens when the server emits an event like newNotification?

  • Only one client receives the update.
  • The event is ignored until the next refresh.
  • All connected clients instantly receive the message.
  • The event is stored for later retrieval.

Answer: All connected clients instantly receive the message.

Common Integration Issues

You already know that your application has three major layers: frontend, backend, and database. A small issue in communication between any of these can break the flow.

Let's go through the most frequent problems developers face when connecting these layers.

1. CORS (Cross-Origin Resource Sharing) Errors

Angular running on port 4200 sends a request to Express running on port 3000. The browser blocks it because of different origins.

To solve this, enable CORS in Express:

import cors from 'cors';

app.use(cors());

2. Proxy Configuration Issues in Angular

You set up the proxy to forward /api calls to Express, but it fails because of the wrong proxy path.

Example:

GET http://localhost:4200/api/products 404 (Not Found)

To resolve this, check your proxy.conf.json:

{
  "/api": {
    "target": "http://localhost:3000",
    "secure": false
  }
}

3. Incorrect API Endpoints

The frontend may try to access a route that does not exist in Express, which results in 404 Not Found. To overcome this, verify endpoints on both sides and ensure naming consistency.

4. MongoDB Connection Errors

The backend may fail to connect to MongoDB due to a wrong URI or because MongoDB is not running.

mongoose.connect('mongodb://mongo:27017/shoppingDB')

MCQ

Question: Which issue occurs when Angular (port 4200) cannot access Express (port 3000) due to different origins?

  • Wrong proxy path
  • MongoDB connection error
  • CORS error
  • Missing API endpoint

Answer: CORS error

Express Error Handling Middleware

You already learned about centralized error handling middleware in Express, and now we use that in the shopping application.

Creating the Error Handling Middleware

Add this middleware to your Express backend in middlewares/errorHandler.js.

export function errorHandler(err, req, res, next) {
  console.error('Error:', err.message);

  // Define a default status code
  const status = err.statusCode || 500;

  // Send a structured error response
  res.status(status).json({
    success: false,
    message: err.message || 'Something went wrong on the server.',
    stack: err.stack,
  });
}

Here:

  • err is the error object passed from routes or other middleware.
  • status sets a default HTTP status code (500 if none is provided).
  • res.status().json() sends a clear, structured error response.
  • console.error() logs the error for debugging.

Express error handlers must have 4 arguments (err, req, res, next), or Express will not recognize them as error middleware.

Register the Middleware

Finally, import and use this middleware after all route definitions in your index.js.

import { errorHandler } from './middlewares/errorHandler.js';

app.use(errorHandler);

MCQ

Question: Where should the Express error handling middleware be placed?

  • Before defining routes
  • Inside each route
  • At the top of the file
  • After all route definitions

Answer: After all route definitions

Angular HTTP Interceptor

In a shopping app, many HTTP requests are made. If an API fails or the network is unstable, the app can break unless errors are handled properly. This is where Angular Interceptors come in.

An HTTP Interceptor acts like middleware for all outgoing and incoming HTTP requests. It allows us to:

  • Catch errors globally instead of handling errors inside every service.
  • Modify requests, for example by adding authentication tokens.
  • Handle specific HTTP status codes like 401, 404, or 500.

Creating the Interceptor in Angular

Let's create a global interceptor to handle HTTP and API errors in one place inside app/interceptors/error-interceptor.ts.

import { HttpErrorResponse, HttpInterceptorFn } from '@angular/common/http';
import { catchError, throwError } from 'rxjs';

export const errorInterceptor: HttpInterceptorFn = (req, next) =>
  next(req).pipe(
    catchError((error: HttpErrorResponse) => {
      const errorMessage = error.error instanceof ErrorEvent
        ? `Network Error: ${error.error.message}`
        : ({
            0: 'Unable to connect to the server.',
            400: '400 Bad Request. Please check your input.',
            404: '404: Requested resource not found.',
            500: '500: Internal Server Error. Please try again later.'
          }[error.status] || `Unexpected Error: ${error.statusText}`);

      console.error('HTTP Error:', errorMessage);
      return throwError(() => new Error(errorMessage));
    })
  );

Here:

  • HttpInterceptorFn defines a function that intercepts all HTTP requests and responses in Angular.
  • next(req).pipe(catchError(...)) sends the request to the server and allows us to catch any errors that come back.
  • Client-side errors (ErrorEvent) happen on the user's device, like network issues or browser problems.
  • Server-side errors (HttpErrorResponse) happen on the server, where you can inspect status codes and show meaningful messages.
  • console.error() logs the error in the browser console for debugging.
  • throwError() sends the error to the component or service that made the request so it can be displayed or handled globally.

Registering the Interceptor Globally

To use this interceptor for all HTTP requests, register it in app.config.ts:

provideHttpClient(
  withFetch(),
  withInterceptors([errorInterceptor])
)

This ensures all outgoing HTTP requests and incoming responses go through the interceptor automatically.

MCQ

Question: In Angular, what is the main purpose of using an HttpInterceptor like the errorInterceptor shown above?

  • To directly update the UI with HTTP responses
  • To catch errors globally, modify requests, and handle specific HTTP status codes
  • To replace Angular services for making HTTP requests
  • To prevent HTTP requests from being sent

Answer: To catch errors globally, modify requests, and handle specific HTTP status codes