Skip to content

feat(typescript): add passthrough fetch method to generated SDK clients#12920

Merged
Swimburger merged 11 commits intomainfrom
devin/1772142208-passthrough-fetch
Mar 13, 2026
Merged

feat(typescript): add passthrough fetch method to generated SDK clients#12920
Swimburger merged 11 commits intomainfrom
devin/1772142208-passthrough-fetch

Conversation

@Swimburger
Copy link
Member

@Swimburger Swimburger commented Feb 26, 2026

Description

Refs #5333

Adds a passthrough fetch method to the root client class of every generated TypeScript SDK. This lets users make arbitrary HTTP requests through the SDK client—leveraging configured auth, base URL, retry, timeout, logging, and headers—for endpoints not yet exposed in the SDK definition (e.g., new public endpoints, closed beta endpoints).

The method signature matches the native fetch API, accepting Request | string | URL as the first argument.

Link to Devin run: https://app.devin.ai/sessions/766ccf76c0b44f168c976ca82bc06efe
Requested by: @Swimburger

Usage

// With a URL string
const response = await client.fetch(
    "/v1/models/meta/llama-2-70b-chat",
    {
        method: "GET",
        cache: "no-cache",
        credentials: "same-origin",
    },
    {
        timeoutInSeconds: 30,
        maxRetries: 3,
        headers: { "Custom-Header": "value" },
        abortSignal: signal,
    },
);
const data = await response.json();

// With a Request object
const request = new Request("/v1/models/meta/llama-2-70b-chat", { method: "POST", body: "..." });
const response = await client.fetch(request);

// With a URL object
const response = await client.fetch(new URL("https://api.example.com/v1/resource"));

Changes Made

  • New runtime utility (generators/typescript/utils/core-utilities/src/core/fetcher/makePassthroughRequest.ts): Core implementation that accepts Request | string | URL, resolves base URLs, merges headers (SDK defaults → auth → init → requestOptions), delegates to existing makeRequest/requestWithRetries/getFetchFn infrastructure, and returns a standard Response. When a Request object is passed without explicit init, its properties (method, headers, body, signal, credentials, etc.) are extracted automatically; if both are provided, init takes precedence (matching native fetch behavior).
  • Generator update (GeneratedSdkClientClassImpl.ts): Adds addPassthroughFetchMethod that generates a public async fetch(input: Request | string | URL, init?, requestOptions?) method on root client classes only. Conditionally wires auth headers when the client has an auth provider. Detects multi-URL environments and omits the environment property to avoid passing an object where a string is expected.
  • README.md generator updated (ReadmeSnippetBuilder.ts, features.yml): Added CUSTOM_FETCH feature with documentation snippet showing passthrough fetch usage. All generated READMEs now include a "Custom Fetch" section under Advanced.
  • Unit tests (core-utilities/tests/unit/fetcher/makePassthroughRequest.test.ts): 23 test cases covering URL resolution (8 cases including URL object), header merge priority, auth header injection, method/body forwarding, timeout/retry override precedence, abort signal precedence, credentials passthrough, response handling, Request object input handling (3 cases), and SDK default header supplier resolution.
  • Seed snapshots: All ~230 ts-sdk seed fixtures regenerated to include the updated method signature, makePassthroughRequest.ts file, test file, and README documentation.
  • versions.yml: Added v3.55.0 changelog entry.
  • Updated README.md generator (if applicable)

Updates since last revision

  • Request | string | URL input support: The first argument now matches the native fetch signature. When a Request object is passed without init, its properties are extracted. If both are provided, init takes precedence. URL objects are converted to strings. (3 new unit tests added for this behavior.)
  • README.md generator updated: Added CUSTOM_FETCH feature to features.yml and built snippet in ReadmeSnippetBuilder.ts. All generated READMEs now include a "Custom Fetch" section with usage example.
  • All seed snapshots regenerated: 735 files updated across all ts-sdk fixtures to reflect the new signature, README documentation, and test coverage.
  • Snapshot test fixed: Updated ReadmeSnippetBuilder.test.ts.snap to include the new CUSTOM_FETCH feature in the comprehensive feature set snapshot.

Human review checklist

