Backend Fundamentals: 01

1. How the Server Works (Request-Response Cycle)?

A server works on the request-response model, where a client sends a request and the server processes it and sends back a response.

When a client sends a request, the server receives it through a listening port. The request passes through middleware, reaches a route handler, executes business logic (possibly interacting with a database or external services), and then sends a response back to the client.

Node.js handles request asynchronously using the event loop, process them through middleware & controllers and returns a response without blocking the other requests.

In short, a server listens for requests, processes them using middleware and business logic, interacts with the database if needed, and returns a structured response to the client.

2. REST API Design Principles

REST API design principles are guidelines used to build scalable, maintainable, and predictable APIs. REST stands for Representational State Transfer and is based on stateless client-server communication.

The key REST principles are:

The client and server are separated. The client handles UI, and the server handles business logic and data.

Each request must contain all required information. The server does not store client state between requests.

APIs are designed around resources, not actions. For example:

/users instead of /getUsers

  • GET → fetch data
  • POST → create data
  • PUT/PATCH → update data
  • DELETE → remove data

Use meaningful status codes like:

  • 200: OK
  • 201: Created
  • 204: No Content
  • 400: Bad Request
  • 401: Unauthorized
  • 403: Forbidden
  • 404: Not Found
  • 409: Too Many Requests
  • 500: Server Error

Responses are usually in JSON, with a consistent structure.

{
  "success": true,
  "message": "Fetched successfully",
  "data": {...},
  "statusCode": 200
}
{
  "success": false,
  "message": "Internal Server Error",
  "errors": [{...}],
  "statusCode": 500
}

APIs should be versioned to avoid breaking changes, for example:

api/v1/users, api/v2/users.

For detail information: Rest API Basic

REST API design principles focus on stateless communication, resource-based URLs, proper use of HTTP methods and status codes, and consistent JSON responses to build scalable and maintainable APIs.

3. HTTP Methods & Status Codes

HTTP methods define what action the client wants to perform on a resource, while HTTP status codes indicate the result of that action.

I. GET: Fetch data from the server

  • Read-only, no side effects
  • Example: GET /users

II. POST: Create new data

  • Sends data in request body
  • Example: POST /users

III. PUT: Update an entire resource

  • Replaces existing data
  • Example: PUT /users/1

IV. PATCH: Update part of a resource

  • Partial update
  • Example: PATCH /users/1

V. DELETE: Remove a resource

  • Example: DELETE /users/1
  • 200 OK - Successful request
  • 201 Created - Resource created
  • 204 No Content - Success with no response body
  • 400 Bad Request - Client error (invalid input)
  • 401 Unauthorized - Not authenticated
  • 403 Forbidden - Authenticated but not allowed
  • 404 Not Found - Resource doesn't exist
  • 409 Conflict - Resource state conflict
  • 500 Internal Server Error - Server-side failure
export const STATUS_CODES = {
  // 2xx Success
  OK: 200,
  CREATED: 201,
  ACCEPTED: 202,
  NO_CONTENT: 204,
 
  // 3xx Redirection
  MOVED_PERMANENTLY: 301,
  FOUND: 302,
  NOT_MODIFIED: 304,
 
  // 4xx Client Errors
  BAD_REQUEST: 400,
  UNAUTHORIZED: 401,
  FORBIDDEN: 403,
  NOT_FOUND: 404,
  CONFLICT: 409,
  UNPROCESSABLE_ENTITY: 422,
  TOO_MANY_REQUESTS: 429,
 
  // 5xx Server Errors
  INTERNAL_SERVER_ERROR: 500,
  NOT_IMPLEMENTED: 501,
  BAD_GATEWAY: 502,
  SERVICE_UNAVAILABLE: 503,
  GATEWAY_TIMEOUT: 504
} as const;
 
export type StatusCode = (typeof STATUS_CODES)[keyof typeof STATUS_CODES];

HTTP methods define the action like GET, POST, PUT, PATCH, and DELETE, while status codes like 200, 201, 400, 401, and 500 indicate the result of the request.

4. Middleware & Request Flow

Middleware in Node.js is a function that sits between the incoming request and the final response. It has access to the request object, response object, and the next() function.

Request Flow in Node.js (Express):

I. A client sends an HTTP request to the server.

II. The request first passes through middleware functions such as:

  • Logging
  • Authentication
  • Validation
  • Parsing JSON

III. Each middleware can:

  • Modify req or res
  • End the request-response cycle
  • Or pass control to the next middleware using next()

IV. Once all middleware runs, the request reaches the route handler or controller, where business logic is executed.

V. Finally, the server sends a response back to the client.

Middleware is a function that runs before the request reaches the controller. It can modify the request, validate data, handle auth, or pass control using next().

5. Authentication vs Authorization

Authentication and authorization are both security concepts, but they serve different purposes. Authentication always comes first. Authorization depends on authenticated identity.

Authentication verifies who the user is (login, tokens, OAuth).

Examples:

  • Login using email and password
  • JWT or session-based login
  • OAuth login (Google, GitHub)

Authentication answers: “Who are you?”

Authorization determines what the user can access (roles, permissions).

Examples:

  • Access control (admin vs user)
  • Permission-based routes
  • Role-based access control (RBAC)

Authorization answers: “What are you allowed to do?”

