zaber-dev/laravel-quota
Laravel Quota tracks and enforces application-level usage budgets across daily, weekly, monthly, or custom periods. Store quotas in cache or database, attach them to Eloquent models, protect routes with middleware, and extend with custom backends. Complements RateLimiter.
Quota::for('monthly_exports', $user)->limit(50)->perMonth() is a one-line expression of a pricing plan limit. PMs can prototype and iterate on tier boundaries (Free/Pro/Team) without asking engineering for a new subsystem each time the pricing page changes.2xx/3xx responses — failed validation (422) or server errors (500) don't burn a user's allowance. That's a defensible, user-friendly policy we can state publicly, and it removes a common support-ticket category. Similarly, reset()/set()/decrement() give support agents a sanctioned remedy for "I got charged for a broken request."RateLimiter. The decision it supports is the two-layer split: RateLimiter for burst/DDoS protection at the edge, this package for cumulative budget policy at the application layer. Teams that conflate the two ship either brittle throttles or unenforceable quotas.cache driver for high-frequency, low-stakes counters (per-minute API limits); database driver for anything a customer can dispute or that must survive a deploy/restart (billing-tier budgets). Being able to choose per-action (->using('database')) means PMs don't have to pick one policy for all limits.QuotaConsumed/QuotaExceeded/QuotaReset events provide the hook to start feeding that pipeline cheaply.Adopt when:
HasQuotas trait and polymorphic scoping.quota:exports,50,month).QuotaDriverContract / Quota::extend() seam so a future replacement is a one-file change.Look elsewhere or proceed with caution when:
database driver helps across services sharing a relational DB, but there's no language-agnostic API or sidecar. Polyglot or multi-region architectures with strong consistency needs should build or buy a dedicated quota service.block() lock is scoped to the underlying cache store, not a globally consistent distributed lock. Very high concurrency on a single hot key should be load-tested before you trust it.To executives: We need per-user usage limits to sell tiered plans and control AI/compute costs. The build estimate is 2–4 weeks of senior engineering plus permanent maintenance. This package is MIT-licensed, covers the same ground (calendar budgets, persistent storage, automatic expiry, route-level enforcement, audit events), and gets us to a testable limit policy in roughly a day. The realistic risk is maturity: it's a low-adoption package with no commercial backing. We de-risk that by (a) isolating it behind our own thin wrapper and (b) budgeting a fallback fork, which the permissive license allows. Net: we trade a small maintenance bet for 3+ weeks of roadmap.
To engineering: It composes with RateLimiter rather than replacing it — keep the throttle for burst protection, use this for cumulative budgets. It fits your existing idioms: a fluent facade, an Eloquent trait, a middleware alias, config-published drivers, events on Laravel's dispatcher, and model:prune for cleanup. The parts that would be tedious and bug-prone to write — atomic consume-under-lock (block()), success-only middleware deduction, immutable period DTOs, per-action driver selection — are done. You own the integration surface and the driver, not the quota arithmetic. It also ships a Laravel Boost skill, so AI-assisted codegen will follow the package's conventions instead of inventing its own.
Guardrails to agree before adoption: decide per action whether it's cache (fast, lossy on restart) or database (durable, queryable); define the public message for exhausted quotas; wire the three events into monitoring before launch; and keep the package behind our own abstraction so a swap is cheap.
How can I help you explore Laravel packages today?