DEV Community

Cover image for Network Protocols: A Senior Engineer's Guide
Arghya Majumder
Arghya Majumder

Posted on

Network Protocols: A Senior Engineer's Guide

Network Protocols: A Senior Engineer's Guide

A comprehensive guide to REST, GraphQL, WebSockets, and SSE for system design interviews.


1. REST (Representational State Transfer)

REST is the foundation of most web communication, built on the stateless nature of HTTP.

Transport Mechanism

Operates primarily over HTTP/1.1 or HTTP/2:

Version Behavior
HTTP/1.1 Each request usually requires a new TCP connection (or reuses with overhead)
HTTP/2 Multiplexes multiple requests over a single connection to reduce latency

The "Over-fetching" Problem

A major architectural drawback of REST is that endpoints return a fixed data structure.

GET /users/1

// You only need the name, but you get everything:
{
  "id": 1,
  "name": "John",
  "email": "john@example.com",
  "address": { ... },
  "orderHistory": [ ... ],  // 50KB of data you don't need
  "preferences": { ... }
}
Enter fullscreen mode Exit fullscreen mode

Result: Wasting bandwidth and browser memory.

Caching Strategy (REST's Superpower)

REST is uniquely powerful because it leverages standard HTTP caching headers:

Header Purpose
ETag Content fingerprint for conditional requests
Cache-Control Tells browser/CDN how long to cache
Last-Modified Timestamp-based cache validation

Browsers and CDNs can natively cache REST responses, significantly reducing server load for static or semi-static data.


2. GraphQL

GraphQL is a query language for APIs that provides a complete and understandable description of the data in your API.

The "Under-fetching" Solution

Unlike REST, which might require three separate calls to get a user, their posts, and their followers, GraphQL fetches all of this in a single round trip.

# One request, all the data you need
query {
  user(id: 1) {
    name
    posts {
      title
    }
    followers {
      name
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Critical for mobile: High latency on cellular networks makes multiple round trips expensive.

Schema & Type Safety

GraphQL uses a strongly typed schema:

type User {
  id: ID!
  name: String!
  email: String!
  posts: [Post!]!
}
Enter fullscreen mode Exit fullscreen mode

Benefit: Tools like GraphQL Code Generator automatically create TypeScript interfaces, ensuring the frontend never attempts to access a field that doesn't exist.

Architectural Cost

Because GraphQL often uses POST requests for all queries, native browser caching is much harder.

Solutions:

  • Apollo Client - Sophisticated in-memory cache with normalized data
  • Relay - Facebook's production-grade caching layer
  • Persisted Queries - Hash queries to enable GET requests and CDN caching

3. WebSockets (WS)

WebSockets provide a persistent, full-duplex (two-way) communication channel between client and server.

The Handshake

1. Client sends HTTP request with "Upgrade: websocket" header
2. Server responds with "101 Switching Protocols"
3. Protocol switches from HTTP to Binary/Frame-based communication
4. Connection stays open for bidirectional messaging
Enter fullscreen mode Exit fullscreen mode

Framing and Overhead

Protocol Header Size per Message
HTTP ~800 bytes (cookies, user-agents, etc.)
WebSocket 2-10 bytes (after handshake)

Result: Most efficient protocol for high-frequency data:

  • Cursor positions in collaborative docs (Google Docs)
  • Rapid price updates (Trading platforms)
  • Multiplayer game state

State Management Challenge

Since the connection is persistent, the server must keep a record of every connected client in memory.

Problem: Horizontal scaling is difficult.

Solution: Use a Pub/Sub layer (like Redis) to sync messages across multiple server instances.

┌──────────┐     ┌──────────┐     ┌──────────┐
│ Server 1 │────▶│  Redis   │◀────│ Server 2 │
│ (1000    │     │  Pub/Sub │     │ (1000    │
│  clients)│     └──────────┘     │  clients)│
└──────────┘                      └──────────┘
Enter fullscreen mode Exit fullscreen mode

4. SSE (Server-Sent Events)

SSE is a standard that allows servers to push data to web pages over HTTP.

Unidirectional Flow

Unlike WebSockets, SSE is strictly one-way (Server → Client).

// Client
const eventSource = new EventSource('/notifications');

eventSource.onmessage = (event) => {
  console.log('New notification:', event.data);
};

// Server sends updates whenever available
// data: {"type": "new_message", "count": 5}
Enter fullscreen mode Exit fullscreen mode

Native Advantages

Feature Benefit
Built on HTTP Works through most firewalls and proxies without special configuration
Auto-reconnection Browser automatically tries to reconnect on disconnect
Last-Event-ID Server can "catch up" on missed messages after reconnect

Best Use Case

SSE is the "goldilocks" protocol for:

  • News feeds
  • Stock tickers
  • Social media notifications
  • Live sports scores

When to use: User doesn't need to talk back to the server in real-time, but needs to see server updates immediately.


5. Quick Comparison Table

Protocol Statefulness Browser Caching Scalability Complexity Best For
REST Stateless Excellent (Native) Low CRUD operations, Public APIs
GraphQL Stateless Difficult (Requires Library) Medium Complex data requirements, Mobile apps
WebSockets Stateful None High (Requires Pub/Sub) Real-time bidirectional (Chat, Games)
SSE Stateful Limited Medium Server push notifications

6. Decision Framework for Interviews

Is the data...
│
├─▶ Static or changes infrequently?
│   └─▶ REST (leverage HTTP caching)
│
├─▶ Complex with nested relationships?
│   └─▶ GraphQL (avoid over/under-fetching)
│
├─▶ Real-time AND bidirectional?
│   └─▶ WebSockets (chat, collaboration, games)
│
└─▶ Real-time BUT server-to-client only?
    └─▶ SSE (notifications, feeds, tickers)
Enter fullscreen mode Exit fullscreen mode

7. Deep Dive: HTTP/2 and HTTP/3

Understanding the transport layer is crucial for Senior-level discussions.

HTTP/1.1 Limitations

┌─────────────────────────────────────────────┐
│  Browser (6 connection limit per domain)    │
│                                             │
│  Conn 1: GET /style.css ──────────────────▶ │
│  Conn 2: GET /app.js ─────────────────────▶ │
│  Conn 3: GET /image1.png ─────────────────▶ │
│  Conn 4: GET /image2.png ─────────────────▶ │
│  Conn 5: GET /image3.png ─────────────────▶ │
│  Conn 6: GET /image4.png ─────────────────▶ │
│                                             │
│  image5.png WAITING... (blocked)            │
└─────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Head-of-Line Blocking: If style.css is slow, it blocks its connection.

HTTP/2 Multiplexing

┌─────────────────────────────────────────────┐
│  Single TCP Connection                      │
│                                             │
│  Stream 1: GET /style.css ──┐               │
│  Stream 2: GET /app.js ─────┼───▶ Server    │
│  Stream 3: GET /image1.png ─┤               │
│  Stream 4: GET /image2.png ─┤               │
│  Stream 5: GET /image3.png ─┘               │
│                                             │
│  All requests sent simultaneously!          │
└─────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Key Features:

  • Binary Framing: Headers and body are split into frames
  • Header Compression (HPACK): Reduces header overhead by 85%
  • Server Push: Server can send resources before client asks

HTTP/3 (QUIC)

Built on UDP instead of TCP:

Feature HTTP/2 (TCP) HTTP/3 (QUIC)
Connection Setup TCP + TLS = 2-3 RTT 0-1 RTT (connection ID persists)
Head-of-Line Blocking Still exists at TCP level Eliminated (streams are independent)
Connection Migration Breaks on IP change Survives (uses connection ID, not IP)

Mobile Game-Changer: When user switches from WiFi to cellular, HTTP/3 connection survives.


8. CORS: The Security Handshake

Cross-Origin Resource Sharing is the browser's security mechanism for cross-domain requests.

The Preflight Dance

┌──────────┐                           ┌──────────┐
│  Browser │                           │  Server  │
│(app.com) │                           │(api.com) │
└────┬─────┘                           └────┬─────┘
     │                                      │
     │  OPTIONS /api/users                  │
     │  Origin: https://app.com             │
     │  Access-Control-Request-Method: POST │
     │  Access-Control-Request-Headers:     │
     │    Content-Type, Authorization       │
     │─────────────────────────────────────▶│
     │                                      │
     │  204 No Content                      │
     │  Access-Control-Allow-Origin: *      │
     │  Access-Control-Allow-Methods: POST  │
     │  Access-Control-Max-Age: 86400       │
     │◀─────────────────────────────────────│
     │                                      │
     │  POST /api/users (actual request)    │
     │─────────────────────────────────────▶│
Enter fullscreen mode Exit fullscreen mode

When Preflight is Triggered

Request Type Preflight Required?
GET with standard headers No (Simple Request)
POST with Content-Type: application/json Yes
Any request with Authorization header Yes
PUT, DELETE, PATCH Yes

CORS Headers Reference

# Server response headers
Access-Control-Allow-Origin: https://app.com  # Or * for any
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true  # For cookies
Access-Control-Max-Age: 86400  # Cache preflight for 24 hours
Access-Control-Expose-Headers: X-Custom-Header  # Expose to JS
Enter fullscreen mode Exit fullscreen mode

The Credentials Trap

// Frontend
fetch('https://api.com/data', {
  credentials: 'include'  // Send cookies
});

// Backend MUST respond with:
// Access-Control-Allow-Credentials: true
// Access-Control-Allow-Origin: https://app.com  (NOT *)
Enter fullscreen mode Exit fullscreen mode

Rule: When credentials: 'include', you cannot use * for origin.


9. Request Lifecycle: Under the Hood

DNS Resolution

1. Browser checks local cache
2. OS checks /etc/hosts and its cache
3. Query goes to configured DNS resolver (ISP or 8.8.8.8)
4. Resolver checks its cache
5. If miss: Recursive query to root → TLD → Authoritative NS
6. IP address returned and cached (TTL-based)
Enter fullscreen mode Exit fullscreen mode

TCP Connection Establishment (3-Way Handshake)

Client                    Server
   │                         │
   │─────── SYN ────────────▶│  "I want to connect"
   │                         │
   │◀────── SYN-ACK ─────────│  "OK, I acknowledge"
   │                         │
   │─────── ACK ────────────▶│  "Great, connected!"
   │                         │
   │      Connection Open    │
Enter fullscreen mode Exit fullscreen mode

Time Cost: ~1 RTT (Round Trip Time)

TLS Handshake (HTTPS)

Client                           Server
   │                               │
   │─── ClientHello ──────────────▶│  Supported ciphers, random
   │                               │
   │◀── ServerHello + Certificate ─│  Chosen cipher, cert
   │                               │
   │─── Key Exchange + Finished ──▶│  Pre-master secret
   │                               │
   │◀── Finished ──────────────────│
   │                               │
   │     Encrypted Connection      │
Enter fullscreen mode Exit fullscreen mode

Time Cost: ~2 RTT (TLS 1.2) or ~1 RTT (TLS 1.3)

Total for new HTTPS connection: 3-4 RTT before first byte of data!


10. Interview Tip

"The protocol choice depends on the data access pattern. For standard CRUD with good caching needs, REST wins. For complex, nested data on mobile, GraphQL reduces round trips. For real-time bidirectional communication, WebSockets are necessary despite the scaling complexity. For simple server-push scenarios, SSE offers the best simplicity-to-functionality ratio. I also consider the transport layer — HTTP/2 for multiplexing, HTTP/3 for mobile users who switch networks. And for cross-origin security, I ensure proper CORS configuration with preflight caching to minimize overhead."

Top comments (0)