Skip to content
This repository has been archived by the owner on May 22, 2023. It is now read-only.

Commit

Permalink
Add custom fetch option (#51)
Browse files Browse the repository at this point in the history
* Add custom fetch option

* Fix test request path
  • Loading branch information
hd-o authored May 22, 2023
1 parent 9f117d0 commit 97c8757
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 3 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ createClient<paths>(options);
| Name | Type | Description |
| :-------------- | :------: | :--------------------------------------------------------------------------------------------------------------------------------------- |
| `baseUrl` | `string` | Prefix all fetch URLs with this option (e.g. `"https://myapi.dev/v1/"`). |
| `fetch` | `fetch` | Fetch function used for requests (defaults to `globalThis.fetch`) |
| (Fetch options) | | Any valid fetch option (`headers`, `mode`, `cache`, `signal` …) ([docs](https://developer.mozilla.org/en-US/docs/Web/API/fetch#options)) |

## 🎯 Project Goals
Expand Down
14 changes: 14 additions & 0 deletions src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,20 @@ describe('client', () => {
})
);
});

it('accepts a custom fetch function', async () => {
const data = { works: true };
const client = createClient<paths>({
fetch: async () =>
Promise.resolve({
headers: new Headers(),
json: async () => data,
status: 200,
ok: true,
} as Response),
});
expect((await client.get('/self', {})).data).toBe(data);
});
});

describe('get()', () => {
Expand Down
10 changes: 7 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ const DEFAULT_HEADERS = {
interface ClientOptions extends RequestInit {
/** set the common root URL for all API requests */
baseUrl?: string;
/** custom fetch (defaults to globalThis.fetch) */
fetch?: typeof fetch;
}
export interface BaseParams {
params?: { query?: Record<string, unknown> };
Expand Down Expand Up @@ -51,17 +53,19 @@ export type FetchResponse<T> =
| { data: T extends { responses: any } ? NonNullable<FilterKeys<Success<T['responses']>, JSONLike>> : unknown; error?: never; response: Response }
| { data?: never; error: T extends { responses: any } ? NonNullable<FilterKeys<Error<T['responses']>, JSONLike>> : unknown; response: Response };

export default function createClient<Paths extends {}>(options?: ClientOptions) {
export default function createClient<Paths extends {}>(clientOptions: ClientOptions = {}) {
const { fetch = globalThis.fetch, ...options } = clientOptions;

const defaultHeaders = new Headers({
...DEFAULT_HEADERS,
...(options?.headers ?? {}),
...(options.headers ?? {}),
});

async function coreFetch<P extends keyof Paths, M extends HttpMethod>(url: P, fetchOptions: FetchOptions<M extends keyof Paths[P] ? Paths[P][M] : never>): Promise<FetchResponse<M extends keyof Paths[P] ? Paths[P][M] : unknown>> {
const { headers, body: requestBody, params = {}, querySerializer = (q: QuerySerializer<M extends keyof Paths[P] ? Paths[P][M] : never>) => new URLSearchParams(q as any).toString(), ...init } = fetchOptions || {};

// URL
let finalURL = `${options?.baseUrl ?? ''}${url as string}`;
let finalURL = `${options.baseUrl ?? ''}${url as string}`;
if ((params as any).path) {
for (const [k, v] of Object.entries((params as any).path)) finalURL = finalURL.replace(`{${k}}`, encodeURIComponent(String(v)));
}
Expand Down

0 comments on commit 97c8757

Please sign in to comment.