Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow providing custom Request and Response #1563

Open
1 task done
Dzieni opened this issue Feb 23, 2024 · 1 comment
Open
1 task done

Allow providing custom Request and Response #1563

Dzieni opened this issue Feb 23, 2024 · 1 comment
Labels
enhancement New feature or request help wanted Extra attention is needed openapi-fetch Relevant to the openapi-fetch library

Comments

@Dzieni
Copy link

Dzieni commented Feb 23, 2024

Description

I am using openapi-fetch in Node.js. As you probably know, the built-in fetch is provided by Undici project, which is still in development. Usually it's better to install undici package separately and have a warranty that fetching works the same, no matter the runtime version. Also, if you want to use more sophisticated features (like mocking or proxy connection), they're not available in the built-in version.

When I tried to use Undici in your app, it broke down due to some inconsistency of Request object behavior (despite both implement the WHATWG spec). Here is a code snippet that shows the inconsistency.

// ran on Undici 6.6.2 and Node 20.11.1 (which bundles Undici 5.28.3)
// interesingly, the problem persists when I change the dependency version to 5.28.3
import { Request as UndiciRequest, fetch as undiciFetch } from "undici";

const req = new Request("http://example.com");
const undiciReq = new UndiciRequest("http://example.com");

await fetch(req.clone())
	.then((res) => {
		res.blob().catch(() => {});
		console.log("global Request + global fetch => works");
	})
	.catch((e) => {
		console.error("global Request + global fetch => broken", e);
	});

await undiciFetch(undiciReq.clone())
	.then((res) => {
		res.blob().catch(() => {});
		console.log("undici Request + undici fetch => works");
	})
	.catch((e) => {
		console.error("undici Request + undici fetch => broken", e);
	});

await undiciFetch(req.clone())
	.then((res) => {
		res.blob().catch(() => {});
		console.log("global Request + undici fetch => works");
	})
	.catch((e) => {
		console.error("global Request + undici fetch => broken", e);
	});

await fetch(undiciReq.clone())
	.then((res) => {
		res.blob().catch(() => {});
		console.log("undici Request + global fetch => works");
	})
	.catch((e) => {
		console.error("undici Request + global fetch => broken", e);
	});

Results with:

global Request + global fetch => works
undici Request + undici fetch => works
global Request + undici fetch => broken TypeError: Failed to parse URL from [object Request]
    at fetch (/Users/michal/undiciReqInconsistency/node_modules/undici/index.js:103:13)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async file:///Users/michal/undiciReqInconsistency/index.mjs:24:1 {
  [cause]: TypeError: Invalid URL
      at new URL (node:internal/url:775:36)
      at new Request (/Users/michal/undiciReqInconsistency/node_modules/undici/lib/fetch/request.js:85:21)
      at fetch (/Users/michal/undiciReqInconsistency/node_modules/undici/lib/fetch/index.js:138:21)
      at fetch (/Users/michal/undiciReqInconsistency/node_modules/undici/index.js:100:18)
      at file:///Users/michal/undiciReqInconsistency/index.mjs:24:7
      at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
    code: 'ERR_INVALID_URL',
    input: '[object Request]'
  }
}
undici Request + global fetch => broken TypeError: Failed to parse URL from [object Request]
    at node:internal/deps/undici/undici:12345:11
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async file:///Users/michal/undiciReqInconsistency/index.mjs:33:1 {
  [cause]: TypeError: Invalid URL
      at new URL (node:internal/url:775:36)
      at new Request (node:internal/deps/undici/undici:5853:25)
      at fetch (node:internal/deps/undici/undici:10123:25)
      at Object.fetch (node:internal/deps/undici/undici:12344:10)
      at fetch (node:internal/process/pre_execution:336:27)
      at file:///Users/michal/undiciReqInconsistency/index.mjs:33:7
      at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
    code: 'ERR_INVALID_URL',
    input: '[object Request]'
  }
}

Proposal

createClient should allow providing custom Request and Response constructors, next to fetch.

Checklist

@Dzieni Dzieni added enhancement New feature or request help wanted Extra attention is needed openapi-fetch Relevant to the openapi-fetch library labels Feb 23, 2024
@drwpow
Copy link
Owner

drwpow commented Mar 16, 2024

This is a great idea, and I’d be in favor of adding it. msw has a similar concept where there are reasons why you’d have a custom Request or Response that are WHATWG-compliant but have additional helpers or additional behavior. And of course the Node <> browser differences with the fetch() API, which should match but often don’t (and I haven’t done an in-depth comparison on the latest version of Node, but there are almost always subtle differences with APIs that can break edge cases as you’ve found).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request help wanted Extra attention is needed openapi-fetch Relevant to the openapi-fetch library
Projects
None yet
Development

No branches or pull requests

2 participants