From a83ea31e22592b72e09772e347eb9733cff5ce30 Mon Sep 17 00:00:00 2001 From: kyletsang <6854874+kyletsang@users.noreply.github.com> Date: Sun, 22 Sep 2024 20:09:18 -0700 Subject: [PATCH 1/6] Pass query args to prepareHeaders function --- docs/rtk-query/api/fetchBaseQuery.mdx | 4 +- packages/toolkit/src/query/fetchBaseQuery.ts | 11 ++--- .../src/query/tests/fetchBaseQuery.test.tsx | 42 +++++++++++++++++++ 3 files changed, 51 insertions(+), 6 deletions(-) diff --git a/docs/rtk-query/api/fetchBaseQuery.mdx b/docs/rtk-query/api/fetchBaseQuery.mdx index 79420f7032..ba28565fc9 100644 --- a/docs/rtk-query/api/fetchBaseQuery.mdx +++ b/docs/rtk-query/api/fetchBaseQuery.mdx @@ -65,6 +65,7 @@ type FetchBaseQueryArgs = { BaseQueryApi, 'getState' | 'extra' | 'endpoint' | 'type' | 'forced' >, + args: string | FetchArgs ) => MaybePromise fetchFn?: ( input: RequestInfo, @@ -105,7 +106,7 @@ Typically a string like `https://api.your-really-great-app.com/v1/`. If you don' _(optional)_ -Allows you to inject headers on every request. You can specify headers at the endpoint level, but you'll typically want to set common headers like `authorization` here. As a convenience mechanism, the second argument allows you to use `getState` to access your redux store in the event you store information you'll need there such as an auth token. Additionally, it provides access to `extra`, `endpoint`, `type`, and `forced` to unlock more granular conditional behaviors. +Allows you to inject headers on every request. You can specify headers at the endpoint level, but you'll typically want to set common headers like `authorization` here. As a convenience mechanism, the second argument allows you to use `getState` to access your redux store in the event you store information you'll need there such as an auth token. Additionally, it provides access to `extra`, `endpoint`, `type`, and `forced` to unlock more granular conditional behaviors. Arguments passed to the query function is passed as the third argument in case you need those params in the context of the `prepareHeaders` function. You can mutate the `headers` argument directly, and returning it is optional. @@ -119,6 +120,7 @@ type prepareHeaders = ( type: 'query' | 'mutation' forced: boolean | undefined }, + args: string | FetchArgs ) => Headers | void ``` diff --git a/packages/toolkit/src/query/fetchBaseQuery.ts b/packages/toolkit/src/query/fetchBaseQuery.ts index 00cf6e54ab..9a9bb8d925 100644 --- a/packages/toolkit/src/query/fetchBaseQuery.ts +++ b/packages/toolkit/src/query/fetchBaseQuery.ts @@ -113,6 +113,7 @@ export type FetchBaseQueryArgs = { BaseQueryApi, 'getState' | 'extra' | 'endpoint' | 'type' | 'forced' >, + args: string | FetchArgs ) => MaybePromise fetchFn?: ( input: RequestInfo, @@ -164,9 +165,9 @@ export type FetchBaseQueryMeta = { request: Request; response?: Response } * The base URL for an API service. * Typically in the format of https://example.com/ * - * @param {(headers: Headers, api: { getState: () => unknown; extra: unknown; endpoint: string; type: 'query' | 'mutation'; forced: boolean; }) => Headers} prepareHeaders + * @param {(headers: Headers, api: { getState: () => unknown; extra: unknown; endpoint: string; type: 'query' | 'mutation'; forced: boolean; }, args: string | FetchArgs) => Headers} prepareHeaders * An optional function that can be used to inject headers on requests. - * Provides a Headers object, as well as most of the `BaseQueryApi` (`dispatch` is not available). + * Provides a Headers object, most of the `BaseQueryApi` (`dispatch` is not available), and the args passed into the query function. * Useful for setting authentication or headers that need to be set conditionally. * * @link https://developer.mozilla.org/en-US/docs/Web/API/Headers @@ -212,7 +213,7 @@ export function fetchBaseQuery({ 'Warning: `fetch` is not available. Please supply a custom `fetchFn` property to use `fetchBaseQuery` on SSR environments.', ) } - return async (arg, api) => { + return async (args, api) => { const { signal, getState, extra, endpoint, forced, type } = api let meta: FetchBaseQueryMeta | undefined let { @@ -223,7 +224,7 @@ export function fetchBaseQuery({ validateStatus = globalValidateStatus ?? defaultValidateStatus, timeout = defaultTimeout, ...rest - } = typeof arg == 'string' ? { url: arg } : arg + } = typeof args == 'string' ? { url: args } : args let config: RequestInit = { ...baseFetchOptions, signal, @@ -238,7 +239,7 @@ export function fetchBaseQuery({ endpoint, forced, type, - })) || headers + }, args)) || headers // Only set the content-type to json if appropriate. Will not be true for FormData, ArrayBuffer, Blob, etc. const isJsonifiable = (body: any) => diff --git a/packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx b/packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx index 6160eade9e..2c18d3069e 100644 --- a/packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx +++ b/packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx @@ -850,6 +850,48 @@ describe('fetchBaseQuery', () => { expect(_forced).toBe(true) expect(_extra).toBe(fakeAuth0Client) }) + + test('prepareHeaders provides args', async () => { + let _args + + const baseQuery = fetchBaseQuery({ + baseUrl, + fetchFn: fetchFn as any, + prepareHeaders: (headers, _api, args) => { + _args = args + + return headers + }, + }) + + const fakeAuth0Client = { + getTokenSilently: async () => 'fakeToken', + } + + const doRequest = async () => { + const abortController = new AbortController() + return baseQuery( + { url: '/echo' }, + { + signal: abortController.signal, + abort: (reason) => + // @ts-ignore + abortController.abort(reason), + dispatch: storeRef.store.dispatch, + getState: storeRef.store.getState, + extra: fakeAuth0Client, + type: 'query', + forced: true, + endpoint: 'someEndpointName', + }, + {}, + ) + } + + await doRequest() + + expect(_args!.url).toBe('/echo') + }) }) test('can pass `headers` into `fetchBaseQuery`', async () => { From 163f483f59f163bde2d04d8c426aa3aec9942243 Mon Sep 17 00:00:00 2001 From: kyletsang <6854874+kyletsang@users.noreply.github.com> Date: Sun, 22 Sep 2024 20:43:40 -0700 Subject: [PATCH 2/6] Fix type errors in test --- packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx b/packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx index 2c18d3069e..3207359072 100644 --- a/packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx +++ b/packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx @@ -852,7 +852,7 @@ describe('fetchBaseQuery', () => { }) test('prepareHeaders provides args', async () => { - let _args + let _args: any const baseQuery = fetchBaseQuery({ baseUrl, From 9d43ea0ce06fc1fa2d653cd78f6b3de2b1b8a37e Mon Sep 17 00:00:00 2001 From: kyletsang <6854874+kyletsang@users.noreply.github.com> Date: Tue, 24 Sep 2024 10:08:54 -0700 Subject: [PATCH 3/6] Address feedback --- docs/rtk-query/api/fetchBaseQuery.mdx | 11 ++--- packages/toolkit/src/query/fetchBaseQuery.ts | 22 ++++----- .../src/query/tests/fetchBaseQuery.test.tsx | 48 ++----------------- 3 files changed, 18 insertions(+), 63 deletions(-) diff --git a/docs/rtk-query/api/fetchBaseQuery.mdx b/docs/rtk-query/api/fetchBaseQuery.mdx index ba28565fc9..29107aee87 100644 --- a/docs/rtk-query/api/fetchBaseQuery.mdx +++ b/docs/rtk-query/api/fetchBaseQuery.mdx @@ -64,8 +64,7 @@ type FetchBaseQueryArgs = { api: Pick< BaseQueryApi, 'getState' | 'extra' | 'endpoint' | 'type' | 'forced' - >, - args: string | FetchArgs + > & { args: string | FetchArgs }, ) => MaybePromise fetchFn?: ( input: RequestInfo, @@ -106,7 +105,7 @@ Typically a string like `https://api.your-really-great-app.com/v1/`. If you don' _(optional)_ -Allows you to inject headers on every request. You can specify headers at the endpoint level, but you'll typically want to set common headers like `authorization` here. As a convenience mechanism, the second argument allows you to use `getState` to access your redux store in the event you store information you'll need there such as an auth token. Additionally, it provides access to `extra`, `endpoint`, `type`, and `forced` to unlock more granular conditional behaviors. Arguments passed to the query function is passed as the third argument in case you need those params in the context of the `prepareHeaders` function. +Allows you to inject headers on every request. You can specify headers at the endpoint level, but you'll typically want to set common headers like `authorization` here. As a convenience mechanism, the second argument allows you to use `getState` to access your redux store in the event you store information you'll need there such as an auth token. Additionally, it provides access to `args`, `extra`, `endpoint`, `type`, and `forced` to unlock more granular conditional behaviors. You can mutate the `headers` argument directly, and returning it is optional. @@ -115,12 +114,12 @@ type prepareHeaders = ( headers: Headers, api: { getState: () => unknown + args: string | FetchArgs extra: unknown endpoint: string type: 'query' | 'mutation' forced: boolean | undefined }, - args: string | FetchArgs ) => Headers | void ``` @@ -305,8 +304,8 @@ The default response handler is `"json"`, which is equivalent to the following f ```ts title="Default responseHandler" const defaultResponseHandler = async (res: Response) => { - const text = await res.text(); - return text.length ? JSON.parse(text) : null; + const text = await res.text() + return text.length ? JSON.parse(text) : null } ``` diff --git a/packages/toolkit/src/query/fetchBaseQuery.ts b/packages/toolkit/src/query/fetchBaseQuery.ts index 42dad79c93..9a78c17695 100644 --- a/packages/toolkit/src/query/fetchBaseQuery.ts +++ b/packages/toolkit/src/query/fetchBaseQuery.ts @@ -112,8 +112,7 @@ export type FetchBaseQueryArgs = { api: Pick< BaseQueryApi, 'getState' | 'extra' | 'endpoint' | 'type' | 'forced' - >, - args: string | FetchArgs, + > & { args: string | FetchArgs }, ) => MaybePromise fetchFn?: ( input: RequestInfo, @@ -165,7 +164,7 @@ export type FetchBaseQueryMeta = { request: Request; response?: Response } * The base URL for an API service. * Typically in the format of https://example.com/ * - * @param {(headers: Headers, api: { getState: () => unknown; extra: unknown; endpoint: string; type: 'query' | 'mutation'; forced: boolean; }, args: string | FetchArgs) => Headers} prepareHeaders + * @param {(headers: Headers, api: { getState: () => unknown; args: string | FetchArgs; extra: unknown; endpoint: string; type: 'query' | 'mutation'; forced: boolean; }) => Headers} prepareHeaders * An optional function that can be used to inject headers on requests. * Provides a Headers object, most of the `BaseQueryApi` (`dispatch` is not available), and the args passed into the query function. * Useful for setting authentication or headers that need to be set conditionally. @@ -242,17 +241,14 @@ export function fetchBaseQuery({ headers = new Headers(stripUndefined(headers)) config.headers = - (await prepareHeaders( - headers, - { - getState, - extra, - endpoint, - forced, - type, - }, + (await prepareHeaders(headers, { + getState, args, - )) || headers + extra, + endpoint, + forced, + type, + })) || headers // Only set the content-type to json if appropriate. Will not be true for FormData, ArrayBuffer, Blob, etc. const isJsonifiable = (body: any) => diff --git a/packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx b/packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx index 3207359072..cf09d688e1 100644 --- a/packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx +++ b/packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx @@ -799,16 +799,17 @@ describe('fetchBaseQuery', () => { }) test('prepareHeaders provides extra api information for getState, extra, endpoint, type and forced', async () => { - let _getState, _extra, _endpoint, _type, _forced + let _getState, _args, _extra, _endpoint, _type, _forced const baseQuery = fetchBaseQuery({ baseUrl, fetchFn: fetchFn as any, prepareHeaders: ( headers, - { getState, extra, endpoint, type, forced }, + { getState, args, extra, endpoint, type, forced }, ) => { _getState = getState + _args = args _endpoint = endpoint _type = type _forced = forced @@ -845,53 +846,12 @@ describe('fetchBaseQuery', () => { await doRequest() expect(_getState).toBeDefined() + expect(_args!.url).toBe('/echo') expect(_endpoint).toBe('someEndpointName') expect(_type).toBe('query') expect(_forced).toBe(true) expect(_extra).toBe(fakeAuth0Client) }) - - test('prepareHeaders provides args', async () => { - let _args: any - - const baseQuery = fetchBaseQuery({ - baseUrl, - fetchFn: fetchFn as any, - prepareHeaders: (headers, _api, args) => { - _args = args - - return headers - }, - }) - - const fakeAuth0Client = { - getTokenSilently: async () => 'fakeToken', - } - - const doRequest = async () => { - const abortController = new AbortController() - return baseQuery( - { url: '/echo' }, - { - signal: abortController.signal, - abort: (reason) => - // @ts-ignore - abortController.abort(reason), - dispatch: storeRef.store.dispatch, - getState: storeRef.store.getState, - extra: fakeAuth0Client, - type: 'query', - forced: true, - endpoint: 'someEndpointName', - }, - {}, - ) - } - - await doRequest() - - expect(_args!.url).toBe('/echo') - }) }) test('can pass `headers` into `fetchBaseQuery`', async () => { From 79acc76d95fce1849fc8be31e9650d2c61033822 Mon Sep 17 00:00:00 2001 From: kyletsang <6854874+kyletsang@users.noreply.github.com> Date: Tue, 24 Sep 2024 10:39:13 -0700 Subject: [PATCH 4/6] Fix types in test --- packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx b/packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx index cf09d688e1..2cf0b17109 100644 --- a/packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx +++ b/packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx @@ -799,7 +799,7 @@ describe('fetchBaseQuery', () => { }) test('prepareHeaders provides extra api information for getState, extra, endpoint, type and forced', async () => { - let _getState, _args, _extra, _endpoint, _type, _forced + let _getState, _args: any, _extra, _endpoint, _type, _forced const baseQuery = fetchBaseQuery({ baseUrl, From cc4c7d3c665ce7cf2c7397bf8e090a39f54b38b2 Mon Sep 17 00:00:00 2001 From: Mark Erikson Date: Mon, 14 Oct 2024 14:21:07 -0400 Subject: [PATCH 5/6] Change name back to `arg` --- docs/rtk-query/api/fetchBaseQuery.mdx | 4 ++-- packages/toolkit/src/query/fetchBaseQuery.ts | 12 ++++++------ .../toolkit/src/query/tests/fetchBaseQuery.test.tsx | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/rtk-query/api/fetchBaseQuery.mdx b/docs/rtk-query/api/fetchBaseQuery.mdx index 29107aee87..c2cdbb9dc4 100644 --- a/docs/rtk-query/api/fetchBaseQuery.mdx +++ b/docs/rtk-query/api/fetchBaseQuery.mdx @@ -64,7 +64,7 @@ type FetchBaseQueryArgs = { api: Pick< BaseQueryApi, 'getState' | 'extra' | 'endpoint' | 'type' | 'forced' - > & { args: string | FetchArgs }, + > & { arg: string | FetchArgs }, ) => MaybePromise fetchFn?: ( input: RequestInfo, @@ -114,7 +114,7 @@ type prepareHeaders = ( headers: Headers, api: { getState: () => unknown - args: string | FetchArgs + arg: string | FetchArgs extra: unknown endpoint: string type: 'query' | 'mutation' diff --git a/packages/toolkit/src/query/fetchBaseQuery.ts b/packages/toolkit/src/query/fetchBaseQuery.ts index 9a78c17695..89f9c68ad6 100644 --- a/packages/toolkit/src/query/fetchBaseQuery.ts +++ b/packages/toolkit/src/query/fetchBaseQuery.ts @@ -112,7 +112,7 @@ export type FetchBaseQueryArgs = { api: Pick< BaseQueryApi, 'getState' | 'extra' | 'endpoint' | 'type' | 'forced' - > & { args: string | FetchArgs }, + > & { arg: string | FetchArgs }, ) => MaybePromise fetchFn?: ( input: RequestInfo, @@ -164,9 +164,9 @@ export type FetchBaseQueryMeta = { request: Request; response?: Response } * The base URL for an API service. * Typically in the format of https://example.com/ * - * @param {(headers: Headers, api: { getState: () => unknown; args: string | FetchArgs; extra: unknown; endpoint: string; type: 'query' | 'mutation'; forced: boolean; }) => Headers} prepareHeaders + * @param {(headers: Headers, api: { getState: () => unknown; arg: string | FetchArgs; extra: unknown; endpoint: string; type: 'query' | 'mutation'; forced: boolean; }) => Headers} prepareHeaders * An optional function that can be used to inject headers on requests. - * Provides a Headers object, most of the `BaseQueryApi` (`dispatch` is not available), and the args passed into the query function. + * Provides a Headers object, most of the `BaseQueryApi` (`dispatch` is not available), and the arg passed into the query function. * Useful for setting authentication or headers that need to be set conditionally. * * @link https://developer.mozilla.org/en-US/docs/Web/API/Headers @@ -212,7 +212,7 @@ export function fetchBaseQuery({ 'Warning: `fetch` is not available. Please supply a custom `fetchFn` property to use `fetchBaseQuery` on SSR environments.', ) } - return async (args, api) => { + return async (arg, api) => { const { getState, extra, endpoint, forced, type } = api let meta: FetchBaseQueryMeta | undefined let { @@ -223,7 +223,7 @@ export function fetchBaseQuery({ validateStatus = globalValidateStatus ?? defaultValidateStatus, timeout = defaultTimeout, ...rest - } = typeof args == 'string' ? { url: args } : args + } = typeof arg == 'string' ? { url: arg } : arg let abortController: AbortController | undefined, signal = api.signal @@ -243,7 +243,7 @@ export function fetchBaseQuery({ config.headers = (await prepareHeaders(headers, { getState, - args, + arg, extra, endpoint, forced, diff --git a/packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx b/packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx index 2cf0b17109..e2446d50c2 100644 --- a/packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx +++ b/packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx @@ -799,17 +799,17 @@ describe('fetchBaseQuery', () => { }) test('prepareHeaders provides extra api information for getState, extra, endpoint, type and forced', async () => { - let _getState, _args: any, _extra, _endpoint, _type, _forced + let _getState, _arg: any, _extra, _endpoint, _type, _forced const baseQuery = fetchBaseQuery({ baseUrl, fetchFn: fetchFn as any, prepareHeaders: ( headers, - { getState, args, extra, endpoint, type, forced }, + { getState, arg, extra, endpoint, type, forced }, ) => { _getState = getState - _args = args + _arg = arg _endpoint = endpoint _type = type _forced = forced @@ -846,7 +846,7 @@ describe('fetchBaseQuery', () => { await doRequest() expect(_getState).toBeDefined() - expect(_args!.url).toBe('/echo') + expect(_arg!.url).toBe('/echo') expect(_endpoint).toBe('someEndpointName') expect(_type).toBe('query') expect(_forced).toBe(true) From 3a22c67e96b1244ddce944edc6e4c3865ce1dc96 Mon Sep 17 00:00:00 2001 From: Mark Erikson Date: Mon, 14 Oct 2024 14:22:04 -0400 Subject: [PATCH 6/6] Change name back to `arg` --- docs/rtk-query/api/fetchBaseQuery.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rtk-query/api/fetchBaseQuery.mdx b/docs/rtk-query/api/fetchBaseQuery.mdx index c2cdbb9dc4..456ab0dc18 100644 --- a/docs/rtk-query/api/fetchBaseQuery.mdx +++ b/docs/rtk-query/api/fetchBaseQuery.mdx @@ -105,7 +105,7 @@ Typically a string like `https://api.your-really-great-app.com/v1/`. If you don' _(optional)_ -Allows you to inject headers on every request. You can specify headers at the endpoint level, but you'll typically want to set common headers like `authorization` here. As a convenience mechanism, the second argument allows you to use `getState` to access your redux store in the event you store information you'll need there such as an auth token. Additionally, it provides access to `args`, `extra`, `endpoint`, `type`, and `forced` to unlock more granular conditional behaviors. +Allows you to inject headers on every request. You can specify headers at the endpoint level, but you'll typically want to set common headers like `authorization` here. As a convenience mechanism, the second argument allows you to use `getState` to access your redux store in the event you store information you'll need there such as an auth token. Additionally, it provides access to `arg`, `extra`, `endpoint`, `type`, and `forced` to unlock more granular conditional behaviors. You can mutate the `headers` argument directly, and returning it is optional.