Skip to content

Commit d8d1af6

Browse files
committed
Rework endpoint serializeQueryArgs to allow object/number returns
1 parent e0a8bd5 commit d8d1af6

File tree

4 files changed

+105
-12
lines changed

4 files changed

+105
-12
lines changed

packages/toolkit/src/query/createApi.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,22 @@ export function buildCreateApi<Modules extends [Module<any>, ...Module<any>[]]>(
249249
serializeQueryArgs(queryArgsApi) {
250250
let finalSerializeQueryArgs = defaultSerializeQueryArgs
251251
if ('serializeQueryArgs' in queryArgsApi.endpointDefinition) {
252-
finalSerializeQueryArgs =
252+
const endpointSQA =
253253
queryArgsApi.endpointDefinition.serializeQueryArgs!
254+
finalSerializeQueryArgs = (queryArgsApi) => {
255+
const initialResult = endpointSQA(queryArgsApi)
256+
if (typeof initialResult === 'string') {
257+
// If the user function returned a string, use it as-is
258+
return initialResult
259+
} else {
260+
// Assume they returned an object (such as a subset of the original
261+
// query args) or a primitive, and serialize it ourselves
262+
return defaultSerializeQueryArgs({
263+
...queryArgsApi,
264+
queryArgs: initialResult,
265+
})
266+
}
267+
}
254268
} else if (options.serializeQueryArgs) {
255269
finalSerializeQueryArgs = options.serializeQueryArgs
256270
}

packages/toolkit/src/query/defaultSerializeQueryArgs.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ export const defaultSerializeQueryArgs: SerializeQueryArgs<any> = ({
1919
)})`
2020
}
2121

22-
export type SerializeQueryArgs<QueryArgs> = (_: {
22+
export type SerializeQueryArgs<QueryArgs, ReturnType = string> = (_: {
2323
queryArgs: QueryArgs
2424
endpointDefinition: EndpointDefinition<any, any, any, any>
2525
endpointName: string
26-
}) => string
26+
}) => ReturnType
2727

2828
export type InternalSerializeQueryArgs = (_: {
2929
queryArgs: any

packages/toolkit/src/query/endpointDefinitions.ts

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -330,10 +330,13 @@ export interface QueryExtraOptions<
330330
invalidatesTags?: never
331331

332332
/**
333-
* Can be provided to return a custom cache key string based on the provided arguments.
333+
* Can be provided to return a custom cache key value based on the query arguments.
334334
*
335335
* This is primarily intended for cases where a non-serializable value is passed as part of the query arg object and should be excluded from the cache key. It may also be used for cases where an endpoint should only have a single cache entry, such as an infinite loading / pagination implementation.
336336
*
337+
* Unlike the `createApi` version which can _only_ return a string, this per-endpoint option can also return an an object, number, or boolean. If it returns a string, that value will be used as the cache key directly. If it returns an object / number / boolean, that value will be passed to the built-in `defaultSerializeQueryArgs`. This simplifies the use case of stripping out args you don't want included in the cache key.
338+
*
339+
*
337340
* @example
338341
*
339342
* ```ts
@@ -362,13 +365,18 @@ export interface QueryExtraOptions<
362365
* // highlight-start
363366
* serializeQueryArgs: ({ queryArgs, endpointDefinition, endpointName }) => {
364367
* const { id } = queryArgs
365-
* // You can use `defaultSerializeQueryArgs` to do the work:
366-
* return defaultSerializeQueryArgs({
367-
* endpointName,
368-
* queryArgs: { id },
369-
* endpointDefinition
370-
* })
371-
* // Or alternately, create a string yourself:
368+
* // This can return a string, an object, a number, or a boolean.
369+
* // If it returns an object, number or boolean, that value
370+
* // will be serialized automatically via `defaultSerializeQueryArgs`
371+
* return { id } // omit `client` from the cache key
372+
*
373+
* // Alternately, you can use `defaultSerializeQueryArgs` yourself:
374+
* // return defaultSerializeQueryArgs({
375+
* // endpointName,
376+
* // queryArgs: { id },
377+
* // endpointDefinition
378+
* // })
379+
* // Or create and return a string yourself:
372380
* // return `getPost(${id})`
373381
* },
374382
* // highlight-end
@@ -377,7 +385,10 @@ export interface QueryExtraOptions<
377385
*})
378386
* ```
379387
*/
380-
serializeQueryArgs?: SerializeQueryArgs<QueryArg>
388+
serializeQueryArgs?: SerializeQueryArgs<
389+
QueryArg,
390+
string | number | boolean | Record<any, any>
391+
>
381392

382393
/**
383394
* Can be provided to merge an incoming response value into the current cache data.

packages/toolkit/src/query/tests/createApi.test.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,16 @@ describe('custom serializeQueryArgs per endpoint', () => {
854854

855855
const serializer1 = jest.fn(customArgsSerializer)
856856

857+
interface MyApiClient {
858+
fetchPost: (id: string) => Promise<SuccessResponse>
859+
}
860+
861+
const dummyClient: MyApiClient = {
862+
async fetchPost(id) {
863+
return { value: 'success' }
864+
},
865+
}
866+
857867
const api = createApi({
858868
baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
859869
serializeQueryArgs: ({ endpointName, queryArgs }) =>
@@ -866,6 +876,34 @@ describe('custom serializeQueryArgs per endpoint', () => {
866876
query: (arg) => `${arg}`,
867877
serializeQueryArgs: serializer1,
868878
}),
879+
queryWithCustomObjectSerializer: build.query<
880+
SuccessResponse,
881+
{ id: number; client: MyApiClient }
882+
>({
883+
query: (arg) => `${arg.id}`,
884+
serializeQueryArgs: ({
885+
endpointDefinition,
886+
endpointName,
887+
queryArgs,
888+
}) => {
889+
const { id } = queryArgs
890+
return { id }
891+
},
892+
}),
893+
queryWithCustomNumberSerializer: build.query<
894+
SuccessResponse,
895+
{ id: number; client: MyApiClient }
896+
>({
897+
query: (arg) => `${arg.id}`,
898+
serializeQueryArgs: ({
899+
endpointDefinition,
900+
endpointName,
901+
queryArgs,
902+
}) => {
903+
const { id } = queryArgs
904+
return id
905+
},
906+
}),
869907
listItems: build.query<string[], number>({
870908
query: (pageNumber) => `/listItems?page=${pageNumber}`,
871909
serializeQueryArgs: ({ endpointName }) => {
@@ -931,6 +969,36 @@ describe('custom serializeQueryArgs per endpoint', () => {
931969
).toBeTruthy()
932970
})
933971

972+
test('Serializes a returned object for query args', async () => {
973+
await storeRef.store.dispatch(
974+
api.endpoints.queryWithCustomObjectSerializer.initiate({
975+
id: 42,
976+
client: dummyClient,
977+
})
978+
)
979+
980+
expect(
981+
storeRef.store.getState().api.queries[
982+
'queryWithCustomObjectSerializer({"id":42})'
983+
]
984+
).toBeTruthy()
985+
})
986+
987+
test('Serializes a returned primitive for query args', async () => {
988+
await storeRef.store.dispatch(
989+
api.endpoints.queryWithCustomNumberSerializer.initiate({
990+
id: 42,
991+
client: dummyClient,
992+
})
993+
)
994+
995+
expect(
996+
storeRef.store.getState().api.queries[
997+
'queryWithCustomNumberSerializer(42)'
998+
]
999+
).toBeTruthy()
1000+
})
1001+
9341002
test('serializeQueryArgs + merge allows refetching as args change with same cache key', async () => {
9351003
const allItems = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'i']
9361004
const PAGE_SIZE = 3

0 commit comments

Comments
 (0)