Skip to content

feat(mw): Rate limit#3463

Merged
RobinTail merged 17 commits into
masterfrom
feat-rate-limit
Jun 16, 2026
Merged

feat(mw): Rate limit#3463
RobinTail merged 17 commits into
masterfrom
feat-rate-limit

Conversation

@RobinTail

@RobinTail RobinTail commented Jun 15, 2026

Copy link
Copy Markdown
Owner

Using well-known solution as an optional peer. Hybrid integration approach as a NativeMiddleware in order to provide flexibility on the factory level, recognizing the possibility of different needs across public/private or commercial/free endpoints of the same APIs.

Summary by CodeRabbit

  • New Features
    • Added a rate-limiting middleware helper with configurable throttling and automatic 429 handling, including rate-limit details on the middleware context.
    • Added a factory shorthand (useRateLimit) to attach rate limiting to endpoints.
    • Re-exported the middleware helper from the package entry point.
  • Tests
    • Added unit tests for middleware creation, option forwarding (including custom handlers), limit vs. pass-through behavior, and context/rate-limit propagation.
  • Chores
    • Updated package metadata to include express-rate-limit as an optional peer dependency (and added it for development).

@RobinTail RobinTail added enhancement New feature or request dependencies Pull requests that update a dependency file labels Jun 15, 2026
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

A new createRateLimitMiddleware function is introduced that lazily loads express-rate-limit as an optional peer, wraps it in ExpressMiddleware with a 429 handler, and surfaces req.rateLimit as typed context. EndpointsFactory gains a useRateLimit shorthand method. The function is publicly re-exported and express-rate-limit is registered as an optional peer dependency.

Changes

Rate limit middleware integration

Layer / File(s) Summary
createRateLimitMiddleware implementation and public export
express-zod-api/src/rate-limit-middleware.ts, express-zod-api/src/index.ts
New module exports createRateLimitMiddleware, which lazily loads express-rate-limit, injects a custom 429 error handler using optionsUsed.message, extracts getKey and resetKey from the limiter, and returns an ExpressMiddleware exposing req.rateLimit as rateLimit: RateLimitInfo in context. Re-exported from the package index.
EndpointsFactory.useRateLimit shorthand
express-zod-api/src/endpoints-factory.ts
Imports createRateLimitMiddleware and adds the public useRateLimit(...args) method that delegates to the private #extend(createRateLimitMiddleware(...args)) mechanism.
Peer dependency registration
express-zod-api/package.json
Adds express-rate-limit ^7.5.1 as an optional peer dependency in peerDependencies and peerDependenciesMeta, and mirrors it in devDependencies.
Test mock setup
express-zod-api/tests/peers-mock.ts
Extends mock infrastructure with limiterApiMock and rateLimitMock definitions and adds "express-rate-limit" case to the loadPeer switch, exporting both mocks for test use.
Test coverage
express-zod-api/tests/rate-limit-middleware.spec.ts, express-zod-api/tests/endpoints-factory.spec.ts
New spec validates middleware instantiation, config forwarding to rateLimit, custom handler function, next() behavior for allowed requests, 429 rejection via custom handler, and ctx.rateLimit population from request-attached data. Factory spec adds .useRateLimit test with spy assertions on call args and middleware count.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • RobinTail/express-zod-api#3465: The main PR's createRateLimitMiddleware relies on loadPeer to lazily load express-rate-limit, and this PR refactors loadPeer to a synchronous CommonJS implementation.
  • RobinTail/express-zod-api#2777: The main PR adds useRateLimit(...) to EndpointsFactory, which delegates to the private #extend mechanism that this PR refactors.

Poem

🐇 Hop, hop, the requests come fast,
But now the rate limiter holds them steadfast.
A 429 waits for those who dare exceed,
While rateLimit context fills every need.
The factory gains a shorthand so neat —
The bunny approves: middleware complete! 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(mw): Rate limit' is directly related to the main change, which is adding rate limiting middleware functionality to the express-zod-api framework.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-rate-limit

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coveralls-official

coveralls-official Bot commented Jun 15, 2026

Copy link
Copy Markdown

Coverage Status

coverage: 100.0%. remained the same — feat-rate-limit into master

