I Thought I Needed a Database. Then I Needed Cloudflare's Durable Objects

There is a particular kind of engineering problem I really enjoy.
The kind where you start with:
“This should be easy.”
And three hours later you're reading documentation for a technology you didn't even know existed that morning.
That happened while my friend and I were building Obverse.
We were building an agentic system where users could interact with an AI agent through a persistent, real-time conversation.
At first, my brain went where it usually goes:
We need a database.
Obviously.
We need to keep track of conversations.
Messages.
Agent state.
Maybe some metadata.
Easy.
Database.
Done.
Except... not quite.
The problem wasn't really storing the conversation
This is where I think the distinction gets interesting.
A database is very good at answering questions like:
“What messages belong to this conversation?”
or:
“What was the user's last interaction?”
But our problem was slightly different.
We needed something that could represent an ongoing conversation.
Something that could receive events.
Talk to the client.
Coordinate the agent.
Keep track of what was happening.
Maintain a real-time connection.
And importantly, stay associated with that particular conversation.
We weren't just asking:
“Where do we put this data?”
We were asking:
“What owns this conversation while it is happening?”
That is a different question.
Enter the Durable Object
This was one of those moments where a technology suddenly makes more sense because you have a problem for it.
Cloudflare's Durable Objects are essentially stateful serverless objects: each object has a globally unique identity, its own durable storage, and a single-threaded execution model. Cloudflare also supports WebSockets inside Durable Objects, making them particularly useful for applications that need a persistent real-time coordination point.
And I remember thinking:
Oh.
This isn't just a database.
It's more like:
a little server with an identity and a memory.
That mental model clicked for me.
A normal Worker is great at handling a request:
Request
↓
Worker
↓
Response
But a Durable Object lets you say:
Conversation #123
↓
Durable Object
├── state
├── storage
├── logic
└── WebSocket connection
Now the conversation itself can have an identity.
And that's exactly the kind of abstraction we were looking for.
The “never stop” part
There was another reason this was interesting for our agent chat.
We wanted the interaction to feel less like:
User
↓
Request
↓
AI
↓
Response
↓
Goodbye.
and more like:
User
↕
Conversation
↕
Agent
↕
Tools / Events / State
The socket is alive.
The agent can stream.
The client can send events.
The server can respond.
Things can happen over the lifetime of the conversation.
That starts looking much more like a session than a traditional request.
And this is where Durable Objects made a surprising amount of sense.
Cloudflare specifically documents WebSockets as a use case for Durable Objects because a Durable Object can act as a single point of coordination for long-lived connections and multiple clients.
But does this mean Durable Objects replace databases?
No.
And this is probably the most important thing I learned.
A Durable Object is not “a better database.”
That isn't really the comparison.
A database is fundamentally concerned with persisting and querying data.
A Durable Object is concerned with owning and coordinating stateful computation.
And, conveniently, Durable Objects also have durable storage attached to them. Cloudflare describes that storage as transactional and strongly consistent, and current Durable Objects can use either a key-value API or SQLite-backed storage.
So instead of thinking:
Database vs Durable Object
I now think:
Database
↓
Where is my data?
Durable Object
↓
Who owns this state
and coordinates what happens to it?
Sometimes the answer is:
Both.
And sometimes you don't need the external database for that particular piece of the system at all.
The funny thing about building
This is probably my favourite part of the whole story.
I didn't sit down one morning and decide:
“Today, I shall learn Cloudflare Durable Objects.”
Absolutely not.
We were trying to build something.
We hit a problem.
We searched.
We read.
We experimented.
And suddenly we're reading about stateful serverless architectures and the actor model at 2am.
That's one of the things I love about building software.
Projects force you to learn things tutorials never would.
You can read about WebSockets for days.
But when you actually need a persistent connection between a user and an AI agent, the concept suddenly becomes very different.
You can read about distributed state.
But when two parts of your application need to agree on what is happening inside one conversation, consistency stops being a theoretical word.
You can read about serverless.
But when you need something to behave like a long-lived entity without managing a server yourself, suddenly the abstraction matters.
Building gives concepts somewhere to land.
And then I found the funny contradiction
There is something almost contradictory about Durable Objects.
They're durable, but they aren't necessarily running forever.
A Durable Object can start when it's needed, process requests, remain active while work is happening, and eventually hibernate when idle. Its durable state remains available across that lifecycle.
Which means the mental model isn't:
“Keep my server alive forever.”
It's closer to:
“Keep the identity and state alive. Run the computation when necessary.”
That's a subtle difference.
And I think it is an increasingly useful way to think about modern applications.
Especially now that we're building software where an “application” isn't always a request-response function.
Sometimes it is a conversation.
Sometimes it is an agent.
Sometimes it is a room.
Sometimes it is a workflow.
Sometimes it is a user session that might last five seconds or five hours.
The thing that matters isn't necessarily keeping a process alive.
It's keeping the state of the thing alive.
I thought I needed a database
Looking back, I don't think the initial thought was wrong.
We absolutely needed persistence.
We just hadn't identified the actual problem yet.
We thought:
“Where do we store the conversation?”
The better question was:
“What represents this conversation while it is alive?”
That shift in perspective led us to Durable Objects.
And that's probably the bigger lesson I took from building Obverse.
Good architecture doesn't always come from knowing more technologies.
Sometimes it comes from describing the problem more accurately.
Because once you describe the problem correctly, the technology has a much easier time introducing itself.
Apparently, ours introduced itself as a Durable Object.
References
Cloudflare — What are Durable Objects? Cloudflare's documentation on stateful serverless compute, durable storage, unique object identities, WebSockets, hibernation and the actor model.
Cloudflare — Rules of Durable Objects Cloudflare’s guidance on designing Durable Objects around an “atom of coordination” — such as a chat room, game session, document, user, or workspace.
Cloudflare Durable Objects Design Patterns — Maintaining consistent state A useful discussion of consistency and state management when working with Durable Objects.
Adam Gardner — The Cloudflare Durable Object that would not die A practical story about the lifecycle of Durable Objects, alarms, retries and an unexpectedly persistent object.
Ofuzor Chukwuemeke — I ❤️ Cloudflare A personal perspective on building with Cloudflare and the developer experience around its platform.
Obverse The project that prompted this particular rabbit hole.