Skip to content

Commit 2840542

Browse files
authored
Merge pull request #2493 from michal-kurz/feature/endpoint-specific-args-serializers
2 parents 08bd66d + e4c6765 commit 2840542

File tree

3 files changed

+101
-8
lines changed

3 files changed

+101
-8
lines changed

packages/toolkit/src/query/createApi.ts

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import type { Api, ApiContext, Module, ModuleName } from './apiTypes'
22
import type { CombinedState } from './core/apiState'
33
import type { BaseQueryArg, BaseQueryFn } from './baseQueryTypes'
4-
import type { SerializeQueryArgs } from './defaultSerializeQueryArgs'
5-
import { defaultSerializeQueryArgs } from './defaultSerializeQueryArgs'
4+
import {
5+
defaultSerializeQueryArgs,
6+
SerializeQueryArgs,
7+
} from './defaultSerializeQueryArgs'
68
import type {
79
EndpointBuilder,
810
EndpointDefinitions,
911
} from './endpointDefinitions'
10-
import { DefinitionType } from './endpointDefinitions'
12+
import { DefinitionType, isQueryDefinition } from './endpointDefinitions'
1113
import { nanoid } from '@reduxjs/toolkit'
1214
import type { AnyAction } from '@reduxjs/toolkit'
1315
import type { NoInfer } from './tsHelpers'
@@ -236,15 +238,25 @@ export function buildCreateApi<Modules extends [Module<any>, ...Module<any>[]]>(
236238
})
237239
)
238240