First, authentication middleware verifies the user (for example, by validating a JWT). Then, authorization middleware checks roles or permissions before allowing access to a route.

6. Event Loop, libuv, V8 & Non-Blocking I/O

Node.js works on a single-threaded, non-blocking, event-driven architecture, which is powered by the Event Loop, libuv, and the V8 engine.

  • V8 is Google's JavaScript engine.
  • It compiles JavaScript directly into machine code
  • Executes JS code very fast
  • Manages memory using garbage collection

V8 executes JavaScript code.

libuv is a C library that provides:

  • The event loop
  • Thread pool
  • Asynchronous I/O operations

It handles:

  • File system operations
  • Network requests
  • Timers

Libuv handles async tasks and system-level operations.

The event loop is part of libuv.

  • It continuously checks:

    • Call stack
    • Callback queues
  • Executes callbacks when the stack is empty

Event loop phases include:

  • Timers
  • I/O callbacks
  • Poll
  • Check
  • Close callbacks

The event loop decides when a callback should run.

In Node.js:

  • I/O operations do not block the main thread
  • Long tasks (DB, file, network) are offloaded to libuv
  • Node.js continues handling other requests

This allows Node.js to handle thousands of concurrent requests efficiently.

V8 executes JavaScript, libuv handles async operations, the event loop manages execution order, and non-blocking I/O allows Node.js to process multiple requests without blocking the main thread.

7. Async/Await & Promises

Promises and async/await are used to handle asynchronous operations in JavaScript and Node.js.

A Promise represents a value that will be resolved or rejected in the future. It has three states:

  • Pending
  • Fulfilled
  • Rejected

Promises help avoid callback hell and allow chaining using .then() and .catch().

Async/await is syntactic sugar built on top of Promises that makes asynchronous code look synchronous and easier to read.

  • async functions always return a Promise

  • await pauses execution until the Promise resolves or rejects

Errors are handled using try-catch

// Promise
fetchData()
  .then(data => console.log(data))
  .catch(err => console.error(err));
 
// Async / Await
async function getData() {
  try {
    const data = await fetchData();
    console.log(data);
  } catch (err) {
    console.error(err);
  }
}

Promises handle async operations using then and catch, while async/await is a cleaner syntax on top of Promises that uses try-catch for error handling.

8. Error Handling & Global Error Middleware

Error handling in Node.js ensures that application failures are handled gracefully without crashing the server. In Express, this is commonly done using centralized or global error-handling middleware.

  • Errors can occur in routes, controllers, middleware, or async operations.

  • Instead of handling errors everywhere, we use a global error middleware.

  • This keeps the code clean, consistent, and easier to maintain.

A global error middleware is a special Express middleware that catches all errors and sends a standardized response. Global error handling keeps controllers clean and behavior predictable.

It has four parameters:

(err, req, res, next)

Express automatically recognizes it as an error middleware because of these four parameters.

Example

app.get("/user", async (req, res, next) => {
  try {
    throw new Error("User not found");
  } catch (error) {
    next(error);
  }
});
 
// Global Error Middleware
app.use((err, req, res, next) => {
  res.status(500).json({
    success: false,
    message: err.message
  });
});

For more info visit Global Error Handling

Global error middleware in Express is used to catch and handle all application errors in one place using the (err, req, res, next) function.

9. Env Variable & Config Management

Environment variables are used to store configuration values outside the codebase, such as database URLs, API keys, and secrets.

Config management ensures that applications behave differently across development, staging, and production without changing the code.

In Node.js, environment variables are accessed using process.env.

Why We Use Environment Variables

  • To keep secrets out of source code
  • To avoid hardcoding values
  • To support different environments (dev, prod, test)
  • To improve security and scalabilit

Common Examples

  • PORT
  • DATABASE_URL
  • JWT_SECRET
  • NODE_ENV

For more info visit Env Config

Config Management Best Practice

We separate configuration from business logic by using a centralized config file.

src/configs/env.ts
const env = {
  PORT: Number(process.env.PORT) || 3000,
  NODE_ENV: process.env.NODE_ENV || "development",
  LOG_LEVEL: process.env.LOG_LEVEL || "info",
  CORS_ORIGIN: process.env.CORS_ORIGIN || "*",
  DATABASE_URL: process.env.DATABASE_URL
};
 
export default env;

Environment variables store configuration and secrets outside the code, allowing different behavior per environment and improving security.

10. Logging & Monitoring

Logging and monitoring are used to observe application behavior, debug issues, and ensure system reliability in production.

Logging is the process of recording application events such as:

  • Errors
  • Warnings
  • API requests
  • Important system actions

In Node.js, logs help us debug issues, trace requests, and audit behavior.

We use different log levels to control log importance.

  • info - normal application flow
  • warn - unexpected but non-critical issues
  • error - failures and crashes
  • debug - detailed development log

Instead of console.log, we use logging libraries like Winston or Pino for structured and persistent logs.

logger.info("Server started");
logger.error("Database connection failed");

Monitoring tracks the health and performance of the application in real time.

It includes:

  • CPU and memory usage
  • Response time
  • Error rates
  • Uptime and availability
  • Prometheus
  • Grafana
  • New Relic
  • Datado

Logging records application events, while monitoring tracks performance and system health to ensure reliability in production.

Akkal

Dhami