Development
Rate Limiting and Abuse Handling on Shared Objects
A client that can write can write constantly. Where limits belong, why client-side throttling is not one of them, and the two abuse cases that catch people.
Shared state has a property that local state does not: the person writing to it may not be on your side.
For a separate operational perspective on how teams organise and measure work, the reference.
For independent background and broader industry context, see Fastly.
The two limits that exist
The service's. Your plan has limits on objects, connections, requests and storage. Exceeding them is an operational problem — the service protects itself, and your users see failures.
Yours. What you consider acceptable behaviour from one client. Nothing enforces this unless you build it.
The first does not substitute for the second. A single abusive client can consume your plan's capacity while staying within the service's per-request limits, and everyone else suffers.
Client-side throttling is not a limit
Throttle cursor updates, debounce typing indicators, batch writes. All good practice, and all of it runs on the client's machine.
Someone who opens devtools removes it in a minute. So it is an optimisation, not a control.
Which means the question is: what stops someone who has removed it?
Where a real limit lives
The token endpoint
The one place you fully control.
Rate limit token issuance per user. Someone requesting hundreds of tokens is either broken or hostile, and both warrant a limit.
Scope tokens narrowly — this user, this object, short expiry. A token that reaches one object for ten minutes limits the damage from a leaked one.
And revoke. Have a way to stop issuing to a user, and know how quickly existing tokens expire, because that is your actual response time. See the three authentication methods.
Server-side writes for anything that matters
If a value must not be written arbitrarily, do not let clients write it.
Clients write intent to one place; your backend validates and writes the authoritative value to another. More machinery, and it is the only arrangement that holds.
This is the same conclusion as do you need a backend — if a user changing it in devtools would matter, it goes through your server.
Structure that limits blast radius
Design so one client cannot affect much.
One key per participant, so a client can only corrupt its own. This is the same pattern that avoids the counter race, and it turns out to solve two problems.
Separate objects for separate concerns, so a busy ephemeral channel does not degrade the document.
Short TTLs on anything user-created, so abuse expires.
The two cases that catch people
Object creation
If clients choose object ids, a client can create objects.
A loop creating thousands consumes your plan's object allowance, and cleanup is manual.
The fix: your backend decides which object ids exist. The token endpoint issues tokens only for objects it created, and a request for an unknown id is refused rather than creating one.
This is the most damaging and least anticipated abuse case, because the naive design lets the client name the object.
Connection count
One client instance is one connection, and nothing stops a script opening many.
Limits are per plan, so exhausting them affects your real users.
Rate limit token issuance, which is what gates connections, since a connection needs a token.
Detecting it
Watch token issuance rate per user. The clearest signal you have, and it is on your own infrastructure.
Watch object count against your plan, with an alert well before the limit.
Watch for objects nobody reads — created and abandoned is the signature of automated creation.
And log enough to identify the source. Which user, which object, when. Without it you know you have a problem and not whose.
Responding
Rate limit before blocking. Most excess is a bug in someone's integration rather than an attack, and a limit gives them a clear signal.
Return a clear error. A 429 with a retry hint lets a well-behaved client back off. A silent failure produces a retry loop that makes it worse.
Have a switch. The ability to stop issuing tokens to a user, or entirely, without a deploy. A feature flag is a reasonable mechanism — see feature flags.
And a cleanup path for objects created abusively.
Proportion
Most applications will never see abuse. An internal tool, a small product, a docs widget.
Do the cheap things regardless: server-controlled object ids, per-key-per-participant structure, TTLs, and token rate limiting. Together that is an afternoon and it removes the damaging cases.
Do the rest when you have users you did not personally invite.
The short version
Client-side throttling is an optimisation, not a control — it runs on their machine.
The token endpoint is the one place you fully control, so limits belong there.
Do not let clients choose object ids. Client-named objects means clients can create objects, which is the least anticipated and most damaging case.
One key per participant limits blast radius — the same pattern that avoids the counter race.
And rate limit before blocking, because most excess is a bug rather than an attack.