Ticker

6/recent/ticker-posts

Node.js Event Loop, Global Objects & Module System Explained with Real-World Examples

 

 The Secret Behind Handling Thousands of Requests

The Question Every Backend Developer Eventually Asks

Imagine your AQAD marketplace goes live.

Suddenly:

5,000 Retailers
2,000 Vendors
Hundreds of Orders Per Minute
Thousands of Product Searches

are happening simultaneously.

Yet your Node.js server continues running smoothly.

Naturally, someone asks:

"How can a single Node.js process handle so many requests?"

This question leads us to one of the most important concepts in backend development:

The Event Loop

Node.js Event Loop, Global Objects & Module System Explained with Real-World Examples


Many developers hear this term.

Some memorize interview answers.

But very few truly understand what it does.

By the end of this chapter, you'll understand it well enough to explain it to another developer using nothing more than a restaurant story.


First, Let's Clear Up a Common Misunderstanding

Many beginners think:

Node.js = Multi-threaded Magic

Not exactly.

Your JavaScript code usually runs on:

One Main Thread

This surprises people.

Because they immediately ask:

"Then how does Node.js handle thousands of requests?"

The answer is:

It spends very little time waiting.

And the Event Loop plays a huge role in making that possible.


The Mega Restaurant Analogy

Imagine AQAD owns a massive restaurant.

Inside the restaurant:

One Head Waiter

Responsible for:

Taking Orders
Managing Requests
Coordinating Tasks

Multiple Kitchen Teams

Responsible for:

Cooking
Cleaning
Packaging
Deliveries

The head waiter never cooks.

The head waiter never washes dishes.

The head waiter delegates work.

This is how Node.js operates.


Mapping The Restaurant To Node.js

Restaurant:

Head Waiter

Node.js:

Event Loop

Restaurant:

Kitchen Staff

Node.js:

Operating System
libuv Thread Pool

Restaurant:

Customers

Node.js:

Incoming Requests

Restaurant:

Order Queue

Node.js:

Callback Queues

What Is The Event Loop?

Simple definition:

The Event Loop is a mechanism that continuously checks for tasks that are ready to execute and processes them when JavaScript is available.

That's the technical explanation.

Let's make it human.

Think:

Check Tasks
↓
Execute Task
↓
Check Again
↓
Execute Next Task
↓
Repeat Forever

That's essentially what the Event Loop does.


Why Node.js Needs The Event Loop

Imagine a retailer requests:

Product Catalog

Database query takes:

500ms

Should JavaScript stop everything and wait?

No.

Instead:

Send Query
Continue Working

When results arrive:

The Event Loop handles the callback.

This is why Node.js feels fast.


Visualizing Request Processing

Request arrives:

Get Products

Flow:

Request
↓
Event Loop
↓
Database Query
↓
Continue Processing Other Requests
↓
Database Response
↓
Callback Queue
↓
Event Loop Executes Callback
↓
Send Response

Notice:

Node.js isn't sitting idle.

It's constantly moving.


Understanding Event Loop Phases

Now we go one level deeper.

Many articles stop at the basic explanation.

Professional backend developers should understand the phases.

Imagine our restaurant has multiple workstations.

Each station handles specific tasks.

Node.js Event Loop behaves similarly.


High-Level Event Loop Flow

Timers
↓
Pending Callbacks
↓
Idle / Prepare
↓
Poll
↓
Check
↓
Close Callbacks

Then:

Repeat
Repeat
Repeat

This cycle runs continuously.

Let's simplify each phase.


Phase 1: Timers

Handles:

setTimeout()

setInterval()

Example:

setTimeout(() => {

    console.log("Timer");

}, 1000);

After the timer expires:

The callback becomes eligible for execution.

Notice:

Eligible

does not mean:

Execute Immediately

This distinction matters.


Restaurant Analogy

Customer says:

Bring dessert in 10 minutes.

The kitchen sets a timer.

After 10 minutes:

The dessert becomes ready.

The waiter still needs to serve it.

Timers work similarly.


Phase 2: Pending Callbacks

Handles certain system-level callbacks.

Most beginners don't interact with this phase directly.

Think of it as:

Special Internal Tasks

For now, simply know it exists.


