> For the complete documentation index, see [llms.txt](https://naz.gitbook.io/my-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://naz.gitbook.io/my-docs/backend-learning/controllers_services_repositories_middlewares_and_request_context.md).

# Lesson 6: Controllers, Services, Repositories, Middlewares and Request Context

## The Request Lifecycle Inside the Server

Client and server communicate over HTTP. From the moment a request reaches the server until the moment a response is received, a lot happens inside the server. That is the request lifecycle inside the server.

The **entry point** is the moment the operating system forwards the HTTP request to the port the server listens on. The port can be 3000, 4000, or any port the server runs on. The server listens on that port, and that is how it gets the request.

After the entry point comes **routing**. The routing algorithm maps the request to a particular **handler**: a predefined function supposed to handle the requests for that API. Routes can be static (`/users`), dynamic (`/users/123`), or any other pattern.

```mermaid
flowchart TB
    REQ["Client request"] --> ENTRY["Entry point<br/>OS forwards request<br/>to the listening port"]
    ENTRY --> ROUTE["Routing<br/>match route to handler"]
    ROUTE --> HANDLER["Handler / Controller"]
    HANDLER --> SERVICE["Service layer"]
    SERVICE --> REPO["Repository layer"]
    REPO --> DB[("Database")]
```

***

## Handlers, Services, and Repositories

There is no hard requirement to split responsibilities into handler, service, and repository. A single handler could do everything. The split is a design pattern with concrete payoffs: a scalable codebase, a maintainable codebase, easier feature additions, easier debugging.

### The Request and Response Objects

Every handler receives at least two objects in its parameters, provided by the runtime:

* **Request object** -- the handler takes values out of the HTTP request from it
* **Response object** -- the handler sends responses and modifies response headers with it

In Go they arrive on the handler parameters; in Node.js with Express they arrive as `req` and `res`. The handler never adds them itself.

### Handler Responsibilities

A handler does three things, in order:

1. **Extract and bind data.** Take the data out of the request object, then deserialize the JSON into the language's native format. This step is also called **binding**.

   * GET request: take out the query params
   * POST, PUT, PATCH: take out the request body (DELETE can also carry a body)
   * Native formats: a struct in Go, a dictionary or class in Python, a struct in Rust
   * Node.js usually skips this in the handler: upstream middleware (a JSON body parser) already deserializes into JavaScript objects

   ```go
   var book Book
   if err := json.NewDecoder(r.Body).Decode(&book); err != nil {
       http.Error(w, "bad request", http.StatusBadRequest)
       return
   }
   ```

   If deserialization fails, the handler sends **400 Bad Request** and terminates the request there. It does not proceed.
2. **Validate and transform.** Validate everything coming from the client (path parameters, request body, query parameters) as specifically as possible, then optionally transform. Example: a `GET /books` API accepts an optional `sort` query parameter with values `name` or `date`. If the client sends nothing, validation passes and the transformation step sets a default, so downstream layers always see an expected value.

   ```
   GET /books?sort=name HTTP/1.1
   Host: localhost
   ```
3. **Call the service and respond.** The controller calls the service layer with the validated, transformed data plus anything the service needs, such as authentication info (user ID, permissions). Then it sends the response:
   * **200**, **201**: success
   * **204 No Content**: success with an empty body (the RESTful standard for DELETE)
   * **400**: client failure
   * **500**: server failure

```mermaid
flowchart TB
    H1["Handler entry"] --> BIND["1. Extract and bind<br/>deserialize JSON to native format"]
    BIND -->|fails| R400["400 Bad Request<br/>terminate request"]
    BIND -->|ok| VT["2. Validate and transform"]
    VT -->|fails| R400
    VT -->|ok| CALL["3. Call service layer<br/>with data and auth info"]
    CALL --> RESP["4. Send response<br/>200 / 201 / 204<br/>or 400 / 500"]
```

### Services: Processing Without HTTP

The service layer never deals with HTTP concerns. All of that stays in the controller. A service method is just a function: from one look, it should be impossible to tell it is used in an API. It takes data, processes it, and returns data, without worrying about response codes or validation.

The service calls repository methods for database operations when needed, but not always. A service that only sends an email takes the address, executes the send, and returns something like `{ "success": true }` to the handler without touching the repository.

Beyond single operations, the service **orchestrates**: it calls multiple repository methods, merges the different results, picks the necessary fields, and returns the combined data to the handler.

The division of labor:

* **Handlers**: input and output data formats
* **Services**: the actual processing
* **Repositories**: all database calls

### Repositories: One Method, One Job

The repository (or database) layer takes the data to insert, or the filtering and sorting data to fetch with, constructs the database query, and returns whatever the database returned.

One repository method returns one kind of data:

* `getAllBooks`: returns all books
* `getBookById`: takes an ID, returns a single book

A method must not take an optional parameter that changes what it returns. No single function that returns either one book or all books depending on a flag. Each repository method does one thing: fetch, insert, or delete per the requirement.

***

## Middleware

### What Middleware Receives

Replay the request lifecycle with boundaries: entry point, middleware, routing, middleware, handler, service, repository, middleware, response. The functions sitting at these boundaries are **middleware**: functions executed in the middle of routing, handlers, services, and the response.

Like handlers, middleware gets the request and response objects from the runtime, plus a third value:

* **Request object** -- take values out of the request, or modify them
* **Response object** -- send a response back to the client from this point, without the request ever reaching the router or the handlers
* **`next`** -- a function that passes execution to the next processing context (the next middleware, the router, or the handler)

```js
function authMiddleware(req, res, next) {
  // verify the token ...
  next(); // pass execution onward
}
```

### Why It Exists and Why Order Matters

Middleware exists for the same reason functions exist: to avoid writing the same lines in every handler. A backend serves thousands to billions of requests per day across hundreds or thousands of endpoints, each with its own handler. Without middleware, every handler would duplicate the same security, logging, authentication, and data-passing code.

Order matters. Starting at the entry point, each middleware calls `next()` to pass execution along until a response is sent. A typical order:

1. **CORS**: terminate disallowed origins at the earliest stage
2. **Logging**: record the request
3. **Authentication**: verify credentials
4. **Global error handling**: last, because an error can occur at any stage and only the last middleware can catch errors from every stage

The request flows one way. An error middleware placed in the middle could never see errors raised by later stages.

### The Standard Middleware Stack

* **CORS middleware** -- browsers enforce the same-origin policy: an app on `example.com` can only access resources from `example.com`. The server checks the request's `Origin` against the allowed frontend origin. On a match it adds the appropriate response headers and calls `next()`; on a mismatch it adds no headers and the browser blocks the response by default.
* **Security headers middleware** -- sets headers such as Content-Security-Policy on every response, then forwards to the next middleware.
* **Authentication middleware** -- takes the token (JWT, session ID) from the headers or payload and verifies the credentials. On failure it sends **401 Unauthorized** immediately, terminating the request before it reaches the handlers. On success it extracts the user ID, role, and permissions, stores them in the request context, and calls `next()`.
* **Rate limiting middleware** -- prevents one client from making too many requests in a short period. Thresholds are decided up front, for example more than 30 API calls in 2 seconds from one IP address. Exceeding the limit returns **429 Too Many Requests**; otherwise execution passes on.
* **Logging middleware** -- logs per-request details (path, method, query parameters, request body) to the terminal or a log file for debugging, reports, and auditing.
* **Global error handling middleware** -- catches unstructured errors from anywhere (handler layer, service layer, any middleware), determines client error vs server error, and sends a structured message (message plus code/ID plus status): **400** or **500**.
* **Compression and data-passing middleware** -- compresses large responses (for example with gzip) for transit; serialization/deserialization and validation/transformation can also be delegated to middleware depending on the codebase's standards.

```mermaid
flowchart LR
    E["Entry point"] --> CORS["CORS"]
    CORS --> LOG["Logging"]
    LOG --> ROUTE["Routing"]
    ROUTE --> AUTH["Authentication"]
    AUTH --> H["Handler"]
    H --> SVC["Service"]
    SVC --> REPO["Repository"]
    REPO --> ERR["Global error handling"]
    ERR --> RESP["Response to client"]
```

***

## Request Context

### Shared State Scoped to One Request

The authentication middleware verified the credentials and stored the user ID and role in some kind of storage for the next handler. That storage is the **request context**. Every language and framework provides an implementation (details vary, the concept stays the same):

* A key-value store **scoped to one request**
* Each request gets its own context
* Accessible across all middleware and handler boundaries
* Lets values flow downstream without coupling the components together

### What Lives in the Context

* **Auth information** -- after successful authentication, the middleware stores the user ID, the role (user, admin, sales), and permissions (read, write). Downstream, a `POST /books` handler reads the user ID from the context, not from the client payload, and inserts it into the book row. Taking the user ID from the client would let a malicious client send someone else's ID to perform unexpected operations. Permission checks (whether the user may insert a book at all) also read from this context.
* **Request IDs** -- an upstream middleware generates a UUID and stores it in the context. Every stage uses it: logging it, or forwarding it as an `x-request-id` header to downstream microservices. When auditing or debugging logs, the ID traces where the request started and which services it called.
* **Cancellation signals and deadlines** -- carried to downstream services so a service never hangs perpetually.

```mermaid
flowchart LR
    A2["Auth middleware"] -->|sets userId, role| CTX[("Request context<br/>key-value store<br/>scoped to one request")]
    R2["Request-id middleware"] -->|sets uuid| CTX
    CTX -->|read| H2["Handler and<br/>downstream middleware"]
```

***

## Review

* There is no hard requirement for the handler/service/repository split. It is a design pattern for scalable, maintainable, debuggable code.
* Handlers receive request and response objects from the runtime. Their responsibilities: extract and bind data, validate and transform it, call the service layer, send the response.
* Binding deserializes JSON into the native format (struct in Go, dict or class in Python). Failure means 400 and termination.
* Services contain the actual processing, never HTTP concerns, and orchestrate repository calls.
* Repositories have a single responsibility: build the database query from the given data and return the result. One method returns one kind of data.
* Middleware are optional functions at the lifecycle boundaries receiving request, response, and `next()`. They remove duplication for cross-cutting concerns, and their order matters.
* Typical middleware in order: CORS, logging, authentication, global error handling last. Also rate limiting (429), security headers, compression, data passing.
* The request context is per-request key-value state shared across middleware and handlers: auth info (user ID, role), request IDs for tracing, cancellation signals and deadlines.

***

## Resource

* [10. What are controllers, services, repositories, middlewares and request context? -- Sriniously](https://youtu.be/hyc-7w3pee8)
