The Wire: Building a Twitter Clone at the Edge
How Cloudflare's edge primitives turn the seemingly impossible—a global social network running without traditional servers—into something approaching inevitable.
There’s a peculiar satisfaction in building things that shouldn’t quite work. A Twitter clone running entirely on Cloudflare Workers, with no traditional servers, no database clusters, no load balancers humming in some distant data center—this falls squarely into that category. And yet, against all reasonable expectations, The Wire works.
The project began as an architectural experiment: what happens when you take Cloudflare’s edge primitives seriously? Not as supplements to a traditional stack, but as the entire stack itself. The answer, it turns out, is both more elegant and more instructive than anticipated.
The Primitives
Cloudflare offers a peculiar toolkit for the adventurous developer. Workers provide serverless compute at the edge—code that runs in data centers scattered across the globe, milliseconds from users. KV offers globally-distributed key-value storage. R2 stores binary blobs. And then there are Durable Objects, the most interesting primitive of the lot: single-threaded, strongly-consistent actors that maintain state across requests.
Most developers encounter these pieces individually, bolting them onto existing architectures. The Wire uses nothing else. No Postgres. No Redis. No MongoDB lurking in the shadows. Just Workers, KV, R2, Durable Objects, and Queues.
This constraint, self-imposed though it may be, forces a certain architectural discipline. You cannot fall back on familiar patterns when the familiar tools are absent.
The Data Model
The challenge with social networks is not storing data—it’s storing data in ways that make the right queries fast. A user’s profile must load instantly. A timeline must assemble from hundreds of followed accounts without perceptible delay. The counts beneath each post—likes, reposts, replies—must stay current without overwhelming the system.
The solution splits responsibilities along natural fault lines:
KV serves as the read cache. Profiles, posts, search indexes—anything that gets read frequently lives here. KV’s strength is global distribution and sub-millisecond reads. Its weakness is eventual consistency; writes take time to propagate worldwide.
Durable Objects serve as the source of truth. Each user has their own Durable Object, maintaining their social graph, settings, and interaction history. Each post has a Durable Object tracking its likes and reposts. These objects are single-threaded by design—no race conditions, no locks, no distributed consensus protocols. Just a JavaScript class that handles requests one at a time.
The pattern is write-through: mutations hit the Durable Object first (authoritative), then update KV (cache). Reads go to KV when possible, falling through to Durable Objects only when necessary.
The Fanout Problem
When a user posts, their followers’ timelines need updating. On a traditional architecture, you might write to a database and let each follower query for new content. This is simple but slow—timeline generation becomes a complex join operation repeated millions of times.
The Wire takes the opposite approach: push updates to followers rather than letting them pull. When a post is created, a message enters the queue:
await env.FANOUT_QUEUE.send({
type: "new-post",
postId,
authorId,
timestamp,
});
A queue consumer then fans this message out to each follower’s FeedDO (a Durable Object maintaining their personalized timeline). The follower’s feed is pre-assembled, waiting. When they next refresh, no complex queries are needed—just read the pre-built list.
This is Twitter’s actual architecture, scaled down to edge primitives. The elegance lies in how cleanly the pieces fit: queues handle the async fanout, Durable Objects maintain per-user state, and KV caches the rendered posts for instant retrieval.
The Performance Dance
Cloudflare imposes limits that force good engineering. Workers can make only six concurrent subrequests; total subrequests per request cap at 1000. These constraints eliminate the N+1 query pattern that plagues naive implementations.
The solution is aggressive batching:
// Bad: sequential reads
for (const id of userIds) {
const user = await env.USERS_KV.get(`user:${id}`); // N requests!
}
// Good: parallel batch
const keys = userIds.map(id => `user:${id}`);
const userMap = await batchKVGet(env, keys, "USERS_KV");
Every list operation, every timeline fetch, every search result must think in batches. The batchKVGet utility handles the chunking automatically, respecting Cloudflare’s concurrency limits while maximizing parallelism.
There’s a lesson here that extends beyond edge computing: constraints breed creativity. The six-subrequest limit isn’t a bug; it’s a forcing function for better architecture.
Durable Objects and the Set Problem
A subtle issue emerged with popular posts. When a post accumulates thousands of likes, checking whether a specific user has liked it becomes expensive. The naive approach—storing likes as an array and calling includes()—is O(n). For viral posts, this adds perceptible latency.
The solution exploits JavaScript’s Set data structure:
// Runtime: O(1) membership checks
likesSet: Set<string>
// Storage: JSON-serializable arrays
likes: [...this.likesSet]
Durable Objects serialize their state as JSON for persistence, which doesn’t support Sets. But nothing prevents using Sets at runtime and converting to arrays only when storing. This dual representation—Sets for speed, arrays for storage—eliminates the performance cliff.
The AI Correspondent
A social network without content is a lonely place. The Wire includes an AI news system that ingests stories from RSS feeds and Hacker News, processes them through Claude, and generates posts in the voice of AI-focused personas.
The pipeline works in stages:
- Aggregation: Fetch stories from configured sources
- Analysis: Claude evaluates each story for significance and generates insights
- Generation: Convert analysis into natural-sounding posts and discussion threads
The result is a feed that feels alive even when human users are absent—a useful property for development and demonstration, but also an interesting experiment in synthetic content generation.
What It Proves
The Wire demonstrates that edge computing has matured beyond simple use cases. A social network—with all its complexity around real-time updates, social graphs, media handling, and personalized feeds—can run entirely on edge primitives.
This doesn’t mean everyone should abandon traditional architectures. Cloudflare’s primitives have different tradeoffs than conventional databases. Durable Objects, for all their elegance, require thinking carefully about data partitioning. KV’s eventual consistency demands write-through patterns. The developer experience remains rougher than mature frameworks.
But for certain applications—global by nature, latency-sensitive, willing to embrace new patterns—edge-native architecture offers compelling advantages. Your code runs within 50 milliseconds of users worldwide. Your “database” requires no provisioning. Your scaling happens automatically.
The Wire is a proof of concept, not a production Twitter replacement. But proofs of concept have a way of becoming production systems when the architecture is sound. And this architecture, for all its novelty, feels increasingly sound.
The Wire is open source:
- Repository: https://github.com/chrischabot/the-wire
- Live Demo: https://the-wire.chabotc.workers.dev/
Clone it. Break it. Improve it. The edge awaits.