Phase 3: Idle / Prepare

Internal Node.js preparation phase.

Developers rarely interact with it.

Node.js performs housekeeping tasks here.

Think:

Restaurant Preparation Area

Phase 4: Poll Phase

This is the heart of the Event Loop.

Most real work happens here.

Examples:

Database Responses
File Reads
Network Requests
API Responses

The Poll phase waits for completed I/O operations.


AQAD Example

Retailer searches:

Soft Drinks

Node.js sends query:

Database Search

When results arrive:

The Poll phase receives them.

Then callbacks become available.


Why Poll Phase Is Important

Many Node.js applications spend most of their life here.

Because backend applications constantly perform:

Database Access
API Calls
File Operations
Network Requests

All of those eventually pass through the Poll phase.


Phase 5: Check Phase

This phase handles:

setImmediate()

Example:

setImmediate(() => {

    console.log("Immediate");

});

The callback executes during the Check phase.


Phase 6: Close Callbacks

Handles cleanup operations.

Example:

Socket Closed
Connection Closed
Resource Cleanup

Think:

Restaurant Closing Procedures

The Event Loop Never Stops

Visualize:

Timers
↓
Pending
↓
Poll
↓
Check
↓
Close
↓
Repeat Forever

This cycle continues until the application exits.


Understanding setTimeout()

Consider:

console.log("Start");

setTimeout(() => {

    console.log("Timer");

}, 0);

console.log("End");

Many beginners expect:

Start
Timer
End

Actual output:

Start
End
Timer

Why?

Because:

The timer callback must still travel through the Event Loop.


Understanding setImmediate()

Example:

setImmediate(() => {

    console.log("Immediate");

});

This callback executes during the Check phase.

Interviewers often compare:

setTimeout()

vs

setImmediate()

We'll discuss the differences later in more detail.


Understanding process.nextTick()

Now we meet a special tool.

Example:

process.nextTick(() => {

    console.log("Next Tick");

});

This behaves differently.

It executes before the Event Loop continues.

Think:

Urgent VIP Task

The restaurant handles it immediately.


Restaurant Analogy For nextTick

Imagine:

A VIP customer arrives.

Manager says:

Handle this before everything else.

That's similar to:

process.nextTick()

Why Node.js Handles Thousands of Requests

Now we can answer the original question.

Node.js succeeds because:

It Delegates Work

Database
Files
Networking

It Doesn't Wait

Continue Processing Requests

Event Loop Coordinates Everything

Ready Tasks
↓
Execute
↓
Repeat

This model is incredibly efficient.


Real AQAD Scenario

Suppose:

1,000 Retailers Searching Products

500 Vendors Updating Inventory

300 Orders Being Created

Node.js doesn't create thousands of waiting threads.

Instead:

Delegate
Queue
Execute When Ready

This saves memory and improves scalability.


Try It Yourself

Run:

console.log("1");

setTimeout(() => {

    console.log("2");

}, 0);

console.log("3");

Predict the result before executing.

Answer:

1
3
2

Understanding why is more important than memorizing it.


Mini Exercise

Question 1

What is the primary responsibility of the Event Loop?


Question 2

Which phase handles timers?


Question 3

Which phase handles most I/O operations?


Question 4

What does setImmediate() use?


Question 5

Why can Node.js handle many requests efficiently?


Common Beginner Mistakes

Mistake 1

Thinking Node.js executes everything immediately.

Reality:

Callbacks must pass through Event Loop phases.


Mistake 2

Thinking setTimeout(0) means instant execution.

It means:

Execute when possible

Not:

Execute now

Mistake 3

Ignoring asynchronous behavior.

Understanding:

Event Loop
Promises
Async Await

is critical for backend development.


Mistake 4

Memorizing instead of understanding.

Focus on:

Delegation
Queues
Execution Flow

The phases become much easier afterward.


FAQ

Is the Event Loop part of JavaScript?

No.

It's provided by Node.js and supported by libuv.


Does Node.js use threads?

Yes.

But not in the way many beginners imagine.

Node.js uses background threads internally through libuv.


Is the Event Loop always running?

Yes.

As long as the Node.js process is active.


Why is Node.js good for APIs?

Because APIs spend a lot of time waiting for I/O operations.

