Skip to content

feat(client): Retry After Plugin#1190

Merged
dinwwwh merged 2 commits intomainfrom
unnoq/issue1181
Nov 9, 2025
Merged

feat(client): Retry After Plugin#1190
dinwwwh merged 2 commits intomainfrom
unnoq/issue1181

Conversation

@dinwwwh
Copy link
Copy Markdown
Member

@dinwwwh dinwwwh commented Nov 7, 2025

A plugin auto retry base on Retry-After response header.

Fixes #1181

Summary by CodeRabbit

  • New Features

    • Introduced a Retry After Plugin for clients enabling automatic retries with configurable conditions, max attempts, and timeouts.
  • Documentation

    • Added Retry After Plugin docs with examples.
    • Updated rate-limit handler docs to note integration with the Retry After Plugin.
    • Navigation updated to include the new plugin docs.
  • Tests

    • Added comprehensive tests covering retry behavior, parsing, timeouts, custom conditions, and abort handling.
  • Bug Fixes

    • Aligned rate-limit status handling with shared definitions.

A plugin auto retry base on `Retry-After` response header.

Fixes #1181
@dinwwwh dinwwwh requested a review from Copilot November 7, 2025 11:45
@vercel
Copy link
Copy Markdown

vercel bot commented Nov 7, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
orpc Ready Ready Preview Comment Nov 7, 2025 1:37pm

@dosubot dosubot bot added the size:XL This PR changes 500-999 lines, ignoring generated files. label Nov 7, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Nov 7, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Pre-merge checks and finishing touches

✅ 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 accurately describes the main addition: a new Retry After Plugin for the client.
Linked Issues check ✅ Passed The PR implements all coding requirements from #1181: retry logic with Retry-After header parsing, timeout mechanism, custom condition support, maxAttempts configuration, comprehensive test suite, and documentation.
Out of Scope Changes check ✅ Passed All changes are in scope. Documentation updates (config, docs page, ratelimit handler note) directly support the feature. The plugin implementation and tests address the stated objectives without unrelated modifications.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch unnoq/issue1181

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f8d707c and 9469f48.

📒 Files selected for processing (1)
  • apps/content/docs/plugins/retry-after.md (1 hunks)
🧰 Additional context used
🪛 LanguageTool
apps/content/docs/plugins/retry-after.md

[uncategorized] ~8-~8: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...his is particularly useful for handling rate limiting and temporary server unavailability. #...

(EN_COMPOUND_ADJECTIVE_INTERNAL)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: lint
  • GitHub Check: publish-commit
  • GitHub Check: test
🔇 Additional comments (1)
apps/content/docs/plugins/retry-after.md (1)

1-40: Documentation looks great—duplicate word issue has been resolved.

The documentation clearly explains the RetryAfterPlugin, its purpose, usage, and all configurable options with sensible defaults. The code example is accurate and demonstrates how to configure the plugin with custom options. All requirements from the PR objectives are covered (retry condition, timeout, max attempts, etc.).

Note: The static analysis tool flagged a potential compound adjective hyphenation on line 8 ("rate limiting"), but this is a false positive—"rate limiting" functions as a noun phrase in the context "useful for handling rate limiting and temporary server unavailability," not as a compound adjective modifying another noun. No changes needed.


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.

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @unnoq, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant enhancement to the client-side request handling by adding a new RetryAfterPlugin. This plugin intelligently manages automatic retries for requests that encounter temporary server issues or rate limiting, leveraging the standard Retry-After HTTP header. It aims to improve the resilience and user experience of applications built with the client by reducing the need for manual retry logic and gracefully handling transient network or server conditions.

Highlights

  • New RetryAfterPlugin: Introduced a new client-side plugin that automatically retries requests based on the Retry-After HTTP response header.
  • Configurable Retry Logic: The plugin allows customization of retry conditions (defaulting to 429 and 503 status codes), maximum retry attempts, and an overall timeout for retries.
  • Comprehensive Testing: A dedicated test suite has been added to ensure the robustness and correct behavior of the RetryAfterPlugin across various scenarios, including different Retry-After header formats, maxAttempts, timeout, custom conditions, and AbortSignal handling.
  • Documentation and Integration: New documentation for the RetryAfterPlugin has been added, and existing documentation for the ratelimit helper has been updated to suggest combining it with the new retry plugin.
  • Codebase Refinement: The ratelimit handler plugin now uses COMMON_ORPC_ERROR_DEFS for status code comparison, improving consistency and maintainability.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new RetryAfterPlugin for the client, which is a valuable addition for automatically handling rate-limited requests. The implementation is robust, well-tested, and includes comprehensive documentation. My review includes a minor correction for a typo in the documentation and a more significant point about a new dependency introduced in the ratelimit package, which could be an architectural concern.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR adds a new RetryAfterPlugin for automatic client-side request retries based on server Retry-After headers, commonly used for rate limiting and temporary service unavailability scenarios.

  • Implements a configurable retry plugin with support for custom conditions, maximum attempts, and timeout settings
  • Updates the ratelimit handler to use error status constants instead of magic numbers
  • Includes comprehensive test coverage and documentation

