← Back to list

Why I Built an Application-Level Quota Manager for Laravel

Laravel provides excellent tools for request throttling through its built-in RateLimiter. But while building applications, I kept running…

Mahedi Zaman Zaber · 2026-07-15 00:34 · 0 claps · 3.3 min read
#laravel #php #web-development #open-source #package
Open on Medium ↗
Wiki topics: 🌐 · Web Development 🔓 · Open Source 🏃 · Running & Endurance

Why I Built an Application-Level Quota Manager for Laravel

Laravel provides excellent tools for request throttling through its built-in RateLimiter. But while building applications, I kept running into a different problem.

I didn’t want to limit requests.

I wanted to limit usage.

Think about features like:

  • Monthly PDF exports
  • AI credits and prompt allowances
  • API request budgets
  • Storage upload limits
  • Team creation caps
  • Subscription plan limits
  • Weekly newsletter quotas
  • Daily transaction budgets

These aren’t traditional rate-limiting problems. They’re examples of application-level quotas, where a user or organization is allocated a fixed amount of usage over a defined period.

The Problem

My first implementations looked like many Laravel applications today.

A database table with counters.

Some Cache::increment() calls.

A few Carbon calculations.

Maybe a scheduled command to reset monthly limits.

It worked.

Until it didn’t.

Soon every feature had its own implementation.

Different period calculations.

Different key formats.

Different reset logic.

Different concurrency handling.

And whenever I needed to add another quota-based feature, I found myself copying the same ideas yet again.

I wanted something reusable.

What I Wanted

Before writing any code, I wrote down a few goals.

The solution should:

  • Support both cache and database storage
  • Understand calendar periods naturally
  • Work with Eloquent models
  • Support arbitrary targets such as users, teams, API tokens, and IP addresses
  • Be usable from controllers, jobs, commands, and middleware
  • Prevent concurrent over-consumption
  • Allow custom storage backends
  • Feel natural to use

Most importantly, I wanted the API to read almost like English.

Quota::for('monthly_exports', $user)
    ->limit(50)
    ->perMonth()
    ->consume();

Or simply inspect what’s left.

$remaining = Quota::for('monthly_exports', $user)
    ->remaining();

That became the foundation of the package.

Calendar Periods Shouldn’t Require Date Math

One thing I noticed was how often I was writing Carbon logic just to calculate period boundaries.

Start of month.

End of month.

Start of week.

Custom billing cycles.

Every project had slightly different implementations.

Instead, I wanted period management to become part of the API.

Quota::for('api_requests')
    ->limit(1000)
    ->perDay();

Or:

Quota::for('exports')
    ->limit(50)
    ->perMonth();

Or even:

Quota::for('billing')
    ->period($start, $end);

The application shouldn’t have to worry about timestamps.

It should simply express the business rule.

Using Laravel’s Manager Pattern

Like my other packages, I built Laravel Quota around Laravel’s Illuminate\Support\Manager.

Instead of coupling the package to a single storage implementation, the manager resolves interchangeable storage backends.

Today the package supports:

  • Cache
  • Database

Adding another backend follows Laravel’s familiar extend() pattern, making it straightforward to integrate services such as DynamoDB or MongoDB.

This separation keeps the public API identical regardless of where quota data is stored.

Treating Models as First-Class Targets

Another thing that always felt repetitive was manually constructing quota keys.

Instead of writing something like:

Cache::increment("exports:user:{$user->id}");

I wanted the package to understand models directly.

$user->quota('exports')
    ->limit(50)
    ->perMonth()
    ->consume();

The package automatically resolves unique identifiers for Eloquent models, scalar values, API tokens, IP addresses, and other supported targets.

No manual key generation.

No duplicated naming conventions.

Middleware Shouldn’t Punish Validation Errors

One design decision I spent quite a bit of time thinking about was middleware behavior.

Many quota implementations deduct usage before the controller executes.

Imagine generating a report.

You submit the request.

Validation fails.

You fix a typo.

Now you’ve already lost one of your monthly exports.

That never felt right.

Instead, Laravel Quota only consumes units after successful (2xx) or redirected (3xx) responses.

If validation fails or an exception occurs, users can immediately correct the problem without losing part of their allowance.

It’s a small detail, but one that makes the experience much more user-friendly.

Concurrency Matters

Another issue appears when multiple requests arrive simultaneously.

Suppose a user has one remaining export.

Two requests hit the server at nearly the same time.

Without synchronization, both requests might pass the capacity check before either updates the counter.

Suddenly the user has consumed more than their allowed quota.

To avoid that, Laravel Quota includes atomic execution through block().

Quota::for('monthly_exports', $user)
    ->limit(50)
    ->perMonth()
    ->block(function () {
        // Generate export...
    });

The package temporarily locks the operation, executes the callback, and only deducts quota after successful completion.

That keeps usage accounting consistent even under heavy concurrency.

More Than Just a Counter

Over time the package grew to include the features I repeatedly needed across projects:

  • Cache and Database storage backends
  • Fluent builder API
  • Calendar-based periods
  • Atomic execution
  • Native Eloquent integration
  • Route middleware
  • Immutable DTOs
  • Automatic database pruning
  • Event dispatching
  • Custom storage backend support

The goal wasn’t to replace Laravel’s excellent RateLimiter.

It was to solve a different class of problems: application-level quota management and usage tracking.

Open Source

I eventually decided to package everything into Laravel Quota and release it as open source.

If you’d like to try it, explore the code, or share feedback on the API or architecture, I’d genuinely appreciate it.

GitHub:

https://github.com/zaber-dev/laravel-quota

Open source improves through discussion, and I’d love to hear how you’re currently handling quotas, usage budgets, and subscription limits in your own Laravel applications.


메타데이터
post_id
6fe0c4e1310d
slug
why-i-built-an-application-level-quota-manager-for-laravel-6fe0c4e1310d
url
https://medium.com/@zaberdev/why-i-built-an-application-level-quota-manager-for-laravel-6fe0c4e1310d
canonical_url
https://medium.com/@zaberdev/why-i-built-an-application-level-quota-manager-for-laravel-6fe0c4e1310d
author_url
https://medium.com/@zaberdev
status
ok
fetched_at
2026-07-15 10:15:25