Node.js handles waiting efficiently.


Global Objects in Node.js
Introduction: The Tools Available Everywhere in Your Backend

Imagine you have just joined a large restaurant as a manager.

On your first day, nobody gives you a box full of tools.

Instead, certain things are already available everywhere in the restaurant:

  • A wall clock
  • Employee attendance records
  • Kitchen status board
  • Emergency contact list
  • Building address
  • Daily sales report

No matter which room you enter, these resources are already there.

You don't need to request them.

You don't need to import them.

You don't need special permission to access them.

They are globally available.

Node.js works in a very similar way.

When your application starts, Node.js automatically provides several useful objects and variables that can be accessed from almost anywhere inside your application.

These are called Global Objects.

They help developers:

  • Access system information
  • Read environment variables
  • Get application paths
  • Handle command-line arguments
  • Control application execution
  • Monitor memory usage
  • Manage process information

Almost every production backend uses these global objects daily.

Whether you are building:

  • An e-commerce website
  • A banking application
  • A social media platform
  • An inventory management system
  • The AQAD marketplace

you will use global objects constantly.

Before building APIs and backend services, understanding these objects is essential.


What Are Global Objects?

A global object is an object that Node.js automatically makes available to your application.

You can use it directly without importing anything.

Example:

console.log("Hello World");

Notice something?

We never imported console.

Yet it works.

Why?

Because console is globally available.

Similarly:

setTimeout(() => {
console.log("Executed");
}, 1000);

Again:

  • No import
  • No require

Because setTimeout() is also globally available.

Node.js provides many such objects.


Why Global Objects Exist

Imagine if every file needed this:

const console = require('console');
const process = require('process');
const setTimeout = require('timers');

Your code would become unnecessarily complicated.

Node.js provides commonly used utilities globally to improve developer productivity.

Benefits:

Simplicity

Less boilerplate code.

Faster Development

Frequently used features are immediately available.

Better Developer Experience

Developers can focus on building applications rather than importing basic utilities.


Most Important Global Objects

As a backend developer you will frequently use:

Global ObjectPurpose
global                    Global namespace
processCurrent running process
__dirnameCurrent directory
__filenameCurrent file
consoleLogging
BufferBinary data
setTimeoutDelayed execution
setIntervalRepeated execution

Understanding the global Object

The global object is the highest-level object in Node.js.

Think of it as the main control room of a large company.

Everything that is globally accessible is attached to this object.

Example:

console.log(global);

This prints the Node.js global object.


Creating Global Variables

Example:

global.companyName = "AQAD";

console.log(global.companyName);

Output:

AQAD

You can access it from any file.


Why Global Variables Are Dangerous

Beginners often think:

"Great! I'll store everything globally."

Bad idea.

Imagine 50 employees editing the same whiteboard simultaneously.

Chaos.

Similarly:

global.user = {
name: "John"
};

Another file:

global.user = {
name: "Ahmed"
};

Now data becomes unpredictable.

Production applications avoid excessive global variables.


The process Object

Among all global objects, process is one of the most important.

It represents the currently running Node.js application.

Think of it as the manager responsible for monitoring the restaurant.

The manager knows:

  • Revenue
  • Staff count
  • Working hours
  • Current status
  • Resource usage

Similarly, process knows everything about the running application.


Getting Process Information

Example:

console.log(process);

This returns detailed information about the running application.


Process ID

Every running application gets an ID.

console.log(process.pid);

Output:

5231

This helps administrators identify running processes.


Node.js Version

console.log(process.version);

Output:

v24.x.x

Useful when debugging compatibility issues.


Current Platform

console.log(process.platform);

Output: win32  or linux  or darwin

This helps create platform-specific logic.


Exiting the Application

Sometimes we want to stop the application.

Example:

process.exit();

Node.js immediately terminates.


AQAD Example

Suppose AQAD cannot connect to DynamoDB.

Instead of keeping a broken application alive:

if (!databaseConnected) {
console.log("Database unavailable");
process.exit(1);
}

This prevents further failures.


Environment Variables

One of the most important backend concepts.

Every professional Node.js application uses environment variables.


The Real-World Problem

Imagine storing AWS credentials directly inside code.

