diff --git a/.changeset/wise-poems-do.md b/.changeset/wise-poems-do.md new file mode 100644 index 000000000..764b19c4b --- /dev/null +++ b/.changeset/wise-poems-do.md @@ -0,0 +1,5 @@ +--- +"openapi-fetch": patch +--- + +Added the option to provide custom fetch function to individual API calls. diff --git a/docs/astro.config.js b/docs/astro.config.js index e73ec98ac..fc6cf0071 100644 --- a/docs/astro.config.js +++ b/docs/astro.config.js @@ -1,7 +1,7 @@ -import { defineConfig } from "astro/config"; import preact from "@astrojs/preact"; import react from "@astrojs/react"; import sitemap from "@astrojs/sitemap"; +import { defineConfig } from "astro/config"; import sassDts from "vite-plugin-sass-dts"; // https://astro.build/config @@ -21,9 +21,15 @@ export default defineConfig({ }, }, define: { - "import.meta.env.VITE_ALGOLIA_APP_ID": JSON.stringify(process.env.ALGOLIA_APP_ID ?? ""), - "import.meta.env.VITE_ALGOLIA_INDEX_NAME": JSON.stringify(process.env.ALGOLIA_INDEX_NAME ?? ""), - "import.meta.env.VITE_ALGOLIA_SEARCH_KEY": JSON.stringify(process.env.ALGOLIA_SEARCH_KEY ?? ""), + "import.meta.env.VITE_ALGOLIA_APP_ID": JSON.stringify( + process.env.ALGOLIA_APP_ID ?? "", + ), + "import.meta.env.VITE_ALGOLIA_INDEX_NAME": JSON.stringify( + process.env.ALGOLIA_INDEX_NAME ?? "", + ), + "import.meta.env.VITE_ALGOLIA_SEARCH_KEY": JSON.stringify( + process.env.ALGOLIA_SEARCH_KEY ?? "", + ), }, plugins: [sassDts()], }, diff --git a/docs/src/content/docs/openapi-fetch/api.md b/docs/src/content/docs/openapi-fetch/api.md index dc6ba2360..3fac22d1f 100644 --- a/docs/src/content/docs/openapi-fetch/api.md +++ b/docs/src/content/docs/openapi-fetch/api.md @@ -34,6 +34,7 @@ client.get("/my-url", options); | `querySerializer` | QuerySerializer | (optional) Provide a [querySerializer](#queryserializer) | | `bodySerializer` | BodySerializer | (optional) Provide a [bodySerializer](#bodyserializer) | | `parseAs` | `"json"` \| `"text"` \| `"arrayBuffer"` \| `"blob"` \| `"stream"` | (optional) Parse the response using a built-in instance method (default: `"json"`). `"stream"` skips parsing altogether and returns the raw stream. | +| `fetch` | `fetch` | Fetch instance used for requests (default: fetch from `createClient`) | | (Fetch options) | | Any valid fetch option (`headers`, `mode`, `cache`, `signal`, …) (docs) | ### querySerializer diff --git a/docs/src/content/docs/openapi-fetch/examples.md b/docs/src/content/docs/openapi-fetch/examples.md index 38c70a8f7..8f6edbd97 100644 --- a/docs/src/content/docs/openapi-fetch/examples.md +++ b/docs/src/content/docs/openapi-fetch/examples.md @@ -90,7 +90,7 @@ openapi-fetch is simple vanilla JS that can be used in any project. But sometime ### Svelte / SvelteKit -SvelteKit’s automatic type inference can easily pick up openapi-fetch’s types in both clientside fetching and Page Data fetching. And it doesn’t need any additional libraries to work. +SvelteKit’s automatic type inference can easily pick up openapi-fetch’s types in both clientside fetching and Page Data fetching. And it doesn’t need any additional libraries to work. SvelteKit also advises to use their custom fetch in load functions - this can be achieved with fetch options. _Note: if you’re using Svelte without SvelteKit, the root example in `src/routes/+page.svelte` doesn’t use any SvelteKit features and is generally-applicable to any setup._ diff --git a/packages/openapi-fetch/examples/sveltekit/src/hooks.server.ts b/packages/openapi-fetch/examples/sveltekit/src/hooks.server.ts new file mode 100644 index 000000000..5d1e65207 --- /dev/null +++ b/packages/openapi-fetch/examples/sveltekit/src/hooks.server.ts @@ -0,0 +1,10 @@ +import type { Handle } from "@sveltejs/kit"; + +export const handle: Handle = async ({ event, resolve }) => { + return resolve(event, { + filterSerializedResponseHeaders(name) { + // SvelteKit doesn't serialize any headers on server-side fetches by default but openapi-fetch uses this header for empty responses. + return name === "content-length"; + }, + }); +}; diff --git a/packages/openapi-fetch/examples/sveltekit/src/lib/api/index.ts b/packages/openapi-fetch/examples/sveltekit/src/lib/api/index.ts index 0528b2e94..bdbdcbd21 100644 --- a/packages/openapi-fetch/examples/sveltekit/src/lib/api/index.ts +++ b/packages/openapi-fetch/examples/sveltekit/src/lib/api/index.ts @@ -1,12 +1,5 @@ import createClient from "openapi-fetch"; import type { paths } from "./v1"; -import type { PageServerLoadEvent } from "../../routes/page-data/$types"; const client = createClient({ baseUrl: "https://catfact.ninja/" }); export default client; - -export const createServerClient = (fetcher: PageServerLoadEvent["fetch"]) => - createClient({ - baseUrl: "https://catfact.ninja/", - fetch: fetcher, - }); diff --git a/packages/openapi-fetch/examples/sveltekit/src/routes/page-data/+page.server.ts b/packages/openapi-fetch/examples/sveltekit/src/routes/page-data/+page.ts similarity index 71% rename from packages/openapi-fetch/examples/sveltekit/src/routes/page-data/+page.server.ts rename to packages/openapi-fetch/examples/sveltekit/src/routes/page-data/+page.ts index 53b9c6784..610f6ded4 100644 --- a/packages/openapi-fetch/examples/sveltekit/src/routes/page-data/+page.server.ts +++ b/packages/openapi-fetch/examples/sveltekit/src/routes/page-data/+page.ts @@ -1,14 +1,12 @@ -import { createServerClient } from "$lib/api/index.js"; +import client from "$lib/api/index.js"; // Note: this uses Svelte’s custom fetcher as an example, but Node’s // native fetch works, too. See Svelte’s docs to learn the difference: // @see https://kit.svelte.dev/docs/load#making-fetch-requests export async function load({ fetch }) { - const client = createServerClient(fetch); const fact = await client.GET("/fact", { - params: { - query: { max_length: 500 }, - }, + params: { query: { max_length: 500 } }, + fetch, }); return { diff --git a/packages/openapi-fetch/src/index.test.ts b/packages/openapi-fetch/src/index.test.ts index 90e0030af..1e68ac651 100644 --- a/packages/openapi-fetch/src/index.test.ts +++ b/packages/openapi-fetch/src/index.test.ts @@ -396,18 +396,26 @@ describe("client", () => { }); it("accepts a custom fetch function", async () => { - const data = { works: true }; - const customFetch = { - clone: () => ({ ...customFetch }), - headers: new Headers(), - json: async () => data, - status: 200, - ok: true, - }; - const client = createClient({ - fetch: async () => Promise.resolve(customFetch as Response), - }); - expect((await client.GET("/self")).data).toBe(data); + function createCustomFetch(data: any) { + const response = { + clone: () => ({ ...response }), + headers: new Headers(), + json: async () => data, + status: 200, + ok: true, + } as Response; + return async () => Promise.resolve(response); + } + + const baseData = { works: true }; + const customBaseFetch = createCustomFetch(baseData); + const client = createClient({ fetch: customBaseFetch }); + expect((await client.GET("/self")).data).toBe(baseData); + + const data = { result: "it's working" }; + const customFetch = createCustomFetch(data); + const customResponse = await client.GET("/self", { fetch: customFetch }); + expect(customResponse.data).toBe(data); }); }); diff --git a/packages/openapi-fetch/src/index.ts b/packages/openapi-fetch/src/index.ts index 6f78db13c..51b04f1e3 100644 --- a/packages/openapi-fetch/src/index.ts +++ b/packages/openapi-fetch/src/index.ts @@ -70,7 +70,8 @@ export type RequestBodyOption = OperationRequestBodyContent extends never ? { body?: OperationRequestBodyContent } : { body: OperationRequestBodyContent }; -export type FetchOptions = RequestOptions & Omit; +export type FetchOptions = RequestOptions & + Omit & { fetch?: ClientOptions["fetch"] }; export type FetchResponse = | { @@ -95,7 +96,7 @@ export default function createClient( clientOptions: ClientOptions = {}, ) { const { - fetch = globalThis.fetch, + fetch: baseFetch = globalThis.fetch, querySerializer: globalQuerySerializer, bodySerializer: globalBodySerializer, ...options @@ -110,6 +111,7 @@ export default function createClient( fetchOptions: FetchOptions, ): Promise> { const { + fetch = baseFetch, headers, body: requestBody, params = {},