Skip to content

Commit 0714412

Browse files
committed
Fix issue with non-absolute URLs
1 parent b7b5d0f commit 0714412

File tree

2 files changed

+73
-11
lines changed

2 files changed

+73
-11
lines changed

src/FetchClient.test.ts

+40
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,46 @@ Deno.test("can use kitchen sink", async () => {
762762
assert(optionsCalled);
763763
});
764764

765+
Deno.test("can getJSON relative URL", async () => {
766+
const provider = new FetchClientProvider();
767+
let requestedUrl = "";
768+
const fakeFetch = (req: URL | Request | string): Promise<Response> =>
769+
new Promise((resolve) => {
770+
if (typeof req === "string") {
771+
requestedUrl = req;
772+
} else if (req instanceof Request) {
773+
requestedUrl = req.url;
774+
} else {
775+
requestedUrl = req.toString();
776+
}
777+
const data = JSON.stringify({});
778+
resolve(new Response(data));
779+
});
780+
781+
provider.fetch = fakeFetch;
782+
const client = provider.getFetchClient();
783+
784+
await client.getJSON<Products>(
785+
`/todos/1`,
786+
{
787+
params: {
788+
limit: 3,
789+
},
790+
},
791+
);
792+
assertEquals(requestedUrl, "http://localhost/todos/1?limit=3");
793+
794+
await client.getJSON<Products>(
795+
`todos/1`,
796+
{
797+
params: {
798+
limit: 3,
799+
},
800+
},
801+
);
802+
assertEquals(requestedUrl, "http://localhost/todos/1?limit=3");
803+
});
804+
765805
function someMiddleware(ctx: FetchClientContext, next: () => Promise<void>) {
766806
ctx.someMiddleware = true;
767807
return next();

src/FetchClient.ts

+33-11
Original file line numberDiff line numberDiff line change
@@ -485,12 +485,29 @@ export class FetchClient {
485485
this.#counter.increment();
486486
this.#provider.counter.increment();
487487

488+
let request: Request | null = null;
489+
490+
try {
491+
request = new Request(url, init);
492+
} catch {
493+
// try converting to absolute URL
494+
const origin = globalThis.location?.origin ?? "http://localhost";
495+
if (!url.startsWith("http")) {
496+
if (url.startsWith("/")) {
497+
request = new Request(origin + url, init);
498+
} else {
499+
request = new Request(origin + url, init);
500+
}
501+
}
502+
}
503+
488504
const context: FetchClientContext = {
489505
options,
490-
request: new Request(url, init),
506+
request: request!,
491507
response: null,
492508
meta: {},
493509
};
510+
494511
await this.invokeMiddleware(context, middleware);
495512

496513
this.#counter.decrement();
@@ -619,21 +636,23 @@ export class FetchClient {
619636
}
620637

621638
private buildUrl(url: string, options: RequestOptions | undefined): string {
622-
if (url.startsWith("/")) {
623-
url = url.substring(1);
624-
}
639+
let builtUrl = url;
625640

626-
if (!url.startsWith("http") && this.options?.baseUrl) {
627-
url = this.options.baseUrl + "/" + url;
641+
if (!builtUrl.startsWith("http") && this.options?.baseUrl) {
642+
if (this.options.baseUrl.endsWith("/") || builtUrl.startsWith("/")) {
643+
builtUrl = this.options.baseUrl + builtUrl;
644+
} else {
645+
builtUrl = this.options.baseUrl + "/" + builtUrl;
646+
}
628647
}
629648

630-
const isAbsoluteUrl = url.startsWith("http");
649+
const isAbsoluteUrl = builtUrl.startsWith("http");
631650

632651
const origin: string | undefined = isAbsoluteUrl
633652
? undefined
634-
: globalThis.location?.origin ?? undefined;
653+
: globalThis.location?.origin ?? "http://localhost";
635654

636-
const parsed = new URL(url, origin);
655+
const parsed = new URL(builtUrl, origin);
637656

638657
if (options?.params) {
639658
for (const [key, value] of Object.entries(options?.params)) {
@@ -644,10 +663,13 @@ export class FetchClient {
644663
}
645664
}
646665

647-
url = parsed.toString();
666+
builtUrl = parsed.toString();
648667
}
649668

650-
const result = isAbsoluteUrl ? url : `${parsed.pathname}${parsed.search}`;
669+
const result = isAbsoluteUrl
670+
? builtUrl
671+
: `${parsed.pathname}${parsed.search}`;
672+
651673
return result;
652674
}
653675

0 commit comments

Comments
 (0)