239-
const optionsWithDefaults = {
241+
const optionsWithDefaults: CreateApiOptions<any, any, any, any> = {
240242
reducerPath: 'api',
241-
serializeQueryArgs: defaultSerializeQueryArgs,
242243
keepUnusedDataFor: 60,
243244
refetchOnMountOrArgChange: false,
244245
refetchOnFocus: false,
245246
refetchOnReconnect: false,
246247
...options,
247248
extractRehydrationInfo,
249+
serializeQueryArgs(queryArgsApi) {
250+
let finalSerializeQueryArgs = defaultSerializeQueryArgs
251+
if ('serializeQueryArgs' in queryArgsApi.endpointDefinition) {
252+
finalSerializeQueryArgs =
253+
queryArgsApi.endpointDefinition.serializeQueryArgs!
254+
} else if (options.serializeQueryArgs) {
255+
finalSerializeQueryArgs = options.serializeQueryArgs
256+
}
257+
258+
return finalSerializeQueryArgs(queryArgsApi)
259+
},
248260
tagTypes: [...(options.tagTypes || [])],
249261
}
250262

@@ -266,8 +278,8 @@ export function buildCreateApi<Modules extends [Module<any>, ...Module<any>[]]>(
266278
enhanceEndpoints({ addTagTypes, endpoints }) {
267279
if (addTagTypes) {
268280
for (const eT of addTagTypes) {
269-
if (!optionsWithDefaults.tagTypes.includes(eT as any)) {
270-
optionsWithDefaults.tagTypes.push(eT as any)
281+
if (!optionsWithDefaults.tagTypes!.includes(eT as any)) {
282+
;(optionsWithDefaults.tagTypes as any[]).push(eT)
271283
}
272284
}
273285
}
@@ -290,7 +302,7 @@ export function buildCreateApi<Modules extends [Module<any>, ...Module<any>[]]>(
290302
} as Api<BaseQueryFn, {}, string, string, Modules[number]['name']>
291303

292304
const initializedModules = modules.map((m) =>
293-
m.init(api as any, optionsWithDefaults, context)
305+
m.init(api as any, optionsWithDefaults as any, context)
294306
)
295307

296308
function injectEndpoints(
@@ -319,6 +331,7 @@ export function buildCreateApi<Modules extends [Module<any>, ...Module<any>[]]>(
319331

320332
continue
321333
}
334+
322335
context.endpointDefinitions[endpointName] = definition
323336
for (const m of initializedModules) {
324337
m.injectEndpoint(endpointName, definition)

packages/toolkit/src/query/endpointDefinitions.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { AnyAction, ThunkDispatch } from '@reduxjs/toolkit'
2+
import { SerializeQueryArgs } from './defaultSerializeQueryArgs'
23
import type { RootState } from './core/apiState'
34
import type {
45
BaseQueryExtraOptions,
@@ -272,6 +273,8 @@ export interface QueryExtraOptions<
272273
* Not to be used. A query should not invalidate tags in the cache.
273274
*/
274275
invalidatesTags?: never
276+
277+
serializeQueryArgs?: SerializeQueryArgs<any>
275278
}
276279

277280
export type QueryDefinition<

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

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
} from './helpers'
1818
import { server } from './mocks/server'
1919
import { rest } from 'msw'
20+
import { SerializeQueryArgs } from '../defaultSerializeQueryArgs'
2021

2122
const originalEnv = process.env.NODE_ENV
2223
beforeAll(() => void ((process.env as any).NODE_ENV = 'development'))
@@ -823,3 +824,79 @@ describe('structuralSharing flag behaviors', () => {
823824
expect(firstRef.data === secondRef.data).toBeFalsy()
824825
})
825826
})
827+
828+
describe('custom serializeQueryArgs per endpoint', () => {
829+
const customArgsSerializer: SerializeQueryArgs<number> = ({
830+
endpointName,
831+
queryArgs,
832+
}) => `${endpointName}-${queryArgs}`
833+
834+
type SuccessResponse = { value: 'success' }
835+
836+
const serializer1 = jest.fn(customArgsSerializer)
837+
838+
const api = createApi({
839+
baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
840+
serializeQueryArgs: ({ endpointName, queryArgs }) =>
841+
`base-${endpointName}-${queryArgs}`,
842+
endpoints: (build) => ({
843+
queryWithNoSerializer: build.query<SuccessResponse, number>({
844+
query: (arg) => `${arg}`,
845+
}),
846+
queryWithCustomSerializer: build.query<SuccessResponse, number>({
847+
query: (arg) => `${arg}`,
848+
serializeQueryArgs: serializer1,
849+
}),
850+
}),
851+
})
852+
853+
const storeRef = setupApiStore(api)
854+
855+
it('Works via createApi', async () => {
856+
await storeRef.store.dispatch(
857+
api.endpoints.queryWithNoSerializer.initiate(99)
858+
)
859+
860+
expect(serializer1).toHaveBeenCalledTimes(0)
861+
862+
await storeRef.store.dispatch(
863+
api.endpoints.queryWithCustomSerializer.initiate(42)
864+
)
865+
866+
expect(serializer1).toHaveBeenCalled()
867+
868+
expect(
869+
storeRef.store.getState().api.queries['base-queryWithNoSerializer-99']
870+
).toBeTruthy()
871+
872+
expect(
873+
storeRef.store.getState().api.queries['queryWithCustomSerializer-42']
874+
).toBeTruthy()
875+
})
876+
877+
const serializer2 = jest.fn(customArgsSerializer)
878+
879+
const injectedApi = api.injectEndpoints({
880+
endpoints: (build) => ({
881+
injectedQueryWithCustomSerializer: build.query<SuccessResponse, number>({
882+
query: (arg) => `${arg}`,
883+
serializeQueryArgs: serializer2,
884+
}),
885+
}),
886+
})
887+
888+
it('Works via injectEndpoints', async () => {
889+
expect(serializer2).toHaveBeenCalledTimes(0)
890+
891+
await storeRef.store.dispatch(
892+
injectedApi.endpoints.injectedQueryWithCustomSerializer.initiate(5)
893+
)
894+
895+
expect(serializer2).toHaveBeenCalled()
896+
expect(
897+
storeRef.store.getState().api.queries[
898+
'injectedQueryWithCustomSerializer-5'
899+
]
900+
).toBeTruthy()
901+
})
902+
})

0 commit comments

Comments
 (0)