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

feat: Custom fetch per call #1373

Merged
merged 6 commits into from
Oct 8, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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