1.5. Request-Response Cycle
Table of Contents
Overview
When you use a website or an API, your browser or app sends a request to a server and then waits for a response. This back‑and‑forth is called the request‑response cycle. Understanding this cycle is one of the most important foundations for backend development, because everything your backend code does is triggered by a request and ends with a response.
In this chapter you will see what happens in that cycle, step by step, with concrete examples that will help you relate it to your own browsing experience and to backend code you will write later.
The Basic Flow
At its simplest, the request‑response cycle looks like this:
- A client wants something.
- The client sends an HTTP request to a server.
- The server receives the request, processes it, and may talk to databases or other services.
- The server prepares an HTTP response.
- The server sends the response back to the client.
- The client receives the response and uses it, for example by rendering a page or updating the screen.
You will learn the detailed networking pieces like TCP, ports, and HTTP structure in later chapters. Here we stay at the level that matters to a backend developer who is designing and implementing server logic.
Who Is the Client, Who Is the Server?
The client is usually a web browser, a mobile app, or another backend service that wants data or an action to be performed.
The server is your backend application that listens for requests and responds to them.
In a simple example:
- You open
https://example.com/profilein your browser. - Your browser is the client.
- The backend that runs
example.comis the server.
From a backend developer’s point of view, the client is anything that speaks HTTP to your server. It does not matter whether it is a browser, an Android app, or another API. You handle all of them through the same request‑response pattern.
A Simple Real‑World Example
Imagine you visit a blog at:
https://myblog.com/posts/42
You want to read the blog post with ID 42. This is what happens in the request‑response cycle in simplified form.
- You type the URL and press Enter.
- Your browser prepares an HTTP GET request to
/posts/42on the hostmyblog.com. - The request travels across the network to the server that hosts
myblog.com. - The backend application running there receives the request and looks at:
- the path
/posts/42 - the method
GET - maybe some cookies or headers for authentication
- The backend code sees that this URL matches a “get post by id” route, for example
GET /posts/{id}. - The code queries the database: “Give me the post with ID 42.”
- The database returns the stored data for that post.
- The backend renders an HTML page that contains the title and content of the post.
- The backend sends an HTTP response with:
- a status code, for example
200 OK - headers describing the content type
- the HTML page as the body of the response
- Your browser receives the response, parses the HTML, and shows the blog post on your screen.
This whole story is one request‑response cycle. You clicked one URL; the server handled one request and returned one response.
If you then click “Next post,” that is a new request‑response cycle. Every time the browser or app “talks” to the server, a new cycle starts.
Structure of a Request in the Cycle
Each request in the cycle contains several important parts that your backend code can use:
- The HTTP method, such as GET or POST.
- The URL path and query, such as
/search?q=fastapi. - Headers, such as
Content-TypeorAuthorization. - Sometimes a body, for example form data or JSON in a POST request.
Later chapters will go deep into each of those pieces, but here it is important to connect them to the cycle: the server receives the request as a structured object that your code can inspect.
For example, when a client sends this request:
GET /users/5 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer <token>your backend framework will present it to your code as something like:
- method:
"GET" - path:
"/users/5" - headers: a dictionary with keys like
"Accept"and"Authorization"
Your route handler will then decide what to do based on this information.
Structure of a Response in the Cycle
The response has three main parts:
- A status line with a status code, such as
200 OKor404 Not Found. - Headers, such as
Content-TypeorSet-Cookie. - A body, which might be HTML, JSON, an image, or no body at all.
For example, the server might respond to GET /users/5 with:
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 5,
"name": "Alice",
"email": "alice@example.com"
}Your backend code is responsible for choosing the correct status code, setting the right headers, and generating the body that the client expects.
A backend application must always return a valid HTTP response for every request it accepts. At a minimum this means:
- a status code,
- optional headers,
- and optionally a response body.
Step‑by‑Step: Login Request‑Response Cycle
Consider a typical login form. You open https://example.com/login. That first page load is already one request‑response cycle:
- Browser sends
GET /login. - Server responds with an HTML login page that includes a form with username and password fields.
Now you fill in the form and click “Login.” That triggers another cycle.
- The browser reads the form and sends something like:
POST /login HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
username=alice&password=secret123- The server receives the POST request and passes it to the part of the code that handles
/loginwith the POST method. - The backend reads the form data from the request body.
- The backend checks the credentials against the database.
- If the credentials are correct:
- it might create a session record in the database or memory,
- it might generate a session ID,
- it returns a redirect to
/dashboardand sets a session cookie. - If the credentials are wrong:
- it returns a response that contains the login page again
- plus an error message like “Invalid username or password.”
From the browser’s viewpoint, every login attempt is one independent request‑response cycle. From your backend’s viewpoint, each attempt is one independent call to your login handler.
From URL to Backend Code
Most backend frameworks, including those you will use later, follow a pattern like this in each request‑response cycle:
- A low‑level web server (for example Uvicorn or Gunicorn) listens for incoming HTTP requests.
- When a request arrives, the server parses it into a request object and hands it to your framework (for example FastAPI).
- The framework matches the request to a route, for example:
GET /posts→ functionlist_postsPOST /posts→ functioncreate_postGET /posts/{id}→ functionget_post- It then calls the appropriate function, passing the request data as parameters.
- Your function runs your business logic. It might:
- validate input,
- query or update the database,
- talk to other APIs,
- prepare the data for the client.
- Your function returns some result object, usually including:
- a status code,
- a body (string, dictionary, etc.),
- maybe headers.
- The framework converts that result into a proper HTTP response.
- The web server sends that HTTP response back over the network to the client.
As a backend developer you mainly write the code in step 5 and define the mapping in step 3. The rest is handled by the framework and the web server, but all of it exists to support the request‑response cycle.
Multiple Requests, Not a Continuous Conversation
It may feel like you have a continuous conversation with a website, but the web is built around many separate request‑response cycles. Each page load, button click, or API call is a new request that does not automatically “remember” previous ones.
To maintain continuity, the backend uses tools like cookies, sessions, and tokens, which you will learn about later. However, each HTTP request itself is independent.
For example, in an online store:
GET /productsretrieves the list of products.POST /cartadds an item to your cart.GET /cartshows what is currently in your cart.POST /checkoutstarts the payment process.
From the user’s point of view this is one flow: “I shopped and paid.” From the backend’s point of view this is a sequence of request‑response cycles that share state through identifiers like session IDs or user IDs.
Synchronous vs Asynchronous Feeling
The request‑response cycle is usually synchronous from the client’s perspective: the client sends a request and waits for the response. During this time the client might show a loading spinner.
However, modern applications sometimes want to feel more “live.” They still use the request‑response cycle, but often in smaller pieces:
- A single page loads once, then uses JavaScript to send background requests (AJAX or Fetch) to update parts of the page without a full reload.
- A mobile app sends many small requests in the background, for example to load new messages or notifications.
Each of those background calls is still a complete HTTP request‑response cycle. There is just no full page reload, and the browser or app decides how to use the response.
Errors in the Request‑Response Cycle
Not every request ends with a successful response. Sometimes the server cannot fulfill the request, or the client sent something invalid.
Typical error cases include:
- The client asks for a resource that does not exist. The server might respond with
404 Not Found. - The client does not include necessary authentication information. The server might respond with
401 Unauthorizedor403 Forbidden. - The server encounters an unexpected problem, for example a bug or a database outage. The server responds with
500 Internal Server Error.
From the viewpoint of the cycle, this is still a normal response. The server received a request and replied with a valid HTTP response, just with an error code and possibly an error message in the body.
From the client’s point of view, an HTTP error such as 404 or 500 is still a successful completion of the request‑response cycle. The request reached the server, and the server responded. The error is part of the response, not a failure to complete the cycle.
Idempotent and Non‑Idempotent Cycles
This idea is important when you think about what happens if a request is sent twice by mistake, for example because of a network retry.
Some requests are safe to repeat:
GET /posts/42just fetches data. It is usually fine if it runs twice; the state on the server does not change.
Other requests change state:
POST /ordersmight create a new order. If the same request is processed twice, you might get two orders instead of one.
The request‑response cycle is always “one request, one response,” but as a backend developer you must keep in mind what happens if a client repeats the cycle by sending the same request again. This is closely tied to how you use HTTP methods, which you will cover in detail later.
How the Cycle Looks in Code (Preview)
In a Python backend using a modern framework, a route handler function usually corresponds to one request‑response cycle. When a request matches that route, the framework calls your function once, and whatever you return becomes the response.
Here is a very simplified example in a FastAPI style:
from fastapi import FastAPI
app = FastAPI()
@app.get("/hello")
def say_hello():
return {"message": "Hello, world!"}
For each request that a client sends to GET /hello, the following happens:
- FastAPI receives the HTTP request from the web server.
- It recognizes that the path
/hellowith method GET should callsay_hello. - It calls
say_hello()once, for this one request. - The function returns a Python dictionary.
- FastAPI converts that dictionary into a JSON response with status code 200.
- The web server sends the response back to the client.
If 100 users hit /hello at nearly the same time, say_hello will be called around 100 separate times, once per request‑response cycle.
Thinking Like a Backend Developer
When you design and write backend code, try to think in terms of request‑response cycles:
- What URLs and HTTP methods will the client call?
- For each one, what data will the request contain?
- For each one, what should the server do, step by step?
- What should the response contain: which status code, which headers, which body format?
- What should happen if something goes wrong?
If you can clearly describe the life of a single request from the moment it arrives until the moment a response leaves the server, you are already thinking like a backend developer.
Later chapters will fill in the technical details about HTTP, headers, cookies, sessions, APIs, and frameworks. The common foundation for all of them is the same simple pattern: a client sends a request, your backend processes it, and your backend sends a response.
Views: 11
KAHIBARO