Items that warrant close attention during review:

  1. Request object body handling: When a Request object is passed, input.body (a ReadableStream | null) is extracted and passed to makeRequest. If the Request body has already been consumed, this will fail. Native fetch has the same limitation. Consider whether we should document this or add a check.
  2. cache: input.cache as RequestCache cast: The cast on line 78 of makePassthroughRequest.ts is suspicious—Request.cache should already be RequestCache type. Verify this is necessary or remove it.
  3. Request + init precedence: When both a Request object and init are provided, only the URL from the Request is used; all other properties come from init. This matches native fetch behavior but may be surprising. The unit tests verify this.
  4. Multi-URL environment handling: The environment option is omitted for multi-URL SDKs. Passthrough fetch with relative paths will only work if baseUrl is configured. Is this the right UX, or should a warning/error be surfaced?
  5. Header merge order: SDK defaults → auth headers → init.headersrequestOptions.headers. All keys are lowercased. Verify this priority is correct (unit tests validate this).
  6. Auth extraction: Only .headers from authProvider.getAuthRequest() is used. getAuthRequest() is called without endpointMetadata since passthrough requests have no endpoint metadata. Verify this is acceptable for all auth schemes (bearer, basic, header, OAuth, inferred).
  7. Abort signal precedence: requestOptions.abortSignal wins over init.signal; they are not combined. If a user passes both, the init signal is silently ignored.
  8. EndpointSupplier.get(value, { endpointMetadata: {} }) passes an empty metadata object when resolving default headers—confirm this is safe for all supplier types.

Testing

  • All 229 ts-sdk seed fixtures pass (pnpm seed test --generator ts-sdk)
  • Multi-URL environment fixtures (multi-url-environment, multi-url-environment-no-default, server-url-templating) re-verified after fix
  • Unit tests added/updated — 23 test cases added for makePassthroughRequest runtime utility (including 3 for Request/URL object support)
  • Snapshot tests updated — ReadmeSnippetBuilder.test.ts.snap updated to include CUSTOM_FETCH feature
  • All 291 CI checks pass
  • Manual testing completed — not performed; requires a live API to test against

Co-Authored-By: Niels Swimberghe <3382717+Swimburger@users.noreply.github.com>
@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@github-actions
Copy link
Contributor

🌱 Seed Test Selector

Select languages to run seed tests for:

  • Python
  • TypeScript
  • Java
  • Go
  • Ruby
  • C#
  • PHP
  • Swift
  • Rust
  • OpenAPI
  • Postman

How to use: Click the ⋯ menu above → "Edit" → check the boxes you want → click "Update comment". Tests will run automatically and snapshots will be committed to this PR.

devin-ai-integration[bot]

This comment was marked as resolved.

…etch

Co-Authored-By: Niels Swimberghe <3382717+Swimburger@users.noreply.github.com>
devin-ai-integration bot and others added 4 commits February 27, 2026 03:18
Co-Authored-By: Niels Swimberghe <3382717+Swimburger@users.noreply.github.com>
Co-Authored-By: Niels Swimberghe <3382717+Swimburger@users.noreply.github.com>
Co-Authored-By: Niels Swimberghe <3382717+Swimburger@users.noreply.github.com>
Co-Authored-By: Niels Swimberghe <3382717+Swimburger@users.noreply.github.com>
@Swimburger Swimburger enabled auto-merge (squash) February 27, 2026 04:28
@Swimburger Swimburger disabled auto-merge February 27, 2026 04:28
devin-ai-integration bot and others added 3 commits March 13, 2026 16:46
Co-Authored-By: Niels Swimberghe <3382717+Swimburger@users.noreply.github.com>
Co-Authored-By: Niels Swimberghe <3382717+Swimburger@users.noreply.github.com>
Co-Authored-By: Niels Swimberghe <3382717+Swimburger@users.noreply.github.com>
Comment on lines +37 to +38
url: string,
init?: RequestInit,
Copy link
Member Author

Choose a reason for hiding this comment

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

I think native fetch also allows you not to provide the URL in the first arg, instead pass in a request object that itself contains the URL, is that right?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, native fetch accepts RequestInfo | URL as the first arg, where RequestInfo = Request | string. So you can do fetch(new Request("/path", { method: "POST", body: "..." })).

Currently our implementation only accepts string. Supporting Request objects would add complexity since we'd need to extract and merge the URL, headers, method, body, etc. from the Request object with our SDK-level overrides.

Want me to add Request object support to match the full native fetch signature, or is string-only sufficient for the initial release?

Copy link
Member Author

Choose a reason for hiding this comment

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

No, support both!

Copy link
Contributor

Choose a reason for hiding this comment

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

Done! The fetch method now accepts Request | string | URL as the first argument, matching the native fetch signature. When a Request object is passed without an explicit init, its properties (method, headers, body, signal, credentials, etc.) are extracted automatically. If both a Request and init are provided, init takes precedence (same as native fetch behavior).

Updated: runtime implementation, generated client code, unit tests (3 new test cases for Request/URL), and all seed snapshots.

devin-ai-integration bot and others added 2 commits March 13, 2026 20:34
… for passthrough fetch

Co-Authored-By: Niels Swimberghe <3382717+Swimburger@users.noreply.github.com>
Co-Authored-By: Niels Swimberghe <3382717+Swimburger@users.noreply.github.com>
@Swimburger Swimburger merged commit 4b8e7f5 into main Mar 13, 2026
299 checks passed
@Swimburger Swimburger deleted the devin/1772142208-passthrough-fetch branch March 13, 2026 22:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants