What is an API?
API (Application Programming Interface) is a contract or set of rules for communication between two software systems.
1- Between Frontend and Backend
2- use API servers (Googlemap, weatherAPIs, ...)
3- Beween microservices
4- IOT
5- ...
Architecture Layer
1- REST: a set of rules based on HTTP protocols.(HTTP/1.1, ...)
2- GraphQL: a query language based on HTTP protoclos.(HTTP/1.1, ...)
3- gRPC: a high-performance, open-source RPC framework built on HTTP/2 and Protocol Buffers.
4- websocket: a protocol enabling full-duplex, real-time, bi-directional communication over a single persistent TCP connection.
5- SOAP: a highly structured, XML-based protocol for exchanging information with strict security and ACID compliance.
6- webhooks: an automated, event-driven HTTP callback mechanism that pushes real-time data from a server to a client.
Protocol Layer
Standardized operations that define the type of action a client wants to perform on a server resource via HTTP.
HTTP/0.9: The original 1991 bare-bones protocol—supports only simple GET requests with plain text HTML responses and no headers.
HTTP/1.0: Introduced headers, status codes, and non-HTML media types (images, CSS), but creates a separate TCP connection for every single request.
HTTP/1.1: Standardized persistent connections (Keep-Alive) to reuse TCP pipelines, added chunked transfer, host headers for multi-domain hosting, and strict caching controls.
HTTP/2: A binary-framed protocol that introduces HTTP/2 Multiplexing (sending multiple requests concurrently over a single TCP connection), header compression (HPACK), and Server Push.
HTTP/3: Replaces TCP with the UDP-based QUIC protocol to eliminate Head-of-Line blocking, reduce connection setup latency, and seamlessly survive network IP changes (e.g., switching from Wi-Fi to mobile data).
Data Structures
Data structures in APIs define how information is organized, formatted, and serialized so that separate systems can send, read, and understand each other's messages over a network. Choosing the right data structure directly impacts bandwidth consumption, parsing speed, schema validation, and ecosystem compatibility.
Definitions & When to Use
JSON: (JavaScript Object Notation): A lightweight, human-readable key-value text format natively supported by JavaScript.
- When to use: Modern Web, Mobile APIs (REST, GraphQL), and public-facing services prioritizing fast integration and readability.
XML: (eXtensible Markup Language): A verbose, tag-based markup format with strict schema support (XSD) and metadata tags.
- When to use: Legacy enterprise architectures, SOAP services, and financial/banking integrations requiring strict compliance.
Protocol Buffers (Protobuf): A strongly-typed, language-neutral binary serialization format engineered for ultra-fast messaging.
- When to use: High-performance microservices, gRPC architectures, and low-latency internal service-to-service communications.
Endpoint
An Endpoint is the specific digital location (URI/URL) where an API receives requests, uniquely defined by combining an HTTP Method (verb) and a URI
API Endpoint = HTTP Method (Verb) + Resource Path
HTTP Method (Action): Defines what operation to perform on the resource (GET, POST, PUT, PATCH, DELETE).
Resource Path (Location): Defines which entity or resource is being targeted on the server (e.g., /api/v1/users).
Authentication & Authorization
Authentication
Purpose: Identity verification.
Primary Methods:
1- API Keys: Simple strings for service identification.
2- JWT / Tokens: Stateless, signed payload strings passed in HTTP headers.
3- Session & Cookies: Stateful session lookup backed by server memory or Redis.
4- OAuth 2.0 / OIDC: Delegated login via external providers (Google, GitHub).
Authorization
Purpose: Access control and permissions enforcement.
Primary Models:
1- RBAC (Role-Based): Assigns permissions directly to defined roles (admin, editor, user).
2- ABAC (Attribute-Based): Dynamically evaluates conditions (user role + resource owner + IP/time).
3- ACL (Access Control Lists): Specific rule sets attached directly to individual resources.
Rate limit
Rate Limiting controls how many requests a client can make to an API within a specified time frame. When a client exceeds the limit, the server rejects subsequent requests—typically returning HTTP status 429 Too Many Requests.
Why Implement Rate Limiting?
1- Prevent Abuse & DoS: Protects backend services from malicious DDoS attacks or buggy clients retrying requests in infinite loops.
2- Resource Fairness: Ensures one heavy user does not monopolize infrastructure at the expense of others.
3- Infrastructure Protection & Cost Control: Prevents cascading database failures and unexpected cloud computing bills during traffic spikes. Common
Rate Limiting Algorithms
1- Token Bucket: Tokens replenish in a bucket at a fixed rate; each request consumes a token. Allows controlled bursts of traffic. (Industry default for REST APIs like Stripe & AWS)
2- Leaky Bucket: Requests enter a queue and are processed at a constant output rate, smoothing out spikes into steady flow. Discards requests when full.
3- Fixed Window: Counts requests per static time block (e.g., 100 req/minute). Simple, but prone to traffic bursts right around window reset boundaries.
4- Sliding Window: Evaluates requests against a rolling dynamic time window. Provides high accuracy without boundary bursts.
Serialization and Deserialization
Serialization is converting an in-memory object into a sendable data format (like JSON or binary bytes); Deserialization is converting that data format back into an in-memory object.
Why We Need It
1- Data Transmission: In-memory objects (pointers, memory addresses) cannot be sent directly across a network; they must be flattened into plain bytes or text.
2- Data Persistence: In-memory objects disappear when a process stops; serialized data can be stored in databases, files, or caches (e.g., Redis).
3- Interoperability: Allows services written in different programming languages (e.g., Node.js and Python) to exchange structured data seamlessly.
Status Codes
// Standard HTTP Status Code Map
enum HttpStatusCode {
// 2xx Success
OK = 200, // Request succeeded (GET, PUT, PATCH)
CREATED = 201, // Resource successfully created (POST)
NO_CONTENT = 204, // Request succeeded, no response body (DELETE)
// 3xx Redirection
NOT_MODIFIED = 304, // Cached response is still valid
// 4xx Client Errors
BAD_REQUEST = 400, // Malformed payload or failed schema validation
UNAUTHORIZED = 401, // Missing or invalid authentication credentials
FORBIDDEN = 403, // Authenticated user lacks permission
NOT_FOUND = 404, // Endpoint or resource ID does not exist
TOO_MANY_REQUESTS = 429, // Exceeded rate limit thresholds
// 5xx Server Errors
INTERNAL_SERVER_ERROR = 500, // Unhandled exception or unexpected server crash
BAD_GATEWAY = 502, // Upstream service or proxy error
SERVICE_UNAVAILABLE = 503, // Server overloaded or down for maintenance
GATEWAY_TIMEOUT = 504 // Upstream service took too long to respond
}
// Example Express Controller Response Usage
app.get("/api/v1/users/:id", async (req, res) => {
const user = await findUser(req.params.id);
if (!user) {
return res.status(HttpStatusCode.NOT_FOUND).json({
status: HttpStatusCode.NOT_FOUND,
error: "User not found"
});
}
return res.status(HttpStatusCode.OK).json({
status: HttpStatusCode.OK,
data: user
});
});
Versioning
API _Versioning _allows developers to introduce changes, bug fixes, or new features without breaking existing client applications.
Why We Need Versioning
1- Prevent Breaking Changes: Modifying response structures or removing endpoints without versioning causes active mobile/web apps to crash.
2- Backward Compatibility: Keeps legacy applications running while newer clients migrate to updated features.
3- Controlled Upgrades: Gives third-party developers time to test and adapt to new API updates using deprecation timelines.
4 Common Ways to Version an API
1- URI Path Versioning (Most Popular & Clear)
- Format: /api/v1/users vs /api/v2/users
- Pros: Highly visible, simple to route in API gateways, easy to cache.
2- Query Parameter Versioning
- Format: /api/users?version=1 vs /api/users?version=2
- Pros: Easy to set default fallbacks, quick to test in browsers.
3- Custom Header Versioning
- Format: /api/users with header X-API-Version: 2
- Pros: Keeps URL paths clean and focused strictly on resource identification.
4- Content Negotiation / Accept Header
- Format: /api/users with header Accept: application/vnd.myapi.v2+json
- Pros: Strictly adheres to RESTful architecture standards.
Pagination
Pagination divides large API datasets into smaller chunks (pages) to optimize network bandwidth, reduce server memory usage, and improve response times.
Why Implement Pagination?
Performance: Prevents heavy database full-table scans and keeps memory consumption low.
Network Efficiency: Sends only the data the client needs, reducing response latency.
Better UX: Enables structured multi-page navigation or continuous infinite scrolling.
The 2 Main Pagination Strategies
1- Offset-Based Pagination (LIMIT & OFFSET)
- Mechanism: Skips a set number of records (e.g., page 3 = skip 40 items, take 20).Best For: Small datasets where users need to jump to specific page numbers (e.g., page 1, 5, 10).
- Drawbacks: Slow on large datasets ($O(N)$ scan time); can produce duplicate or missing items if data changes while paging.
2- Cursor-Based Pagination (Keyset)
- Mechanism: Uses a unique pointer/ID from the last item to fetch the next set (e.g., WHERE id > cursor LIMIT 20).Best For: Large datasets, real-time feeds, and infinite scroll interfaces.
- Drawbacks: Cannot jump directly to an arbitrary page number; requires a sequential, unique column.
Anatomy of an HTTP Request & Response
=== HTTP REQUEST ===
POST /v1/users HTTP/1.1 # 1. Request Line: [Method] [Path] [HTTP Version]
Host: api.example.com # 2. Request Headers: Metadata (Key-Value pairs)
User-Agent: curl/8.7.1
Authorization: Bearer eyJhbGciOiJIUzI1Ni...
Content-Type: application/json
Accept: application/json
Content-Length: 46
# 3. Empty Line (CRLF): Separates headers from body
{"name": "Moein", "email": "moein@example.com"} # 4. Request Body: Data payload sent to server
=== HTTP RESPONSE ===
HTTP/1.1 201 Created # 1. Status Line: [HTTP Version] [Status Code] [Reason Phrase]
Date: Mon, 24 Aug 2026 18:46:00 GMT # 2. Response Headers: Metadata returned by server
Content-Type: application/json; charset=utf-8
Content-Length: 138
Connection: keep-alive
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 99
# 3. Empty Line (CRLF): Separates headers from body
{ # 4. Response Body: Data payload returned to client
"status": 201,
"data": {
"id": "usr_9813",
"name": "Moein",
"email": "moein@example.com",
"createdAt": "2026-08-24T18:46:00Z"
}
}
API Caching Strategies & Revalidation
Caching reduces database load and network latency by storing API responses. When data updates before max-age expires, cache invalidation and revalidation strategies ensure clients receive fresh data without serving stale responses.
The 3 Layers of API Caching
API caching operates across three strategic layers along the request path to optimize speed and infrastructure load:
Client-Side (Browser / App)
Location: Browser memory, HTTP cache, or frontend state managers (e.g., React Query, RTK Query).
Primary Benefit: Zero network latency (0 ms) and zero server load by reusing local data.
Key Challenge: Hardest to invalidate early without force-revalidation (ETag) or cache-busting URLs.Middleware (CDN / Proxy / Gateway)
Location: Edge networks (Cloudflare, Fastly), reverse proxies (Nginx), or API Gateways (Kong).
Primary Benefit: Reduces global latency by serving data near the user while shielding backend servers from traffic spikes.
Key Challenge: Cache invalidation requires explicit API purging (cache tags) and careful handling of public vs. private user data.Server-Side (In-Memory / Database)
Location: In-memory stores (Redis, Memcached) or application RAM within the backend network.
Primary Benefit: Protects the primary database from heavy queries with instant key deletion (DEL) on data updates.
Key Challenge: Still incurs a client-to-server network round trip and consumes server memory.
Server Proxy (Reverse Proxy)
- Role: Operates at the network/transport layer in front of web servers.
- Responsibilities: Handles SSL/TLS termination, HTTP/2 multiplexing, load balancing across instances, and static file or raw response caching (e.g., Nginx, HAProxy, Varnish).
API Proxy
Role: Acts as an interface abstraction layer between the frontend and backend services.
Responsibilities: Decouples clients from backend endpoints, rewrites request paths, transforms data formats (e.g., XML to JSON), and hides internal backend architecture.
API Gateway
- Role: Serves as the single, high-level entry point for microservice architectures.
- Responsibilities: Enforces centralized security (JWT validation, OAuth2), dynamic rate limiting, request routing, service discovery, and distributed caching (e.g., Kong, AWS API Gateway).
Ways to Get New Data Before max-age Expires
1- Validation via ETag (If-None-Match)
The server generates a unique content hash (ETag) for the response.
The client stores this ETag and sends it back in the If-None-Match header on subsequent requests.
If data hasn't changed, the server responds with 304 Not Modified (no response body transferred). If data updated, the server returns 200 OK with the new body and new ETag.
2- Stale-While-Revalidate (Cache-Control)
Header: Cache-Control: max-age=60, stale-while-revalidate=300
The client receives cached data instantly while a background request checks the server for updates. The next request gets the refreshed data seamlessly.
3- No-Cache Directive (Cache-Control: no-cache)
- Tells the browser/proxy to store the response, but forces it to revalidate with the origin server before serving it to the user.
4- Active Cache Purging (CDN / Reverse Proxy API)
- The origin server issues an API call to clear specific cache keys or URLs on the edge proxy (e.g., Nginx, Cloudflare) immediately when a POST, PUT, or DELETE mutation occurs.
Raw HTTP Cache Revalidation Example
=== 1. CLIENT REVALIDATION REQUEST (Conditional GET) ===
GET /v1/products/42 HTTP/1.1 # 1. Request Line
Host: api.example.com # 2. Request Headers
If-None-Match: "e3b0c44298fc1c149afbf4c8996fb924" # Previously saved ETag hash
Cache-Control: max-age=0 # Force revalidation check
# 3. Empty Line (CRLF)
# 4. Request Body (Empty)
=== 2. SERVER RESPONSE (Data Unchanged - 304) ===
HTTP/1.1 304 Not Modified # 1. Status Line
Date: Mon, 24 Aug 2026 18:52:00 GMT # 2. Response Headers
ETag: "e3b0c44298fc1c149afbf4c8996fb924"
Cache-Control: max-age=3600, stale-while-revalidate=300
# 3. Empty Line (CRLF)
# 4. Response Body (Empty - Saved Bandwidth!)
=== 3. SERVER RESPONSE (Data Changed - 200 OK) ===
HTTP/1.1 200 OK # 1. Status Line
Date: Mon, 24 Aug 2026 18:52:00 GMT # 2. Response Headers
ETag: "a8f5f167f44f4964e6c998dee827110c" # Updated hash
Cache-Control: max-age=3600, stale-while-revalidate=300
Content-Type: application/json; charset=utf-8
# 3. Empty Line (CRLF)
{ # 4. Response Body (Fresh Payload)
"status": 200,
"data": {
"id": 42,
"price": 89.99,
"updatedAt": "2026-08-24T18:50:00Z"
}
}
API Mocking
API Mocking simulates a live backend by returning fake, realistic HTTP responses so development and testing can proceed without a running backend or database.
Why Mock?
1- Parallel Dev: Frontend and backend work simultaneously using an agreed API schema/contract.
2- Reliable Testing: Unit/integration tests run fast without network delays, database dependencies, or broken endpoints.
3- Cost & Limit Protection: Avoids burning third-party API quotas (e.g., Stripe, OpenAI) during local testing.
Main Mocking Methods
1- Client-Side / Service Worker (MSW, MirageJS)
Intercepts network calls inside the browser thread before they leave the client. Great for frontend integration tests.
2- Mock Server / Proxy (Prism, WireMock, JSON Server)
Runs a fake standalone HTTP server returning contract-based JSON. Useful across mobile, frontend, and backend environments.
3- Unit Test Spies (Jest jest.fn(), Vitest)
Mocks individual functions or API client methods directly in code during test runs.
=== HTTP REQUEST (Client requesting user data) ===
GET /api/v1/users/usr_9813 HTTP/1.1 # 1. Request Line: [Method] [Path] [HTTP Version]
Host: mock-api.example.com # 2. Target mock server domain
Authorization: Bearer mock_jwt_token_xyz123 # Mock authentication header
Accept: application/json # Client expects JSON format
# 3. Empty Line (CRLF)
# 4. Request Body (Empty for GET)
=== HTTP RESPONSE (Mock Server Returning Simulated Data) ===
HTTP/1.1 200 OK # 1. Status Line: Request succeeded
Date: Mon, 24 Aug 2026 19:48:00 GMT # 2. Response Timestamp
Content-Type: application/json; charset=utf-8 # Returned body media type
X-Mock-Server: MSW/v2.0 (Service Worker) # Custom header identifying this as mock data
X-Response-Time-Simulated: 250ms # Simulated network latency delay
Cache-Control: no-store # Prevent caching mock responses
# 3. Empty Line (CRLF)
{ # 4. Response Body: Simulated JSON payload
"status": 200,
"data": {
"id": "usr_9813", # Mocked unique resource identifier
"name": "Moein Mohammadnia", # Mocked user profile field
"email": "moein@example.com",
"role": "developer",
"isVerified": true, # Mocked boolean state
"stats": {
"totalShipments": 14, # Mocked nested numerical data
"activePackages": 2
},
"createdAt": "2026-08-24T18:00:00.000Z" # Mocked ISO timestamp
}
}
Wrapping Up & What's Next
Understanding core API architecture—from request mechanics and caching layers to proxying and mocking—is essential for building resilient, scalable backend systems.
This is Part 1 of our deep dive into modern web architecture. In Part 2, we will put these concepts into practice by building a production-ready Express API with Node.js, Prisma ORM, Redis caching, and automated testing with MSW.
💬 Let's Connect!
How do you handle cache invalidation in your current projects?
Which mocking strategies have worked best for your team?
Drop your thoughts or questions in the comments below!
If you found this useful, give it a ❤️ / 🦄 / 🔖— it helps more developers find this guide.
See you in Part 2! Happy coding! 💻✨
Top comments (0)