const accessKey = "ABC123";
const secretKey = "XYZ456";

Dangerous.

If code reaches GitHub:

  • Credentials leak
  • Database compromised
  • Cloud resources exposed

Instead we use environment variables.


Accessing Environment Variables

Node.js provides:

process.env

Example:

console.log(process.env);

This prints all environment variables.


Reading Specific Variables

console.log(process.env.PORT);

Output:

5000

AQAD Example

Production configuration:

PORT=5000

AWS_REGION=me-central-1

DB_TABLE=products

JWT_SECRET=mySecretKey

Accessing them:

const PORT = process.env.PORT;

const REGION = process.env.AWS_REGION;

Why Environment Variables Matter

Benefits:

Security

Sensitive data stays outside code.

Flexibility

Different configurations for:

  • Development
  • Testing
  • Production

Scalability

Easy deployment across servers.


Using dotenv

Most projects use:

npm install dotenv

Create:

PORT=5000

Then:

require('dotenv').config();

console.log(process.env.PORT);

Output:

5000

This is standard industry practice.


Understanding __dirname

One of the most used Node.js variables.

__dirname gives the directory of the current file.

Imagine you are inside a warehouse.

Someone asks:

"Which warehouse are you currently standing in?"

That's what __dirname tells Node.js.

Example:

console.log(__dirname);

Output:

C:\Projects\AQAD\controllers

Why __dirname Is Useful

Suppose you need to load a file.

const filePath = __dirname + "/data.json";

Node.js knows exactly where to look.

Without it, file paths can break.


AQAD Example

Product images:

uploads/
products/
vendors/

Saving image:

const path =
__dirname + "/uploads/products";

The backend always knows the correct location.


Understanding __filename

While __dirname gives the folder,

__filename gives the complete file path.

Example:

console.log(__filename);

Output:

C:\Projects\AQAD\controllers\userController.js

Difference Between __dirname and __filename

Example:

__dirname

Output:

C:\Projects\AQAD\controllers

Example:

__filename

Output:

C:\Projects\AQAD\controllers\userController.js

Think of it this way:

Directory = Building

Filename = Exact Room


Command-Line Arguments

Node.js allows passing values while starting the application.

Example:

node app.js hello

Access:

console.log(process.argv);

Output:

[
'node',
'app.js',
'hello'
]

AQAD Example

Suppose you want to run different scripts.

node app.js development
const mode = process.argv[2];

console.log(mode);

Output:

development

Useful for automation scripts.


Monitoring Memory Usage

Node.js provides:

process.memoryUsage();

Example:

console.log(process.memoryUsage());

Output:

{
heapUsed: 4500000,
heapTotal: 9000000
}

Why This Matters

Imagine AQAD suddenly becomes slow.

Thousands of retailers are placing orders.

You can monitor memory consumption:

console.log(
process.memoryUsage().heapUsed
);

This helps detect memory leaks.


Real Backend Example

Suppose AQAD API starts.

console.log("Server Starting...");
console.log("Directory:", __dirname);
console.log("Environment:", process.env.NODE_ENV);
console.log("Process ID:", process.pid);

Output:

Server Starting...
Directory: C:\AQAD
Environment: production
Process ID: 3412

This information is commonly logged in production systems.


Common Mistakes

Mistake 1

Hardcoding Secrets

Bad:

const jwtSecret = "123456";

Good:

const jwtSecret =
process.env.JWT_SECRET;

Mistake 2

Using Too Many Global Variables

Bad:

global.userData = {};

Avoid unnecessary global state.


Mistake 3

Committing .env Files

Bad:

git add .env
git commit

Always add:

.env

to:

.gitignore

Mistake 4

Using Relative Paths Incorrectly

Bad:

"./uploads/image.png"

Good:

__dirname + "/uploads/image.png"

Mini Exercises

Exercise 1

Print:

  • Node version
  • Platform
  • Process ID

Exercise 2

Create:

APP_NAME=AQAD

Print it using:

process.env.APP_NAME

Exercise 3

Print:

__dirname

and

__filename

Observe the difference.


Exercise 4

Pass your name using command line:

node app.js Aqad

Print:

Hello Aqad

using:

process.argv

Try It Yourself

Create a file:

