Skip to content

feat: support multiple REST URIs#64

Merged
beer-1 merged 2 commits into
mainfrom
feat/rest-uri-fallback
May 6, 2026
Merged

feat: support multiple REST URIs#64
beer-1 merged 2 commits into
mainfrom
feat/rest-uri-fallback

Conversation

@beer-1

@beer-1 beer-1 commented May 6, 2026

Copy link
Copy Markdown
Member

Summary

  • Support REST URI config as either a single string or a JSON array of fallback endpoints.
  • Route initia.js REST requests through fallback REST endpoints while keeping the worker API as new RESTClient(chainConfig.restUri, ...).
  • Update config schema, examples, docs, and tests for REST URI arrays.

Why

Some public REST/RPC providers periodically state-sync or reset nodes when they have node issues. When that happens, historical state around the last height used for MsgUpdateClient proof generation can be pruned or unavailable from that endpoint. Even if another provider still has the required state, the relayer previously had no REST fallback path, so update-client generation could fail or keep retrying against an endpoint that can no longer serve the needed height.

Allowing multiple REST URIs lets the relayer fail over to another endpoint that may still retain the required historical state, reducing client update failures caused by provider pruning, state-sync resets, or transient REST node issues.

Validation

  • ./node_modules/.bin/tsc --noEmit --incremental false
  • ./node_modules/.bin/jest src/lib/config.spec.ts src/lib/restClient.spec.ts --runInBand --config '{"preset":"ts-jest","testEnvironment":"node","moduleNameMapper":{"^src/(.*)$":"<rootDir>/src/$1"}}'

Summary by CodeRabbit

  • New Features

    • Added support for multiple REST and RPC endpoints with automatic fallback to alternate endpoints if the primary fails.
    • Configuration now accepts both single URI strings and arrays of URIs for flexible endpoint management.
  • Documentation

    • Updated configuration examples and documentation to reflect multiple URI support.

@coderabbitai

coderabbitai Bot commented May 6, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 749845fd-b343-4fbf-979f-d284c659f909

📥 Commits

Reviewing files that changed from the base of the PR and between 04d5be5 and 48fded4.

📒 Files selected for processing (5)
  • src/lib/config.spec.ts
  • src/lib/config.ts
  • src/lib/restClient.spec.ts
  • src/lib/restClient.ts
  • src/test/testSetup.ts

Walkthrough

Adds support for single or multiple REST/RPC URIs across config, env loading, and runtime. Introduces URI normalization, a RESTClient that accepts multiple endpoints with failover/retry/backoff, updates tests and examples, and adapts test and worker wiring to use the first URI for mocks.

Changes

Multi-Endpoint REST & RPC (single coherent DAG)

