Design a Chat API
You'll learn to
- -Choose and justify a real-time protocol for a chat system, combining WebSocket-based delivery with a REST API for history and setup
- -Design presence, delivery status, and read receipts as first-class, queryable state rather than ephemeral signals
A chat API is fundamentally a real-time problem wrapped around a fairly conventional set of resources - conversations, messages, participants - which makes it a strong test of whether a candidate picks the protocol that actually fits each part of the problem, rather than forcing one protocol to do everything.
Step 1-3: Clients, Resources, and a Deliberately Mixed Protocol Choice
Clients: a mobile and web chat client, needing both real-time message delivery and the ability to load conversation history on first open. This naturally splits into two protocol needs rather than one: a persistent, bidirectional connection for live messages (gRPC's bidirectional streaming, or a WebSocket-based approach, depending on client platform constraints), and a conventional REST API for everything that isn't inherently real-time - fetching conversation history, managing participants, searching past messages.
Real-Time Delivery
service ChatService {
rpc Connect (stream ClientMessage) returns (stream ServerMessage);
}
message ClientMessage {
oneof payload {
SendMessage send = 1;
MarkAsRead read_receipt = 2;
TypingIndicator typing = 3;
}
}A single bidirectional stream (from the gRPC streaming chapter) carries every real-time event in both directions - outgoing messages, read receipts, typing indicators - using `oneof` (from the wire-evolution chapter) to represent the different kinds of client-to-server events over one connection, rather than needing a separate stream per event type.
REST for Everything Else
GET /conversations/501/messages?limit=50&before=msg_1200
GET /conversations/501/participants
POST /conversationsLoading the last 50 messages when a user opens a conversation, or listing participants, has no real-time component - it's a conventional paginated fetch (cursor-based, from the pagination chapter), and forcing this through the same real-time streaming connection would add unneeded complexity for no benefit over a plain REST call.
Presence and Delivery Status as Queryable State
{
"id": "msg_1201",
"sender_id": "user_42",
"text": "Hey, are you free later?",
"sent_at": "2026-08-07T14:00:00Z",
"delivery_status": "read",
"read_by": [{"user_id": "user_99", "read_at": "2026-08-07T14:01:30Z"}]
}Delivery status and read receipts shouldn't be purely ephemeral, in-the-moment signals lost the instant a client isn't connected to receive them - modeling them as durable, queryable fields on the message resource means a client that reconnects after being offline can fetch the current, correct state via the REST API, rather than having missed a live event permanently.
A strong signal on this prompt is explicitly justifying the REST-plus-streaming split rather than defaulting to streaming for everything - naming "this specific part doesn't actually need real-time delivery" is exactly the protocol-choice judgment this course's opening module set up as the actual skill being tested.
Interview Signal is part of Pro
See a real weak answer next to a real strong one for this exact topic.
Quiz is part of Pro
Test what you just read with a short quiz, and bank the XP.
Design a Chat API in the API Design Lab's Full System Design act.