console.log("PID:", process.pid);

console.log("Node Version:",
process.version);

console.log("Directory:",
__dirname);

console.log("File:",
__filename);

console.log("Arguments:",
process.argv);

Run:

node app.js test

Analyze the output.

Try changing arguments and observe the results.


 Understanding the Node.js Modules System


Introduction: Why One Huge File Becomes a Disaster

Imagine AQAD has become one of the largest B2B marketplaces in the UAE.

The platform now handles:

  • 50,000+ retailers
  • 10,000+ vendors
  • Millions of products
  • Thousands of daily orders

Now imagine the entire business running from a single room.

Inside that room:

  • Customer support team
  • Finance team
  • Logistics team
  • Vendor management team
  • Marketing team
  • HR team

Everyone is working in the same place.

Files are mixed.

Documents are scattered.

People interrupt each other.

Nobody knows where anything is.

Very quickly, the company becomes impossible to manage.

The same thing happens in software.

When beginners start learning Node.js, they often write everything inside one file:

// app.js

// Login Code

// Product Code

// Order Code

// Payment Code

// Delivery Code

// Inventory Code

// Notification Code

Initially this looks fine.

But after a few months:

  • 5,000 lines
  • 10,000 lines
  • 20,000 lines

Now the file becomes a nightmare.

Finding bugs becomes difficult.

Adding features becomes risky.

Team collaboration becomes painful.

This is exactly why the Module System exists.

Modules allow us to divide large applications into smaller manageable pieces.

Think of modules as departments inside a company.

Each department has a specific responsibility.

Together they make the entire organization work efficiently.


What Is a Module?

A module is simply a separate file that contains related code.

For example:

AQAD

├── products.js
├── orders.js
├── payments.js
├── users.js
└── app.js

Instead of putting everything into one file, we divide responsibilities.


Real-Life Analogy

Consider a restaurant.

Would one employee handle:

  • Taking orders
  • Cooking food
  • Managing inventory
  • Handling payments
  • Delivering food

No.

Different people perform different jobs.

Similarly:

User Module
Product Module
Order Module
Payment Module

Each module focuses on one responsibility.


Why Modules Are Important

Without modules:

// 15,000 lines in one file

With modules:

user.js
product.js
order.js
payment.js
inventory.js

Benefits:

Better Organization

Code is easier to understand.

Easier Maintenance

Finding bugs becomes faster.

Reusability

One module can be reused in multiple places.

Team Collaboration

Multiple developers can work simultaneously.

Scalability

Applications can grow without becoming messy.


Your First Module

Let's create a simple module.


Step 1: Create math.js

function add(a, b) {
return a + b;
}

Now the function exists only inside this file.

Other files cannot access it.


Exporting a Module

To share code, we must export it.

function add(a, b) {
return a + b;
}

module.exports = add;

Now the function becomes available outside the file.


Importing a Module

Inside app.js:

const add = require('./math');

console.log(add(10, 20));

Output:

30

Congratulations.

You have created your first Node.js module.


Understanding module.exports

This is one of the most important concepts in Node.js.

Think of a department manager.

The department does not expose everything.

Only selected resources are shared.

Similarly:

module.exports

controls what becomes accessible outside the file.


Example

const companyName = "AQAD";

function calculateRevenue() {}

function calculateProfit() {}

module.exports = {
calculateRevenue,
calculateProfit
};

Accessible:

calculateRevenue()
calculateProfit()

Not accessible:

companyName

unless exported.


Exporting Multiple Functions

Most real applications export multiple functions.

Example:

function add(a, b) {
return a + b;
}

function subtract(a, b) {
return a - b;
}

module.exports = {
add,
subtract
};

Importing

const math = require('./math');

console.log(math.add(10, 5));

console.log(math.subtract(10, 5));

Output:

15

5

Destructuring Imports

A cleaner approach:

const {
add,
subtract
} = require('./math');

Now:

console.log(add(10, 5));

This style is common in production applications.


AQAD Example: Product Module

Imagine AQAD's product functionality.


product.js

function getProducts() {
return "Products List";
}

function addProduct() {
return "Product Added";
}

module.exports = {
getProducts,
addProduct
};

app.js

const {
getProducts,
addProduct
} = require('./product');