Layer / File(s) Summary
Schema
config.schema.json
chain.restUri now accepts either a string or an array of strings (oneOf).
Example config
config.example.json, README.md
Example for chain-2 restUri changed from a single string to an array with a fallback entry; README env examples updated to show JSON array strings for REST_URI and RPC_URI.
Config parsing / Types
src/lib/config.ts, src/lib/config.spec.ts
Added parseUriConfig to trim and normalize URIs from env/config, supporting JSON array strings; ChainConfig.restUri and rpcUri types changed to `string
REST client core
src/lib/restClient.ts
Added RESTUri type alias and refactored RESTClient to accept RESTUri. Implemented normalization to URI array, a centralized request loop with per-endpoint state, failover across endpoints, backoff/retry logic, and requester wiring; added IBC-related API surface and related types.
Runtime wiring
src/workers/index.ts
Replaced APIRequester usage with new RESTClient constructor form and passed options (http/https agents, timeout).
Test harness changes
src/test/testSetup.ts, src/lib/restClient.spec.ts
Added firstUri helper for tests to derive a single base URI from restUri; expanded RESTClient tests validating: first-endpoint success, fallback on server error, no fallback on client errors, and preference for last-successful endpoint; added logger assertions.

Sequence Diagram

sequenceDiagram
    participant Worker as Worker
    participant Client as RESTClient
    participant EP1 as Endpoint 1
    participant EP2 as Endpoint 2
    participant Logger as Logger

    Worker->>Client: instantiate with [EP1, EP2]
    Worker->>Client: request /ibc/...
    Client->>EP1: GET /ibc/...
    EP1-->>Client: 500 Error
    Client->>Logger: log endpoint failure
    Client->>Client: backoff / increment retry
    Client->>EP2: GET /ibc/...
    EP2-->>Client: 200 OK + payload
    Client-->>Worker: return payload
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hop through urls, one, two, and three,
If one is broken, the next answers me,
Retries and backoff, a careful dance,
Endpoints align when I take a chance,
A little rabbit celebrates resilience.

🚥 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: support multiple REST URIs' accurately and clearly describes the primary change—enabling REST endpoint configuration as either a single string or array of fallback URIs.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/rest-uri-fallback

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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

@beer-1 beer-1 requested a review from traviolus May 6, 2026 03:44
@beer-1 beer-1 marked this pull request as ready for review May 6, 2026 03:44

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 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 `@src/lib/config.ts`:
- Around line 34-42: parseUriConfig currently returns whatever safeJsonParse
yields (including malformed bracketed strings or empty arrays) which defers
errors to runtime; update parseUriConfig so that after calling safeJsonParse for
bracketed input you validate the result is an array of non-empty strings and has
at least one element, and if not throw a descriptive error immediately.
Specifically, in the parseUriConfig function (and around the safeJsonParse call)
check Array.isArray(parsed), ensure every item is typeof string and item.trim()
!== '', and throw (not return) for invalid or empty arrays; leave the
non-bracketed trimmedUri path returning the single string as before.

In `@src/lib/restClient.ts`:
- Around line 19-23: The shared currentIndex on RESTRequestState makes endpoint
selection global across concurrent requests; change logic so currentIndex is not
mutated on the shared state during retries: make the retry cursor a local
variable inside the request() function (use a local index/attempt counter) and
only update/publish RESTRequestState.currentIndex or a preferred index after a
successful response; update code that reads currentIndex (e.g., any loops in
request(), getNextUri logic, and the logic around restUris and requesterConfig)
to use the local cursor for attempts and preserve thread-safety for concurrent
calls.
- Around line 141-159: The catch block in the REST client currently retries on
every thrown error; change it to rethrow immediately for deterministic client
errors (HTTP 4xx) so they don't trigger failover/backoff loops. Inside the catch
for the request in restClient.ts (the block that sets errorContext,
logger.error, state.currentIndex, and uses retryCount / MAX_RETRY), inspect the
error object for an HTTP response status (e.g., error.response?.status or
equivalent from your HTTP library) and if the status is in the 400–499 range,
log the errorContext and rethrow the error without advancing state.currentIndex
or initiating backoff; only proceed with the existing failover logic for
network/timeout errors or retryable 5xx responses. Ensure you reference the same
variables used there: state.currentIndex, retryCount, MAX_RETRY, logger, and
errorContext.

In `@src/test/testSetup.ts`:
- Around line 13-15: The helper firstUri currently returns undefined when given
an empty array; update firstUri(uri: string | string[]) to explicitly check
Array.isArray(uri) and if so verify uri.length > 0, otherwise throw a clear
configuration error (e.g., "restUri must contain at least one entry") so callers
that build URLs get an actionable message; ensure the thrown error is used
instead of returning undefined and reference the firstUri function when making
the change.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6a2b824b-08b4-48cd-addb-39f4ea66ab21

📥 Commits

Reviewing files that changed from the base of the PR and between db86db2 and 04d5be5.

📒 Files selected for processing (9)
  • README.md
  • config.example.json
  • config.schema.json
  • src/lib/config.spec.ts
  • src/lib/config.ts
  • src/lib/restClient.spec.ts
  • src/lib/restClient.ts
  • src/test/testSetup.ts
  • src/workers/index.ts

Comment thread src/lib/config.ts
Comment thread src/lib/restClient.ts
Comment thread src/lib/restClient.ts
Comment thread src/test/testSetup.ts

@traviolus traviolus 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.

LGTM!

@beer-1 beer-1 merged commit 2b2f601 into main May 6, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants