jacyimp/api-platform-rate-limiter
A Laravel/PHP rate limiter for API platforms, providing middleware-driven request throttling to protect endpoints and control traffic. Lightweight, easy to integrate, and configurable to fit existing API stacks.
Architecture fit. The package sits in the request-lifecycle rate-limiting layer of a Laravel application, attaching RateLimit metadata to resource definitions (extraProperties) and resolving limits per operation. v0.2.0 moves it toward a leaner, declarative configuration model: limits are plain metadata objects composed additively (resource-level + operation-level now combine rather than override), dynamic values are expressed as class-string<T> resolvers, and buckets are declared explicitly via bucketResolver. That is a better fit for a conventional Laravel service than the previous abstraction stack, because it removes bespoke value objects (DynamicLimit, DynamicCost, DynamicBucket, Identity, Condition, Interval) that would otherwise leak into domain code and be hard to test or document.
Integration feasibility. Integration is low-ceremony for greenfield work: a RateLimit object list in extraProperties, string or native DateInterval intervals, no required third-party packages. The optional symfony/security-core dependency is now genuinely optional, with a clean fallback from authenticated-user identity to client IP — that meaningfully widens the addressable set of Laravel apps, since most Laravel apps authenticate via Laravel's own guards, not Symfony Security.
Technical risk. The release is entirely breaking and pre-1.0, so the risk is concentrated in migration rather than runtime behavior:
DynamicLimit / DynamicCost / DynamicBucket / Identity / Condition usage must be rewritten as resolver class names or bucketResolver options. This is a mechanical-but-total rewrite of limit definitions.RateLimitChecking loses that hook with no stated replacement. This is the highest-risk item: it is a silent behavioral loss, not a compile-time error.Interval implementations must become strings/DateInterval; any code type-hinting Interval breaks.Key questions.
RateLimitChecking lifecycle event, or an intended extension point for pre-check logic?class-string<T>) stable and container-resolved (so they can take dependencies), and is the T resolved at compile time or runtime?bucketResolver required whenever multiple tenants share a limit, or does it default to something safe?Stack fit. Good for a Laravel service already using attribute/metadata-driven resource definitions. The package now has essentially one concept to teach (a RateLimit list) plus two extension hooks (resolver class, bucket resolver). No Symfony dependency required; keep symfony/security-core out of composer.json unless we specifically want authenticated-user identity keying — and confirm Laravel guard users are handled acceptably by the IP fallback, which is likely not what we want for authenticated APIs.
Migration path. Treat as an in-place rewrite of the rate-limit configuration, not a version bump:
RateLimitChecking listener.class-string<T> contract; unit-test each in isolation.DateInterval.RateLimitChecking hook, or explicitly accept the loss and document it.Compatibility. Pre-1.0 with an all-breaking release: assume no compatibility guarantees. Pin exactly (0.2.* at most) and avoid adopting across multiple services until the semantics settle. Because the removed abstractions were public API, any third-party code or internal shared package touching them is a blocker, not a warning.
Sequencing. Do this on one representative service first, ideally one with both tenant-scoped and per-operation limits so the bucket/identity clarification is exercised. Only after that service has run in production through a full quota cycle (hourly and daily windows) should we template the change for the rest. Defer broad rollout until the RateLimitChecking question is resolved — if we need that hook and there's no replacement, we may be stranded on the old version or forced to fork.
Maintenance. Net positive on an ongoing basis: fewer moving parts, less metadata boilerplate, one obvious way to declare limits. The trade is a one-time rewrite cost plus the risk of the resolver contracts shifting again before 1.0. Budget for a re-review at each minor release until 1.0.
Support. Troubleshooting gets easier in one respect (declared limits are readable in one place) and harder in another (behavior is now spread across resolver classes that are referenced by name and may not be obvious from the resource definition). On-call needs a way to see the effective resolved limits and bucket key for a given request — ask for or build a debug/introspection path, since the combine semantics make static reading unreliable.
Scaling. Resolver-based dynamic limits are the right shape for scaling: they let limits key off tenant, plan, or cost without the package dictating the model. Bucket-vs-identity semantics being clarified matters here — get the bucket key right or tenant limits will bleed across tenants (or fail to share a pool when they should). Resolver execution is now in the hot path for every limited request; verify resolvers are cheap or cached, and that they don't do I/O per request.
Failure modes.
RateLimitChecking removed): any logic living there (logging, tenant override, bypass rules) simply stops running.Ramp-up. Low for developers writing limits (the example is self-explanatory). Moderate for whoever owns the resolvers and bucket semantics — that is the real skill to transfer, and it should be one named owner with reviewed tests rather than spread across the team. Plan roughly one sprint for migration plus a monitoring window covering at least the longest interval in use (one day at minimum) before declaring the cutover done.
How can I help you explore Laravel packages today?