console.log(getProducts());

console.log(addProduct());

Output:

Products List

Product Added

How require() Works Internally

When Node.js encounters:

require('./product')

it performs several steps.


Step 1

Locate file.

product.js

Step 2

Read file content.


Step 3

Execute file.


Step 4

Store result in memory.


Step 5

Return exported value.


Think of it as:

Restaurant receives ingredient request.

  1. Find ingredient
  2. Fetch ingredient
  3. Prepare ingredient
  4. Store if needed
  5. Deliver ingredient

Module Caching

One of Node.js's smartest features.


Example

product.js

console.log("Module Loaded");

module.exports = {};

app.js

require('./product');

require('./product');

require('./product');

Output:

Module Loaded

Only once.


Why?

Node.js caches modules.

After first load:

Memory Cache

Future requests use cached version.


Benefits

Faster Performance

No repeated file reading.

Reduced CPU Usage

Less work for Node.js.

Better Scalability

Important for large applications.


Real AQAD Example

Suppose:

database.js

creates DynamoDB connection.

Without caching:

Every file would reconnect.

Bad.

With caching:

Single connection reused.

Much better.


Understanding require Path Types

Node.js supports different paths.


Relative Path

require('./product')

Current folder.


Parent Folder

require('../product')

One level above.


External Package

require('express')

Node.js searches inside:

node_modules

AQAD Folder Structure Example

aqad

├── app.js

├── controllers
│ ├── userController.js
│ └── orderController.js

├── services
│ ├── paymentService.js
│ └── emailService.js

├── models
│ ├── userModel.js
│ └── orderModel.js

Import example:

const userController =
require('./controllers/userController');

Creating Utility Modules

Utilities are reusable helper functions.


utils.js

function generateOrderId() {
return Math.random();
}

module.exports = {
generateOrderId
};

app.js

const {
generateOrderId
} = require('./utils');

console.log(generateOrderId());

Built-In Modules

Not all modules are created by you.

Node.js includes many built-in modules.

Examples:

ModulePurpose
fsFile operations
pathPath management
osSystem information
httpWeb server
eventsEvent handling

Example

const fs = require('fs');

Notice:

No path.

Why?

Because fs is built into Node.js.


Third-Party Modules

These come from NPM.

Example:

npm install express

Then:

const express =
require('express');

Express becomes available because it exists in:

node_modules

Types of Modules in Node.js

There are three major types.


1. Local Modules

Created by you.

Example:

require('./product')

2. Core Modules

Built into Node.js.

Example:

require('fs')

3. Third-Party Modules

Installed from NPM.

Example:

require('express')

Common Beginner Mistakes

Mistake 1

Forgetting Exports

Bad:

function add() {}

Trying:

require('./math')

Result:

undefined

Mistake 2

Wrong Path

Bad:

require('product')

Correct:

require('./product')

Mistake 3

Exporting Before Declaration

Bad:

module.exports = add;

const add = () => {};

Can cause issues.


Mistake 4

Creating Huge Modules

Bad:

user.js
(5000 lines)

Better:

userController.js
userService.js
userValidation.js
userRoutes.js

Mini Exercises

Exercise 1

Create:

calculator.js

Export:

add()
multiply()

Import them into:

app.js

Exercise 2

Create:

user.js

Export:

getUser()
createUser()

Call them from:

app.js

Exercise 3

Verify module caching.

Add:

console.log("Loaded");

inside module.

Require it three times.

Observe output.


Try It Yourself

Create:

project

├── app.js
├── products.js
├── orders.js

products.js

module.exports = {
getProducts() {
return "All Products";
}
};

orders.js

module.exports = {
getOrders() {
return "All Orders";
}
};

app.js

const products =
require('./products');

const orders =
require('./orders');

console.log(products.getProducts());

console.log(orders.getOrders());

Run:

node app.js

Observe how separate modules work together.


Real-World AQAD Project Structure

As AQAD grows, modules become critical.

Instead of:

app.js
(25,000 lines)

We organize:

controllers/
services/
models/
routes/
middlewares/
utils/
config/

Each folder becomes a department.

Each file becomes a specialist employee.

This architecture makes large applications manageable.


Post a Comment

0 Comments