Layer 7 is where you spend most of your time as a developer. HTTP is the foundation, and on top of it you choose an API style: REST, GraphQL or gRPC for request-response calls, and Server-Sent Events or WebSockets when the server needs to push data. REST is the sensible default. The others earn their place when a problem has a specific need.
HTTP: the web's foundation#
HTTP is a request-response protocol: the client sends a request, the server sends one response. It is stateless, which means each request stands alone and the server doesn't need to remember earlier requests. Keeping your own services as stateless as possible is what lets you put any number of identical servers behind a load balancer.
Every HTTP message has:
- Method (requests): what you want to do, e.g.
GET,POST. - Status code (responses): how it went, e.g.
200,404. - Headers: metadata such as content type, auth token or caching rules.
- Body: the actual content, if any.
Common methods#
| Method | Meaning | Idempotent? |
|---|---|---|
GET |
Read a resource. Has no body. | Yes |
POST |
Create something or trigger an action. | No |
PUT |
Replace a resource. | Yes |
PATCH |
Partially update a resource. | Not necessarily |
DELETE |
Remove a resource. | Yes |
Idempotent means calling it twice has the same effect as calling it once. This matters for safe retries.
Common status codes#
| Range | Codes |
|---|---|
| 2xx success | 200 OK, 201 Created |
| 3xx redirect | 301 Moved Permanently, 302 Found (temporary) |
| 4xx client error | 401 Unauthorized (not logged in), 403 Forbidden (logged in but not allowed), 404 Not Found, 429 Too Many Requests |
| 5xx server error | 500 Internal Server Error, 502 Bad Gateway (an upstream server sent a bad response) |
HTTPS#
HTTPS is HTTP over TLS. It encrypts traffic so nobody between client and server can read or tamper with it. Any public website uses HTTPS without exception.
Encryption protects data in transit. It does not prove the request came from your own client. An attacker can send any body they like, so never trust it blindly.
A classic bug is reading the user ID from the request body and using it in a database query: change the ID and you can read someone else's data. You can still include IDs in requests, but the server must check them against the authenticated session or token.
REST: simple and flexible#
REST is an architectural style for building APIs on top of HTTP. HTTP is the protocol (how bytes and headers move). REST is a set of conventions for organising your API around resources.
| HTTP | REST | |
|---|---|---|
| What it is | A protocol | A design style |
| Focus | How messages are transmitted | How resources are named and accessed |
| Relationship | The foundation | Conventions for using HTTP well |
An API that follows REST's constraints is called RESTful. Modern REST APIs almost always use JSON bodies.
| Endpoint | Returns | Request body and notes |
|---|---|---|
GET /v1/users/{id} |
User | |
POST /v1/users |
User | {"user_name": "alex"} (server assigns the id) |
PUT /v1/users/{id} |
User | {"user_email": "alex@example.com"} |
GET /v1/users/{id}/posts |
[Post] | Nested resource: a user's posts |
Think in resources, not actions. updateUser and startGame are operations, not resources. The RESTful versions are PUT /users/{id} and PATCH /games/{id} with {"status": "started"}.
When to use it: by default. REST isn't the fastest option for very high-throughput internal traffic, because JSON is a fairly inefficient format to serialize and parse. But most applications aren't bottlenecked by serialization, and REST is well understood, easy to debug and works everywhere. A common split is REST for public APIs and gRPC for hot internal paths.
GraphQL: flexible data fetching#
GraphQL is a query language for APIs. With REST the server decides the shape of each response. With GraphQL the client writes a query listing exactly the fields it wants, and the server returns that shape.
It solves three problems:
- Under-fetching: a page needs data from several endpoints, so it makes several round trips.
- Over-fetching: endpoints return far more than the page needs "just in case", so responses are slow and heavy.
- Endpoint sprawl: writing a new endpoint for every new page.
query {
user(id: "42") {
name
posts(last: 5) { title }
followers { totalCount }
}
}
One request returns the name, the last five post titles and the follower count, instead of calling /user, /posts and /followers.
| Core value | Clients get exactly what they need, nothing more |
| Best for | Mobile apps and complex frontends where many teams share the same data |
| Analogy | REST is a set menu chosen by the server. GraphQL lets the client pick from a buffet. |
| Costs | Harder to cache at the HTTP layer; a careless query can trigger many database calls (the N+1 problem) |
When to use it: when frontend requirements change often or are deliberately vague, or when an app is slow because of too many API round trips.
gRPC: efficient service-to-service calls#
gRPC is an open-source RPC (remote procedure call) framework originally built at Google. It lets one service call a function on another as if it were local. It combines two pieces:
- Protocol Buffers (Protobuf): a compact binary format for data, defined in a
.protoschema. - HTTP/2: a transport that can carry many requests at once over a single connection.
Why binary is faster#
- JSON is text. To read
{"id": 123}the computer scans for braces and quotes and converts the characters1,2,3into a number. That costs CPU. - Protobuf doesn't send field names. It sends a small numeric tag and the raw value, which is quick to decode and much smaller on the wire.
The result is less CPU and bandwidth per call, which lowers both latency and cost at high traffic.
Features worth knowing#
- Strong typing: both sides generate code from the same
.protofile, so sending a string where an integer is expected fails at build time, not in production. - Multiplexing: many calls share one HTTP/2 connection without blocking each other.
- Streaming: the server, the client or both can stream messages, useful for logs or live feeds.
| REST (JSON) | gRPC (Protobuf) | |
|---|---|---|
| Readability | Human-readable, easy to debug | Binary, needs tools to inspect |
| Schema | Loose | Strict .proto contract |
| Performance | Slower (text parsing) | Faster (binary) |
| Best for | Public APIs, browsers, third-party developers | Internal microservices, high-throughput service-to-service calls |
When to use it: internal calls inside your data centre where throughput and latency matter. Avoid it for public APIs: browsers don't support it natively, and external developers find it harder to use. Using REST everywhere is still a fine starting point, and you can move hot paths to gRPC later.
Web server vs API server#
Two kinds of server sit at layer 7, and they do different jobs.
| Web server | API server | |
|---|---|---|
| Goal | Deliver the UI to people | Deliver data to programs |
| Content | Static files: HTML, CSS, JavaScript, images | Dynamic data, usually JSON |
| Serves | The browser directly | Mobile apps and single-page apps (React, Vue) |
| Examples | nginx, Apache | Flask, FastAPI, Django REST Framework (Python) |
Pushing data to the client#
Plain request-response means the client has to ask. When the server needs to send updates as they happen, you have three main options.
Server-Sent Events (SSE)#
- An HTTP standard for the server to push data over one long-lived response that never quite finishes.
- The server writes messages to the stream over time, and the client handles each one as it arrives.
- Browsers support it natively through the
EventSourceAPI, which makes it a good fit for public web apps. - Automatic reconnection: if the connection drops,
EventSourcereconnects and sends the ID of the last message it received, so the server can resume. - Real-world headaches: load balancers and proxies may close long-lived connections, and some networks buffer the stream, which delays messages. Plan for reconnects and heartbeats.
- Use for: one-way, low-latency updates such as live auction prices, stock tickers and notifications.
WebSockets#
- Two-way: client and server can both send messages at any time over a persistent connection.
- Starts as HTTP: the client sends an HTTP request asking to "upgrade". Because of this, cookies and headers from the HTTP request are available during setup.
- Then raw TCP: after the upgrade, the WebSocket owns the TCP connection until one side closes it. There's no required message format, so you can send JSON or binary.
- Every hop must support it: load balancers, proxies and firewalls in the path all need WebSocket support.
- Stateful: each server holds open connections for its users, which makes scaling to millions of users harder (see Load Balancing for the usual pub/sub fix).
- Use for: high-frequency two-way interaction like chat and multiplayer games. For simple request-response or one-way pushes it is overkill.
Choosing: a summary#
Each protocol has a situation where it is the natural choice.
| Protocol | When to use it | Why | Example |
|---|---|---|---|
| REST | Public APIs, typical web apps, general service calls | Simple, works in every browser, easy to test | A URL shortener: POST a long URL, GET link info |
| gRPC | Internal microservices where speed matters | Binary format saves CPU and bandwidth | Checkout service calling a fraud-detection service thousands of times a second |
| GraphQL | Frontends with complex, changing data needs | One round trip, exactly the fields needed | A profile page needing name, recent posts and follower count |
| SSE | One-way updates from server to client | Plain HTTP, auto-reconnect, simpler than WebSockets | Stock ticker, live sports score |
| WebSockets | Frequent two-way real-time messages | Both sides send instantly over one connection | Chat with typing indicators, multiplayer games |
A common real-world mix: REST for external users, gRPC between internal services, and SSE or WebSockets only for the specific live features that need them. Choose based on the requirement, not on what is fashionable.
Key takeaways#
- HTTP is stateless request-response: method, status code, headers and body.
- HTTPS encrypts the pipe but doesn't make the request trustworthy. Always validate identity server-side.
- REST is the default API style. Model resources, not actions.
- GraphQL fixes over- and under-fetching for complex frontends, at the cost of caching and query-cost complexity.
- gRPC (Protobuf over HTTP/2) is the fast, strictly typed choice for internal services.
- Use SSE for one-way push and WebSockets for frequent two-way messages.