Design for the Limits
Write code that stays fast inside the platform's limits.
Every limit — plans, database, files, built-in features, spending caps — is listed once on Limits. This page is about writing code that fits them. The agent already follows these rules; they matter when you change code yourself.
#Database
| Limit (from Limits) | How to design for it |
|---|---|
| One row, string or value up to 2 MB | Keep files in file storage and save only their key in the row |
| 100 bound parameters per query | Insert many rows in chunks (with 5 columns, at most 20 rows per INSERT) |
| 1,000 queries per request, 30 s per query | Read a page in one round trip (db.batch), paginate lists, never query inside a loop |
| About 300 queries a second per database copy | Index the columns you filter and sort on; cache the reads of a busy public page for a few seconds |
| 10 GB per database | Archive or delete old rows, or move to Turso |
Schema changes are Drizzle migrations: change src/db/schema.ts, run npm run db:generate, and the next npm run dev applies the migration to the development database. Publishing applies it to the live one.
#Runtime
The app runs on Cloudflare Workers, not Node: no filesystem at runtime, no long-lived connections, and a request has a short time to answer. Recurring work is a scheduled job; live updates use the SDK's real-time rooms; long work (a large import, many e-mails) is split into jobs that each finish quickly. npm run check runs the build and the Workers checks Totalum runs before deploying.
#Built-in features
The app's calls to built-in features (AI, e-mail, scraping…) count against the plan's quotas and the project's budgets (Limits). Handle 429 answers: wait for Retry-After on a per-minute limit, and show a clear message for a monthly one (errorDetails.resetsAt). Never retry in a tight loop: a burst of requests can trip the emergency stop.