Comment thread express-zod-api/src/rate-limit-middleware.ts Outdated
RobinTail added a commit that referenced this pull request Jun 15, 2026
This is now possible because we dropped Node 20 in v28 ( #3242 ) and
`require(ESM)` is available since 22.12 without flags. So we can do
dynamic module import without async.

Could benefit in #3463 

A semi-breaking change to `Integration::create()` is postponed until
next major.

⌛ Mocking peers in tests requires a rework (in progress)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Refactor**
* Peer dependency loading is now synchronous, simplifying server startup
flow.
* Middleware setup for cookies and uploads now runs synchronously during
initialization.
* `Integration` TypeScript configuration is optional; prefer `new
Integration(...)` over the deprecated `Integration.create()`.

* **Tests**
* Updated integration and peer-helper tests to reflect synchronous
initialization/loader behavior.
  * Adjusted and streamlined mocks for peers used in tests.

* **Documentation**
* Updated the README “End-to-End Type Safety” example and added a
`v28.6.0` changelog entry.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@RobinTail RobinTail marked this pull request as ready for review June 15, 2026 19:13

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ One minor suggestion inline.

Reviewed changes — adds express-rate-limit as an optional peer dependency with a createRateLimitMiddleware() wrapper and useRateLimit() shorthand on EndpointsFactory.

  • Add express-rate-limit optional peer — declared in package.json with optional: true.
  • New createRateLimitMiddleware() — wraps express-rate-limit via ExpressMiddleware, overriding its handler to throw createHttpError(429) and exposing rateLimit info on the context.
  • Add EndpointsFactory#useRateLimit() shorthand — mirrors the existing useCookies() / useCache() pattern.
  • Tests — middleware creation, config forwarding, success, rate-limit-exceeded, and context-population tests via testMiddleware; factory shorthand test in endpoints-factory.spec.ts.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

Comment thread express-zod-api/src/rate-limit-middleware.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@express-zod-api/src/rate-limit-middleware.ts`:
- Around line 23-24: The provider function at line 23-24 in the
rate-limit-middleware.ts file hardcodes the property name as `req.rateLimit`,
but it should instead use the configurable requestPropertyName value that is
read on line 17. When a user customizes requestPropertyName to a different
value, the hardcoded access to req.rateLimit will be undefined while the actual
rate limit info resides at the custom property name. Change the provider
function to dynamically access the rate limit property using the
requestPropertyName config value instead of the hardcoded string "rateLimit".

In `@express-zod-api/tests/endpoints-factory.spec.ts`:
- Around line 18-24: The test defines a custom vi.mock for ../src/peer-helpers
with a loadPeer function that only handles the "express-rate-limit" module and
throws errors for other peers, making the test brittle when multiple peer-backed
middleware paths execute in the same file. Replace this local mock with the
shared peer mock that handles multiple peer dependencies, so that any peer
module resolution in this test suite works correctly without brittle error
handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d7fae9f2-99c3-4f94-934c-4cd582a42285

📥 Commits

Reviewing files that changed from the base of the PR and between e05ac66 and 2411418.

⛔ Files ignored due to path filters (2)
  • express-zod-api/tests/__snapshots__/index.spec.ts.snap is excluded by !**/*.snap
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (7)
  • express-zod-api/package.json
  • express-zod-api/src/endpoints-factory.ts
  • express-zod-api/src/index.ts
  • express-zod-api/src/rate-limit-middleware.ts
  • express-zod-api/tests/endpoints-factory.spec.ts
  • express-zod-api/tests/peers-mock.ts
  • express-zod-api/tests/rate-limit-middleware.spec.ts

Comment thread express-zod-api/src/rate-limit-middleware.ts Outdated
Comment thread express-zod-api/tests/endpoints-factory.spec.ts Outdated

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed changes — two commits since the prior Pullfrog review fix the hardcoded requestPropertyName bug and simplify the return type annotation on createRateLimitMiddleware.

  • Respect requestPropertyName option — provider now reads req[options?.requestPropertyName ?? "rateLimit"] instead of hardcoded req.rateLimit, matching the configured express-rate-limit property name.
  • Simplified return type — removed explicit ExpressMiddleware<Request, Response, …> annotation in favor of TypeScript inference, eliminating unused imports (Request, Response, RateLimitInfo).
  • Prior JSDoc feedback addressed — JSDoc block with @desc, @requires, @param, and @example was added in a prior commit.

Pullfrog  | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes — two commits since the prior Pullfrog review rename the local variable for clarity and expose getKey/resetKey limiter API methods on the middleware context for programmatic rate limit management.

  • Renamed handlerlimiter — the variable IS the rate limiter instance; the new name accurately reflects what it holds.
  • Exposed getKey and resetKey on context — the provider now spreads limiterApi ({ getKey, resetKey } extracted from the limiter) before the req.rateLimit info, giving endpoint handlers programmatic access to query/reset rate limit state. The type assertion RateLimitInfo & typeof limiterApi accurately describes the merged object.

Pullfrog  | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ One minor suggestion on the CHANGELOG punctuation.

Reviewed changes — adds the CHANGELOG entry and README section for rate limiting, plus import cleanups in the Cookies and Caching examples.

  • CHANGELOG v28.7.0 entry — documents rate limiting support with nested bullets covering the peer dependency, the new method/shorthand, 429 behavior, and context properties.
  • README "Rate limiting" section — inserted after Caching with a useRateLimit() example showing buildVoid and rateLimit destructuring, matching existing section style.
  • README import cleanups — removed createCookieMiddleware from the Cookies section import and createCacheMiddleware from the Caching section import, since both examples now use factory shorthands.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

Comment thread CHANGELOG.md
@RobinTail RobinTail added the documentation Improvements or additions to documentation label Jun 16, 2026

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes — adds rate limiting to the example/ app, updates the README with a new "Rate limiting" section, and refreshes the AGENTS.md peers-mock description.

  • Example factory updatedkeyAndTokenAuthenticatedEndpointsFactory now chains .useRateLimit() before .addMiddleware(), demonstrating the new shorthand in practice.
  • Example tests extended — the existing PATCH test now validates rate limit response headers (X-RateLimit-Limit, X-RateLimit-Remaining); a new Rate limiting describe block sends enough requests to trigger a 429 and asserts the error response body and headers.
  • README expanded — new "Rate limiting" section with useRateLimit() example and rateLimit context destructuring; TOC renumbered; import cleanups in Cookies and Caching sections.
  • AGENTS.md updatedexpress-rate-limit added to the peers-mock.ts entry point description.
  • Dev dependency addedexpress-rate-limit pinned in express-zod-api/package.json devDependencies for testing.

Pullfrog  | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

@RobinTail RobinTail merged commit 557af50 into master Jun 16, 2026
14 checks passed
@RobinTail RobinTail deleted the feat-rate-limit branch June 16, 2026 10:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant