In the previous chapters, we learned:
Now we reach the next important layer:
How does the client actually communicate with the application?
That communication is commonly handled through HTTP.
A simplified request path is:
User
↓
Browser / Mobile App
↓
DNS
↓
IP Address
↓
TCP / QUIC
↓
TLS
↓
HTTP Request
↓
Load Balancer / Reverse Proxy
↓
Application
↓
Cache / Database / Other Services
↓
HTTP Response
↓
ClientHTTP is therefore one of the most important foundations of system design.
This chapter covers:
HTTP stands for:
Hypertext Transfer Protocol
It is an application-layer protocol used for communication between clients and servers.
The client sends a request.
The server sends a response.
Client ─────── HTTP Request ───────→ Server
Client ←────── HTTP Response ─────── ServerFor example:
GET /users/123 HTTP/1.1
Host: example.com
Accept: application/jsonThe server may respond:
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 123,
"name": "Anubhav"
}HTTP itself does not normally store application state.
It primarily defines how messages are structured and exchanged.
It is important to understand where HTTP sits.
A simplified stack is:
Application
│
│ HTTP
↓
Transport
│
│ TCP / QUIC
↓
Internet
│
│ IP
↓
Network / LinkWith HTTPS:
HTTP
↓
TLS
↓
TCP
↓
IPFor HTTP/3:
HTTP/3
↓
QUIC
↓
UDP
↓
IPThe exact protocol stack depends on the HTTP version.
The major versions you should know for system design are:
| Version | Important idea |
|---|---|
| HTTP/1.0 | Basic request/response |
| HTTP/1.1 | Persistent connections, chunked transfer, Host header |
| HTTP/2 | Binary framing, multiplexing, header compression |
| HTTP/3 | HTTP over QUIC |
HTTP/1.1 commonly uses persistent TCP connections.
Example:
TCP Connection
├── Request 1
├── Response 1
├── Request 2
├── Response 2
└── Request 3HTTP/2 introduces multiplexing.
Multiple streams can share one connection.
One TCP Connection
├── Stream 1 → Request A
├── Stream 2 → Request B
├── Stream 3 → Request C
└── Stream 4 → Request DThis reduces some of the connection-management overhead found in HTTP/1.x.
HTTP/3 uses QUIC instead of TCP.
HTTP/3
↓
QUIC
↓
UDPOne important advantage is that QUIC provides independent streams, helping avoid some forms of TCP-level head-of-line blocking.
For system design interviews, remember:
HTTP/1.1 → persistent connections
HTTP/2 → multiplexing
HTTP/3 → QUICAn HTTP request generally contains:
Request Line
Headers
Blank Line
Optional BodyExample:
POST /api/users HTTP/1.1
Host: example.com
Content-Type: application/json
Authorization: Bearer <token>
{
"name": "Anubhav",
"email": "user@example.com"
}Let's break this down.
The request line contains:
METHOD PATH VERSIONExample:
GET /api/users/123 HTTP/1.1Here:
GET = HTTP method/api/users/123 = request targetHTTP/1.1 = HTTP versionHeaders provide metadata about the request.
Example:
Host: example.com
Accept: application/json
Content-Type: application/json
Authorization: Bearer token
User-Agent: Mozilla/5.0Headers can communicate:
The body carries data sent by the client.
Example:
{
"name": "Anubhav",
"email": "user@example.com"
}A request body is common with:
It is generally not needed for a normal GET request.
The server response contains:
Status Line
Headers
Blank Line
Optional BodyExample:
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 123,
"name": "Anubhav"
}The status line contains:
HTTP VERSION
STATUS CODE
REASON PHRASEFor example:
HTTP/1.1 200 OKHTTP methods describe the intended operation.
The most important methods are:
| Method | Typical purpose |
|---|---|
| GET | Read data |
| POST | Create resource / trigger operation |
| PUT | Replace resource |
| PATCH | Partially update resource |
| DELETE | Delete resource |
| HEAD | Get headers without response body |
| OPTIONS | Discover supported communication options |
GET retrieves information.
Example:
GET /users/123Response:
{
"id": 123,
"name": "Anubhav"
}GET should generally be safe and should not intentionally modify server state.
POST commonly creates a resource or triggers an operation.
Example:
POST /usersBody:
{
"name": "Anubhav",
"email": "user@example.com"
}Possible response:
HTTP/1.1 201 CreatedPOST is not inherently idempotent.
If a client repeats:
POST /paymentsthe server might create multiple payments unless the API has an idempotency mechanism.
This becomes extremely important in payment systems.
PUT is generally used to replace the representation of a resource.
Example:
PUT /users/123Body:
{
"name": "Anubhav",
"email": "new@example.com"
}Conceptually:
Existing User
↓
Replace with supplied representationPUT is generally considered idempotent when designed according to its semantics.
PATCH is commonly used for partial modification.
Example:
PATCH /users/123Body:
{
"name": "Warrior"
}Only the specified field is changed.
Conceptually:
User
├── id → unchanged
├── name → changed
└── email → unchangedDELETE requests removal of a resource.
Example:
DELETE /users/123Possible response:
204 No ContentWhether repeated DELETE calls produce exactly the same server state depends on implementation, but DELETE is generally treated as idempotent by HTTP semantics.
These terms are frequently asked in interviews.
A safe method is intended not to change server state.
Examples:
GET
HEAD
OPTIONSA method is idempotent if making the same request multiple times has the same intended effect on server state as making it once.
Examples generally include:
GET
PUT
DELETE
HEAD
OPTIONSPOST is generally not idempotent.
Important:
Idempotent does not mean the response must be identical.
For example, the first DELETE may return:
204 No Contentand a later DELETE may return:
404 Not FoundThe key concept is the intended state effect.
Status codes tell the client what happened.
They are grouped into five classes:
1xx → Informational
2xx → Success
3xx → Redirection
4xx → Client-side error
5xx → Server-side errorGeneral successful request.
200 OKCommon with GET.
A resource was successfully created.
201 CreatedCommon with POST.
The request was accepted for processing, but processing is not necessarily complete.
Very useful for asynchronous systems.
Example:
POST /video-processing
↓
202 Accepted
↓
Background worker processes videoSuccessful request with no response body.
Common for:
DELETEor certain update operations.
The resource has permanently moved.
Temporary redirection behavior.
Useful with HTTP caching.
Example:
Client
↓
GET /image.jpg
If-None-Match: "abc"
↓
Server
↓
304 Not ModifiedThe client can use its cached copy.
The request is malformed or invalid.
Example:
{
"email": "not-an-email"
}The client has not provided valid authentication credentials.
A common practical interpretation:
Authentication is required or failed.
The client is authenticated or identifiable, but does not have permission.
Example:
User → tries admin operation
↓
403 ForbiddenThe requested resource cannot be found.
GET /users/999999may return:
404 Not FoundThe resource exists, but the HTTP method is not supported for it.
The request conflicts with the current state.
Example:
POST /users
email already existsCould return:
409 ConflictThe client exceeded a rate limit.
Example:
100 requests/minute allowed
Request #101
↓
429 Too Many RequestsGeneric server-side failure.
A gateway or proxy received an invalid response from an upstream server.
Example:
Client
↓
Load Balancer
↓
Application Server
↓
Application failsThe proxy may return 502 depending on what happened.
The service is temporarily unavailable.
Possible causes:
A gateway/proxy did not receive a timely response from an upstream service.
A classic interview question.
Think:
401 → Who are you?
403 → I know who you are, but you cannot do this.Example:
GET /admin/usersWithout valid authentication:
401Authenticated normal user attempting admin-only operation:
403The exact security behavior can vary by API, but this is the standard mental model.
Headers carry metadata.
Example:
Content-Type: application/json
Accept: application/json
Authorization: Bearer token
Cache-Control: max-age=3600
ETag: "abc123"
User-Agent: Mozilla/5.0Important categories include:
Content-Type tells the receiver what format the message body uses.
Example:
Content-Type: application/jsonOther examples:
text/html
text/plain
application/xml
multipart/form-data
application/x-www-form-urlencodedFor a JSON API:
Content-Type: application/jsonAccept tells the server what response formats the client can process.
Example:
Accept: application/jsonThis is different from Content-Type.
Remember:
Content-Type
→ What format am I sending?
Accept
→ What format do I want back?A common authentication pattern is:
Authorization: Bearer <token>The server validates the token before allowing protected operations.
Typical flow:
Client
↓
Authorization header
↓
API Gateway / Application
↓
Validate token
↓
Authorize requestA cookie is data associated with a client and a website/domain.
Example:
Set-Cookie: session_id=abc123The browser can later send:
Cookie: session_id=abc123Cookies are commonly used for:
A common flow:
1. User logs in
↓
2. Server validates credentials
↓
3. Server creates session
↓
4. Server sends session cookie
↓
5. Browser stores cookie
↓
6. Browser automatically sends cookie
↓
7. Server identifies sessionExample:
Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=LaxPrevents JavaScript from directly reading the cookie.
Useful for reducing exposure to some forms of XSS-based cookie theft.
Cookie should be transmitted over HTTPS.
Controls cross-site cookie behavior and can help mitigate certain CSRF scenarios.
Common values:
Strict
Lax
NoneSameSite=None generally requires Secure.
A session allows a server to associate multiple requests with the same user.
A traditional architecture:
Browser
↓
Session Cookie
↓
Load Balancer
↓
Application Server
↓
Session StoreThe session store may be:
Redis
Database
Distributed cacheImagine:
User
↓
Load Balancer
├── Server A
└── Server BThe user logs in through Server A.
If the session exists only in Server A's memory:
Login
↓
Server A
↓
session stored in A memoryThen the next request goes to Server B:
Request
↓
Server B
↓
No session
↓
User appears logged outThis is one reason centralized or distributed session storage can be important.
One solution is session affinity.
User A → always Server A
User B → always Server BThis is commonly called:
Sticky sessions
It can work, but it creates trade-offs.
Problems can include:
A more scalable design often keeps application servers stateless and stores shared state externally.
In a stateless API:
Request
↓
Server
↓
ResponseThe server does not need local memory of the previous request to understand the current request.
Authentication information may be carried in each request, for example:
Authorization: Bearer <token>Benefits:
But statelessness does not mean the overall system has no state.
The state can live in:
Database
Redis
Object storage
External identity providerREST stands for:
Representational State Transfer
REST is an architectural style, not a protocol.
A REST-style API generally models resources.
For example:
/users
/users/123
/orders
/orders/456
/products
/products/10Instead of:
/getUser
/createUser
/updateUser
/deleteUsera resource-oriented design commonly uses:
GET /users/123
POST /users
PUT /users/123
PATCH /users/123
DELETE /users/123The HTTP method expresses the operation.
The URL identifies the resource.
Example:
/users/123/ordersThis can represent orders belonging to user 123.
Another example:
/orders/456/itemsUse nesting when the relationship is meaningful.
Avoid excessively deep URLs such as:
/users/1/orders/2/items/3/products/4/reviews/5They can become difficult to maintain.
JSON stands for:
JavaScript Object Notation
Example:
{
"id": 123,
"name": "Anubhav",
"active": true
}JSON is widely used in APIs because it is:
A response should have a predictable structure.
Example:
{
"id": "123",
"name": "Anubhav",
"email": "user@example.com"
}For an error:
{
"error": {
"code": "USER_NOT_FOUND",
"message": "User does not exist"
}
}Consistency is more important than inventing a complex format.
These are different.
Answers:
Who are you?
Examples:
Password
OTP
Session
JWT
OAuth
API keyAnswers:
What are you allowed to do?
Example:
User → read own profile
Admin → read all profilesFlow:
Authentication
↓
Identity
↓
Authorization
↓
PermissionAn API key is a credential commonly used to identify an application or client.
Example:
X-API-Key: abc123Common uses:
API keys should be treated as secrets when they provide privileged access.
JWT stands for:
JSON Web Token
A JWT commonly contains:
Header
Payload
SignatureConceptually:
header.payload.signatureA payload might contain claims such as:
{
"sub": "123",
"role": "user",
"exp": 1790000000
}Important:
A signed JWT is not automatically encrypted.
Sensitive information should not be placed in a JWT merely assuming the payload is secret.
User
↓
Login
↓
Authentication Server
↓
JWT
↓
Client
↓
Authorization: Bearer JWT
↓
API
↓
Validate signature + claims
↓
AuthorizeJWTs can be useful for stateless authentication, but they introduce considerations around:
OAuth is an authorization framework used to allow applications to access resources without requiring the user to share their password with the client application.
A simplified example:
User
↓
Application
↓
Authorization Server
↓
User grants permission
↓
Access Token
↓
Application
↓
Resource ServerFor modern web authentication flows, OAuth 2.x and OpenID Connect are important topics.
OpenID Connect builds an identity layer on top of OAuth 2.0.
Idempotency is one of the most important system design concepts.
Suppose a user makes a payment:
POST /paymentsThe network fails after the server processes the payment.
The client does not know whether payment succeeded.
It retries:
POST /paymentsWithout protection:
Payment 1 → ₹10,000
Payment 2 → ₹10,000The customer may be charged twice.
A common solution is an idempotency key.
Client sends:
POST /payments
Idempotency-Key: 7b9f-payment-123The server stores the result associated with that key.
First request:
Key = ABC
Payment processed
Result storedRetry:
Key = ABC
↓
Existing result found
↓
Return previous resultTherefore:
Retry ≠ duplicate operationA simplified architecture:
Client
↓
API
↓
Idempotency Store
↓
Payment Service
↓
DatabaseThe idempotency record might contain:
key
request fingerprint
status
response
created_at
expirationThe exact implementation must carefully handle concurrency.
Two identical requests can arrive at nearly the same time.
The system must avoid:
Request A → process payment
Request B → process same paymentLarge datasets should not normally be returned in one response.
Bad:
GET /usersreturning millions of users.
Instead:
GET /users?page=1&limit=20or cursor-based pagination.
Example:
GET /users?limit=20&offset=40Meaning:
skip 40
return next 20Advantages:
Problems:
Example:
GET /users?limit=20&after=eyJpZCI6MTAw...The cursor represents where to continue.
Conceptually:
Page 1
↓
last item = 100
↓
cursor = 100
Page 2
↓
items after 100Cursor pagination is often better for large or frequently changing datasets.
Example:
{
"data": [
{
"id": 101,
"name": "A"
},
{
"id": 102,
"name": "B"
}
],
"next_cursor": "abc123",
"has_more": true
}APIs often support filtering.
Example:
GET /products?category=electronicsMultiple filters:
GET /products?category=electronics&min_price=1000&max_price=50000Filters should have predictable semantics.
Example:
GET /products?sort=priceDescending:
GET /products?sort=-priceA production API should explicitly document supported fields rather than accepting arbitrary database fields.
Example:
GET /products?q=iphoneFor simple search this may be enough.
For large-scale search systems, a dedicated search engine can become appropriate.
Later in this course we will study search architecture in detail.
Rate limiting controls how many requests a client can make during a period.
Example:
100 requests / minuteIf the client exceeds the limit:
429 Too Many RequestsRate limiting protects:
Imagine an API has:
10 application servers
1 databaseA malicious or buggy client sends:
100,000 requests/secEven if application servers can process some of the traffic, the database may collapse.
Rate limiting creates a protective boundary.
Internet
↓
API Gateway
↓
Rate Limiter
↓
Application
↓
DatabaseImportant algorithms include:
12:00–12:01
100 requests allowedSimple, but boundary bursts can occur.
Tracks requests over a moving time range.
More accurate, but potentially more expensive.
Tokens accumulate up to a maximum capacity.
Requests consume tokens.
Token Bucket
├── token
├── token
├── token
└── tokenUseful because it can allow controlled bursts.
Requests are processed at a controlled rate.
Useful for smoothing traffic.
We will study these algorithms in greater detail later.
Possible locations:
CDN
↓
API Gateway
↓
Load Balancer
↓
ApplicationRate limiting can be:
Distributed systems often use Redis or another shared store when multiple API instances need consistent rate-limit state.
APIs evolve.
Suppose version 1 returns:
{
"name": "Anubhav"
}Version 2 changes it to:
{
"first_name": "Anubhav",
"last_name": "Chaudhary"
}Existing clients may break.
Versioning helps manage compatibility.
Example:
/api/v1/users
/api/v2/usersEasy to understand and commonly used.
Another approach:
Accept: application/vnd.example.v2+jsonThis keeps versioning out of the URL but increases complexity.
There are multiple legitimate strategies.
The important system design concern is:
How do we evolve an API without unexpectedly breaking existing clients?
Good API evolution often follows:
Old client
↓
Existing API behavior remains valid
↓
New functionality added
↓
Old behavior deprecated graduallyAvoid breaking clients without a migration strategy.
Potential strategies:
A large system may place an API gateway in front of services.
┌── User Service
Client
↓ ├── Order Service
API Gateway ──────┼── Payment Service
└── Notification ServiceThe gateway may handle:
We will study API gateways more deeply later.
These concepts overlap but are not identical.
A reverse proxy generally forwards client requests to backend servers.
An API gateway is often a more application/API-aware entry point that can provide:
A reverse proxy can perform some of these functions too.
Never assume every backend request will finish quickly.
Example:
Client
↓
API Gateway
↓
Service A
↓
Service B
↓
DatabaseIf Service B hangs:
Service A waits
Gateway waits
Client waitsEventually resources can be exhausted.
Therefore APIs should have explicit timeouts.
Example:
Client timeout: 10s
Gateway timeout: 8s
Service timeout: 5s
Database timeout: 2sThe exact values depend on the system.
Retries can improve reliability when failures are transient.
Example:
Request
↓
503
↓
wait
↓
retryBut retries can also make an outage worse.
Suppose:
Service overloaded
↓
100 requests fail
↓
each client retries 3 times
↓
300 additional requestsThis is called a retry storm.
Use:
Instead of retrying immediately:
Retry 1 → 100 ms
Retry 2 → 200 ms
Retry 3 → 400 ms
Retry 4 → 800 msJitter adds randomness:
100 ms + random
200 ms + random
400 ms + randomThis helps avoid many clients retrying at exactly the same time.
HTTP supports caching.
Example:
Cache-Control: max-age=3600The client or intermediary can reuse the response for the specified period.
Caching can happen at:
Browser
↓
CDN
↓
Reverse Proxy
↓
Application CacheAn ETag identifies a particular representation.
Example:
ETag: "abc123"The client later sends:
If-None-Match: "abc123"If nothing changed:
304 Not ModifiedThis saves bandwidth.
The general pattern:
First request
↓
200 + ETag
↓
Client stores response
Later request
↓
If-None-Match
↓
Server checks version
↓
304This is particularly useful for frequently requested resources that do not change often.
Large HTTP responses can be compressed.
Common compression methods include:
gzip
BrotliCompression reduces network bandwidth but costs CPU.
The client can advertise support using:
Accept-Encoding: gzip, brThe server can respond with:
Content-Encoding: brSystem design trade-off:
CPU cost
↕
Network bandwidthDistributed systems need a way to trace one request across services.
Example:
X-Request-ID: 7f82abc123Request:
Client
↓ request-id=7f82
Gateway
↓
Service A
↓
Service B
↓
DatabaseLogs can include:
request_id=7f82This makes debugging much easier.
Later we will study distributed tracing and observability in depth.
Bad:
{
"error": "bad"
}Better:
{
"error": {
"code": "INVALID_EMAIL",
"message": "The email address is invalid"
}
}For validation errors, structured details can help:
{
"error": {
"code": "VALIDATION_FAILED",
"message": "Request validation failed",
"fields": {
"email": "Invalid email format"
}
}
}Do not expose:
An API should consider:
Who is calling?
What can they access?
Is the input valid?
Is the caller sending too many requests?
Is communication encrypted?
Are credentials protected?
Are security events observable?
Does the identity have only required permissions?
Never blindly trust client input.
Example:
{
"age": "hello"
}If the API expects an integer, reject it.
Validation should happen before expensive operations.
Request
↓
Authentication
↓
Authorization
↓
Validation
↓
Business logic
↓
DatabaseThe exact ordering can vary, but untrusted input should be validated before use.
A malicious client might send:
POST /upload
Body = 10 GBWithout limits, this can consume memory, bandwidth, storage, or CPU.
Production APIs often enforce:
Suppose we are designing a user API.
POST /api/v1/usersGET /api/v1/users/123PATCH /api/v1/users/123DELETE /api/v1/users/123GET /api/v1/users?limit=20&after=abcThis is much easier to reason about than arbitrary action-based endpoints.
POST /api/v1/orders
GET /api/v1/orders/123
PATCH /api/v1/orders/123
DELETE /api/v1/orders/123
GET /api/v1/users/42/ordersFor an operation that is not naturally CRUD, an action endpoint can sometimes be appropriate.
For example:
POST /api/v1/orders/123/cancelThe goal is not to force every business operation into CRUD.
The goal is to make the API semantics clear and consistent.
Some operations finish quickly:
POST /users
↓
201 CreatedOther operations take a long time:
POST /videos/123/transcodeInstead of keeping the request open for minutes:
POST
↓
202 Accepted
↓
job createdThen:
GET /jobs/456returns:
{
"id": "456",
"status": "processing"
}Later:
{
"id": "456",
"status": "completed"
}This pattern is common in distributed systems.
Sometimes the server needs to notify another system when something happens.
Example:
Payment Service
↓
Payment completed
↓
Webhook
↓
Merchant ApplicationExample:
POST https://merchant.example/webhooks/paymentThe webhook body may contain:
{
"event": "payment.completed",
"payment_id": "pay_123",
"amount": 1000
}Webhook systems need:
API design should not simply expose database tables.
Bad mental model:
Database table
↓
Directly expose tableInstead:
API
↓
Domain / business logic
↓
Data access
↓
DatabaseWhy?
Because the database schema may change without wanting to break clients.
The API should represent a stable business contract.
An API contract defines how clients and servers communicate.
It can specify:
OpenAPI is commonly used to describe HTTP APIs.
Example conceptual contract:
GET /users/{id}
Response:
{
id: string,
name: string,
email: string
}A typical production architecture may look like:
┌── User Service
│
Client
↓ ├── Order Service
CDN / Edge
↓ │
API Gateway
↓ ├── Payment Service
Auth + Rate Limit + Logs │
└── Notification ServiceEach service may have its own:
Cache
Database
QueueThe gateway provides a controlled entry point.
Suppose the API has a target:
P95 latency < 300 msThe request may spend time in:
Gateway 10 ms
Service A 40 ms
Cache 10 ms
Database 80 ms
Service B 60 ms
Network 30 ms
--------------------
Total 230 msThe remaining budget is:
300 - 230 = 70 msSystem designers should think in terms of latency budgets rather than optimizing one component in isolation.
Average latency can hide bad user experiences.
Suppose:
Average = 100 msBut:
P50 = 70 ms
P95 = 300 ms
P99 = 2 secondsThen 1% of requests are taking around seconds.
For large systems, tail latency matters greatly.
We will study performance engineering later.
HTTP sits at the application layer.
HTTP
↓
TCP/QUIC
↓
IPRemember:
401 → authentication problem
403 → permission problemPOST may create duplicates.
Use idempotency mechanisms for operations where retries must not duplicate effects.
Use:
Every network call can potentially hang.
Use explicit timeouts.
Retries can amplify outages.
Use:
Horizontal scaling can break this design.
Use shared state or stateless authentication where appropriate.
A signed JWT is not automatically encrypted.
Do not return stack traces or infrastructure secrets to clients.
An API is a business-facing contract, not necessarily a direct mirror of storage.
Browser
↓
HTTP
↓
ServerBrowser
↓
DNS
↓
CDN
↓
TLS
↓
Load Balancer
↓
API Gateway
↓
Authentication
↓
Rate Limiter
↓
Service
↓
Cache
↓
Database
↓
Queue / Other ServicesThe more advanced system introduces many possible failure and latency points.
System design is largely about controlling those points.
A strong answer:
DNS resolution
↓
Connection establishment
↓
TLS for HTTPS
↓
HTTP request
↓
CDN / reverse proxy / load balancer
↓
Application
↓
Cache/database/dependencies
↓
HTTP response
↓
Browser processes responseGET is normally used to retrieve resources.
POST is commonly used to create resources or trigger operations.
POST is generally not idempotent.
PUT generally replaces a resource representation.
PATCH generally applies partial modifications.
401 → authentication required/failed
403 → authenticated/identified but not allowedTo avoid transferring and processing enormous datasets in one request.
Offset is simple but can become inefficient and unstable for changing large datasets.
Cursor pagination is generally more suitable for large, frequently changing datasets.
Because network failures can cause clients to retry requests after the server has already processed them.
An idempotency key allows the server to recognize the retry and avoid duplicating the operation.
It can centralize:
Without timeouts, blocked dependencies can consume connections, threads, workers, and other resources until the system becomes overloaded.
Because retries increase traffic during failures and can amplify an outage.
Design a REST API for a simple task-management application.
Requirements:
Try to design:
POST /api/v1/...
GET /api/v1/...
PATCH /api/v1/...
DELETE /api/v1/...Also decide:
Design the following:
Client
↓
API Gateway
↓
Task Service
↓
PostgreSQLAdd:
Authentication
Rate Limiting
Pagination
Request Validation
Error Handling
Idempotency
LoggingExample endpoints:
POST /api/v1/tasks
GET /api/v1/tasks
GET /api/v1/tasks/{id}
PATCH /api/v1/tasks/{id}
DELETE /api/v1/tasks/{id}List endpoint:
GET /api/v1/tasks?status=pending&limit=20&after=abcCreate response:
201 CreatedInvalid request:
400 Bad RequestUnauthenticated:
401 UnauthorizedNot permitted:
403 ForbiddenNot found:
404 Not FoundRate limited:
429 Too Many RequestsUnexpected server failure:
500 Internal Server ErrorYou should now understand:
We can now connect the first four chapters:
SYSTEM DESIGN
User
│
▼
Browser / Mobile App
│
│ DNS
▼
IP Address
│
│ TCP / QUIC
▼
Network Connection
│
│ TLS
▼
Secure Connection
│
│ HTTP
▼
CDN / Reverse Proxy / Load Balancer
│
▼
API Gateway
│
├── Authentication
├── Authorization
├── Rate Limiting
└── Routing
│
▼
Application Service
│
├── Cache
├── Database
├── Queue
└── Other Services
│
▼
HTTP ResponseThis is the foundation on which the rest of system design will be built.
Before moving on, make sure you can explain:
In the next chapter, we move from how HTTP communicates with an application to how the application itself is structured and deployed.
We will learn:
Web Server
↓
Application Server
↓
Process
↓
Thread / Worker
↓
Request Handling
↓
Application ArchitectureWe will cover:
Then we will be ready to study scaling applications in Chapter 06.
Mark it as complete to track your mastery.