Skip to content

Commit

Permalink
feat: Custom fetch per call (#1373)
Browse files Browse the repository at this point in the history
* custom fetch per call

* docs update

* code example for fetches during SSR

* updated example

* cahngeset

* Update .changeset/wise-poems-do.md

Co-authored-by: Drew Powers <1369770+drwpow@users.noreply.github.com>

---------

Co-authored-by: Drew Powers <1369770+drwpow@users.noreply.github.com>
  • Loading branch information
HugeLetters and drwpow committed Oct 8, 2023
1 parent ea65d72 commit fd3e96f
Show file tree
Hide file tree
Showing 9 changed files with 54 additions and 31 deletions.
5 changes: 5 additions & 0 deletions .changeset/wise-poems-do.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"openapi-fetch": patch
---

Added the option to provide custom fetch function to individual API calls.
14 changes: 10 additions & 4 deletions docs/astro.config.js
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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()],
},
Expand Down
1 change: 1 addition & 0 deletions docs/src/content/docs/openapi-fetch/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 href="https://developer.mozilla.org/en-US/docs/Web/API/Response#instance_methods" target="_blank" rel="noopener noreferrer">a built-in instance method</a> (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`, …) (<a href="https://developer.mozilla.org/en-US/docs/Web/API/fetch#options" target="_blank" rel="noopener noreferrer">docs</a>) |

### querySerializer
Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/openapi-fetch/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ openapi-fetch is simple vanilla JS that can be used in any project. But sometime

### Svelte / SvelteKit

<a href="https://kit.svelte.dev" target="_blank" rel="noopener noreferrer">SvelteKit</a>’s automatic type inference can easily pick up openapi-fetch’s types in both clientside fetching and <a href="https://kit.svelte.dev/docs/load#page-data" target="_blank" rel="noopener noreferrer">Page Data</a> fetching. And it doesn’t need any additional libraries to work.
<a href="https://kit.svelte.dev" target="_blank" rel="noopener noreferrer">SvelteKit</a>’s automatic type inference can easily pick up openapi-fetch’s types in both clientside fetching and <a href="https://kit.svelte.dev/docs/load#page-data" target="_blank" rel="noopener noreferrer">Page Data</a> fetching. And it doesn’t need any additional libraries to work. SvelteKit also advises to use their <a href="https://kit.svelte.dev/docs/load#making-fetch-requests" target="_blank" rel="noopener noreferrer">custom fetch</a> in load functions - this can be achieved with <a href='/openapi-fetch/api#fetch-options'>fetch options</a>.

_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._

Expand Down
10 changes: 10 additions & 0 deletions packages/openapi-fetch/examples/sveltekit/src/hooks.server.ts
Original file line number Diff line number Diff line change
@@ -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";
},
});
};
Original file line number Diff line number Diff line change
@@ -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<paths>({ baseUrl: "https://catfact.ninja/" });
export default client;

export const createServerClient = (fetcher: PageServerLoadEvent["fetch"]) =>
createClient<paths>({
baseUrl: "https://catfact.ninja/",
fetch: fetcher,
});
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down
32 changes: 20 additions & 12 deletions packages/openapi-fetch/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<paths>({
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<paths>({ 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);
});
});

Expand Down
6 changes: 4 additions & 2 deletions packages/openapi-fetch/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ export type RequestBodyOption<T> = OperationRequestBodyContent<T> extends never
? { body?: OperationRequestBodyContent<T> }
: { body: OperationRequestBodyContent<T> };

export type FetchOptions<T> = RequestOptions<T> & Omit<RequestInit, "body">;
export type FetchOptions<T> = RequestOptions<T> &
Omit<RequestInit, "body"> & { fetch?: ClientOptions["fetch"] };

export type FetchResponse<T> =
| {
Expand All @@ -95,7 +96,7 @@ export default function createClient<Paths extends {}>(
clientOptions: ClientOptions = {},
) {
const {
fetch = globalThis.fetch,
fetch: baseFetch = globalThis.fetch,
querySerializer: globalQuerySerializer,
bodySerializer: globalBodySerializer,
...options
Expand All @@ -110,6 +111,7 @@ export default function createClient<Paths extends {}>(
fetchOptions: FetchOptions<M extends keyof Paths[P] ? Paths[P][M] : never>,
): Promise<FetchResponse<M extends keyof Paths[P] ? Paths[P][M] : unknown>> {
const {
fetch = baseFetch,
headers,
body: requestBody,
params = {},
Expand Down

0 comments on commit fd3e96f

Please sign in to comment.