Reviewed Changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
packages/client/src/plugins/retry-after.ts New plugin implementation with retry logic based on Retry-After headers
packages/client/src/plugins/retry-after.test.ts Comprehensive test suite covering core behavior, parsing, limits, and signal handling
packages/client/src/plugins/index.ts Export statement for new plugin
packages/ratelimit/src/handler-plugin.ts Replace magic number 429 with constant from COMMON_ORPC_ERROR_DEFS
apps/content/docs/plugins/retry-after.md Documentation for the new plugin
apps/content/docs/helpers/ratelimit.md Cross-reference to retry-after plugin
apps/content/.vitepress/config.ts Navigation entry for plugin documentation

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@codecov
Copy link
Copy Markdown

codecov bot commented Nov 7, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Nov 7, 2025

More templates

@orpc/ai-sdk

npm i https://pkg.pr.new/@orpc/ai-sdk@1190

@orpc/arktype

npm i https://pkg.pr.new/@orpc/arktype@1190

@orpc/client

npm i https://pkg.pr.new/@orpc/client@1190

@orpc/contract

npm i https://pkg.pr.new/@orpc/contract@1190

@orpc/experimental-durable-iterator

npm i https://pkg.pr.new/@orpc/experimental-durable-iterator@1190

@orpc/hey-api

npm i https://pkg.pr.new/@orpc/hey-api@1190

@orpc/interop

npm i https://pkg.pr.new/@orpc/interop@1190

@orpc/json-schema

npm i https://pkg.pr.new/@orpc/json-schema@1190

@orpc/nest

npm i https://pkg.pr.new/@orpc/nest@1190

@orpc/openapi

npm i https://pkg.pr.new/@orpc/openapi@1190

@orpc/openapi-client

npm i https://pkg.pr.new/@orpc/openapi-client@1190

@orpc/otel

npm i https://pkg.pr.new/@orpc/otel@1190

@orpc/experimental-publisher

npm i https://pkg.pr.new/@orpc/experimental-publisher@1190

@orpc/experimental-ratelimit

npm i https://pkg.pr.new/@orpc/experimental-ratelimit@1190

@orpc/react

npm i https://pkg.pr.new/@orpc/react@1190

@orpc/react-query

npm i https://pkg.pr.new/@orpc/react-query@1190

@orpc/experimental-react-swr

npm i https://pkg.pr.new/@orpc/experimental-react-swr@1190

@orpc/server

npm i https://pkg.pr.new/@orpc/server@1190

@orpc/shared

npm i https://pkg.pr.new/@orpc/shared@1190

@orpc/solid-query

npm i https://pkg.pr.new/@orpc/solid-query@1190

@orpc/standard-server

npm i https://pkg.pr.new/@orpc/standard-server@1190

@orpc/standard-server-aws-lambda

npm i https://pkg.pr.new/@orpc/standard-server-aws-lambda@1190

@orpc/standard-server-fastify

npm i https://pkg.pr.new/@orpc/standard-server-fastify@1190

@orpc/standard-server-fetch

npm i https://pkg.pr.new/@orpc/standard-server-fetch@1190

@orpc/standard-server-node

npm i https://pkg.pr.new/@orpc/standard-server-node@1190

@orpc/standard-server-peer

npm i https://pkg.pr.new/@orpc/standard-server-peer@1190

@orpc/svelte-query

npm i https://pkg.pr.new/@orpc/svelte-query@1190

@orpc/tanstack-query

npm i https://pkg.pr.new/@orpc/tanstack-query@1190

@orpc/trpc

npm i https://pkg.pr.new/@orpc/trpc@1190

@orpc/valibot

npm i https://pkg.pr.new/@orpc/valibot@1190

@orpc/vue-colada

npm i https://pkg.pr.new/@orpc/vue-colada@1190

@orpc/vue-query

npm i https://pkg.pr.new/@orpc/vue-query@1190

@orpc/zod

npm i https://pkg.pr.new/@orpc/zod@1190

commit: 9469f48

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3312214 and f8d707c.

