Tutorials
Object Classes, Objects and TTL: How Data Is Organised
Account, project, object class, object. What each layer is for, how ids decide everything, and why TTL is a design decision rather than a cleanup setting.
Four layers, and the useful thing is knowing which decisions belong at each.
For a separate operational perspective on how teams organise and measure work, workforce optimization software.
For independent background and broader industry context, see PostgreSQL.
The layers
Account — the container for billing, projects and team members. Named for your company or team.
Project — a distinct set of data with its own API keys. Separate applications, or separate environments, get separate projects.
Object class — a grouping within a project. A new project gets a default one named _undefined_ so you can start immediately.
Object — the actual thing you read and write, addressed by an id.
Your plan sets limits at each level — projects, keys, object classes, and total objects. Worth looking at before designing, because a scheme that needs six object classes does not fit a plan that allows three.
The object id is the whole design
Everything else is organisation. The id is behaviour.
Two clients with the same id are on the same object. Two clients with different ids are not. That is the entire coordination model, and choosing ids well is most of designing with this service.
Choosing them
Derive from the resource. A document, a room, a poll, a user's preferences. One object per thing that is shared.
Do not use sequential ids for private data. doc-1, user-42, room-3 are enumerable. The id is an address, and a guessable address is a shared one. See from API keys to E2EE.
Do not encode meaning you might change. An id containing a tenant name is a problem when the tenant renames.
Keep them stable. Changing an id creates a new object and abandons the old one.
And prefix by type. doc:abc123 rather than abc123 makes debugging and cleanup considerably easier.
How much to put in one object
The question people ask least and should ask first.
One object per shared thing is the default and it is usually right.
Not one object for everything. A single object holding all users' preferences means every client subscribes to every change, and everyone can read everyone's data.
Not one object per key either. An object per individual preference means many connections and many subscriptions for something that is one logical unit.
The test: does everyone who should see one part of this need to see the rest? Yes — one object. No — split.
Object classes
A grouping mechanism within a project.
Useful for: separating kinds of data with different characteristics — short-lived rooms from long-lived documents, or high-churn from stable.
And for organisation when a project holds several unrelated features.
Do not over-partition. Classes are limited by plan, and a scheme needing many of them will hit that before it hits an object limit.
The default class is fine for a single-purpose project, and using it is not a shortcut you will regret.
TTL
const nls = await createOfflineNonLocalStorage(credentials, {
id: 'my-object-id',
ttl: 60000
})
A design decision, not a cleanup setting.
Three reasons to set one:
Limits. Objects count against your plan. A poll per documentation page, created for a year, is a lot of objects that stopped being interesting in week two.
Privacy. Data you no longer need is exposure you did not need. The strongest security level does not help with data that should have been deleted.
Correctness. Some state is meaningless when stale. A "who is editing" record from yesterday is worse than no record.
Three reasons not to:
It is a document. Long-lived state should not vanish.
It is your record of something. Though it probably should not be — objects are for live shared state, not a system of record.
The natural life is genuinely indefinite — a user's preferences.
Set it where there is a natural end, and be deliberate where there is not.
Environments
Separate projects per environment, not separate object classes or id prefixes.
Because keys are per project, and you do not want a development key that reaches production data.
Check your plan's project limit before assuming you can have three.
If you cannot, prefix ids by environment as a fallback and be aware it is weaker — the same key reaches both.
A worked scheme
For a collaborative document product:
Project: one per environment.
Object classes: documents for long-lived content, sessions for presence and cursors.
Ids: doc:{random-id} for documents, session:{doc-random-id} for the live layer of that document.
TTL: none on documents, short on sessions.
Why split: everyone editing sees both, and they have entirely different lifetimes and churn. Keeping ephemeral cursor traffic out of the document object keeps the document's change stream meaningful.
The short version
Account, project, object class, object — and the object id is the only one that changes behaviour.
Same id means same object. That is the whole coordination model.
Do not use enumerable ids for private data, and prefix by type for your own sake.
One object per shared thing — the test is whether everyone who sees one part needs the rest.
And TTL is a design decision about limits, privacy and staleness, not a tidying-up option.