📒 Files selected for processing (7)
  • apps/content/.vitepress/config.ts (1 hunks)
  • apps/content/docs/helpers/ratelimit.md (1 hunks)
  • apps/content/docs/plugins/retry-after.md (1 hunks)
  • packages/client/src/plugins/index.ts (1 hunks)
  • packages/client/src/plugins/retry-after.test.ts (1 hunks)
  • packages/client/src/plugins/retry-after.ts (1 hunks)
  • packages/ratelimit/src/handler-plugin.ts (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
packages/client/src/plugins/retry-after.test.ts (3)
packages/standard-server/src/types.ts (1)
  • StandardLazyResponse (43-49)
packages/client/src/adapters/standard/link.ts (1)
  • StandardLink (24-109)
packages/client/src/plugins/retry-after.ts (1)
  • RetryAfterPlugin (47-147)
packages/client/src/plugins/retry-after.ts (6)
packages/shared/src/value.ts (1)
  • Value (1-1)
packages/standard-server/src/types.ts (1)
  • StandardLazyResponse (43-49)
packages/client/src/adapters/standard/link.ts (2)
  • StandardLinkClientInterceptorOptions (14-16)
  • StandardLinkOptions (18-22)
packages/client/src/adapters/standard/plugin.ts (1)
  • StandardLinkPlugin (4-7)
packages/client/src/error.ts (1)
  • COMMON_ORPC_ERROR_DEFS (5-83)
packages/standard-server/src/utils.ts (1)
  • flattenHeader (51-61)
packages/ratelimit/src/handler-plugin.ts (1)
packages/client/src/error.ts (1)
  • COMMON_ORPC_ERROR_DEFS (5-83)
🪛 LanguageTool
apps/content/docs/plugins/retry-after.md

[uncategorized] ~8-~8: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...his is particularly useful for handling rate limiting and temporary server unavailability. #...

(EN_COMPOUND_ADJECTIVE_INTERNAL)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Upload results
  • GitHub Check: publish-commit
  • GitHub Check: lint
  • GitHub Check: test
🔇 Additional comments (16)
packages/ratelimit/src/handler-plugin.ts (1)

4-4: LGTM! Good refactor to use centralized error definitions.

Replacing the hardcoded 429 with COMMON_ORPC_ERROR_DEFS.TOO_MANY_REQUESTS.status improves maintainability and aligns with the new RetryAfter plugin's approach.

Also applies to: 52-52

packages/client/src/plugins/index.ts (1)

4-4: LGTM! Export follows existing patterns.

The new export is consistent with other plugin exports in this barrel file.

apps/content/docs/helpers/ratelimit.md (1)

227-229: LGTM! Good cross-reference to related plugin.

The info block effectively guides users to the complementary Retry After Plugin, improving discoverability of synergistic features.

apps/content/.vitepress/config.ts (1)

152-152: LGTM! Navigation entry properly configured.

The Retry After plugin entry is correctly placed in the Plugins section with the appropriate link path.

apps/content/docs/plugins/retry-after.md (2)

12-28: LGTM! Clear and comprehensive example.

The usage example demonstrates all key features of the plugin with helpful inline comments.


30-36: LGTM! Options are well-documented.

All options are clearly described with their default values and expected behavior.

packages/client/src/plugins/retry-after.test.ts (5)

28-83: LGTM! Core behavior tests are thorough.

The tests effectively cover both successful retry scenarios and edge cases where retries should not occur (missing header, non-retryable status codes).


85-145: LGTM! Comprehensive parsing validation.

The tests cover various Retry-After formats (numeric seconds, HTTP dates, whitespace handling) and properly validate that invalid values prevent retries.


147-182: LGTM! MaxAttempts logic thoroughly tested.

The tests cover default, static, and dynamic maxAttempts configurations, including verification that dynamic functions are invoked with the correct arguments.


184-261: LGTM! Timeout and condition logic well-tested.

The tests validate static and dynamic timeout configurations, as well as custom condition functions, including proper argument passing.


263-306: LGTM! Signal handling is properly tested.

The tests validate both mid-delay abort scenarios and pre-aborted signals, ensuring the plugin respects cancellation requests.

packages/client/src/plugins/retry-after.ts (5)

9-39: LGTM! Well-designed options interface.

The use of Value<T, TArgs> allows both static and dynamic (function-based) configuration, providing excellent flexibility. Documentation clearly specifies defaults.


47-63: LGTM! Constructor properly initializes defaults.

The constructor correctly sets default values matching the documentation, and uses centralized error definitions for status code comparisons.


65-103: LGTM! Retry interceptor logic is solid.

The retry loop correctly:

  • Evaluates the retry condition after each response
  • Respects maxAttempts boundaries
  • Enforces timeout constraints
  • Handles signal cancellation gracefully
  • Returns immediately when retry criteria aren't met

105-123: LGTM! Robust Retry-After header parsing.

The method correctly handles both numeric (seconds) and HTTP-date formats per the HTTP specification, with proper edge case handling for negative values and invalid inputs.


125-146: LGTM! Delay implementation handles cancellation correctly.

The promise-based delay properly:

  • Checks for pre-aborted signals
  • Responds to mid-delay cancellation
  • Cleans up event listeners in both abort and completion paths
  • Uses once: true for automatic cleanup

@dinwwwh dinwwwh added the lgtm This PR has been approved by a maintainer label Nov 7, 2025
@dinwwwh dinwwwh merged commit 0ab5adc into main Nov 9, 2025
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm This PR has been approved by a maintainer size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A link plugin that auto retry if request failed and retry-after is represent,

2 participants