diff --git a/docs/src/pages/reference/configuration/output.md b/docs/src/pages/reference/configuration/output.md index e30b8d8c1..5613178d7 100644 --- a/docs/src/pages/reference/configuration/output.md +++ b/docs/src/pages/reference/configuration/output.md @@ -1726,6 +1726,8 @@ Use this property to add a custom params serializer to all requests that use que If you provide an object you can also add a default property to use an export default function. +If this is not specified, params are serialized as per `axios` default when using `axios`, or by using `URLSearchParams` when using `fetch`. + Example: ```js @@ -1759,9 +1761,10 @@ export const customParamsSerializerFn = ( Type: `Object` -Use this property to add a default params serializer. Current options are: `qs`. +Use this property to decide how params are serialized. This is only taken into account when `paramsSerializer` is not defined. +Currently, only `qs` is the available option. Read more about `qs` and it's settings [here](https://www.npmjs.com/package/qs). -All options are then passed to the chosen serializer. +If this is not specified, params are serialized as per `axios` default when using `axios`, or by using `URLSearchParams` when using `fetch`. Example: diff --git a/packages/fetch/src/index.ts b/packages/fetch/src/index.ts index 1f2f331d1..271199833 100644 --- a/packages/fetch/src/index.ts +++ b/packages/fetch/src/index.ts @@ -12,6 +12,8 @@ import { generateBodyOptions, isObject, resolveRef, + GeneratorDependency, + ClientDependenciesBuilder, } from '@orval/core'; import { PathItemObject, @@ -86,23 +88,30 @@ export const generateRequestFunction = ( normalizedParams.append(key, value === null ? 'null' : value.toString()) }`; - const getUrlFnImplementation = `export const ${getUrlFnName} = (${getUrlFnProps}) => { -${ - queryParams - ? ` const normalizedParams = new URLSearchParams(); + const queryImplementation = queryParams + ? override.paramsSerializer + ? `const normalizedParams = ${override.paramsSerializer.name}(params);` + : override.paramsSerializerOptions?.qs + ? `const normalizedParams = qs.stringify(params, ${JSON.stringify( + override.paramsSerializerOptions!.qs, + )});` + : `const normalizedParams = new URLSearchParams(); Object.entries(params || {}).forEach(([key, value]) => { ${explodeArrayImplementation} ${!isExplodeParametersOnly ? nomalParamsImplementation : ''} });` - : '' -} + : ''; + const returnQueryImplementation = queryParams + ? override.paramsSerializer || override.paramsSerializerOptions?.qs + ? `return normalizedParams ? \`${route}${'?${normalizedParams}'}\` : \`${route}\`` + : `return normalizedParams.size ? \`${route}${'?${normalizedParams.toString()}'}\` : \`${route}\`` + : `return \`${route}\``; - ${ - queryParams - ? `return normalizedParams.size ? \`${route}${'?${normalizedParams.toString()}'}\` : \`${route}\`` - : `return \`${route}\`` - } + const getUrlFnImplementation = `export const ${getUrlFnName} = (${getUrlFnProps}) => { + ${queryImplementation} + + ${returnQueryImplementation} }\n`; const isNdJson = response.contentTypes.some( @@ -235,9 +244,32 @@ export const generateClient: ClientBuilder = (verbOptions, options) => { }; }; +const PARAMS_SERIALIZER_DEPENDENCIES: GeneratorDependency[] = [ + { + exports: [ + { + name: 'qs', + default: true, + values: true, + syntheticDefaultImport: true, + }, + ], + dependency: 'qs', + }, +]; + +const getFetchDependencies: ClientDependenciesBuilder = ( + _: boolean, + hasParamsSerializerOptions: boolean, +) => { + return [ + ...(hasParamsSerializerOptions ? PARAMS_SERIALIZER_DEPENDENCIES : []), + ]; +}; + const fetchClientBuilder: ClientGeneratorsBuilder = { client: generateClient, - dependencies: () => [], + dependencies: getFetchDependencies, }; export const builder = () => () => fetchClientBuilder; diff --git a/tests/configs/default.config.ts b/tests/configs/default.config.ts index d233eda9f..aed97c011 100644 --- a/tests/configs/default.config.ts +++ b/tests/configs/default.config.ts @@ -209,4 +209,35 @@ export default defineConfig({ target: '../specifications/petstore.yaml', }, }, + paramsSerializer: { + output: { + target: '../generated/default/params-serializer/endpoints.ts', + schemas: '../generated/default/params-serializer/model', + override: { + paramsSerializer: { + path: '../mutators/params-serializer.ts', + name: 'customParamsSerializer', + }, + }, + }, + input: { + target: '../specifications/petstore.yaml', + }, + }, + paramsSerializerOptions: { + output: { + target: '../generated/default/params-serializer-options/endpoints.ts', + schemas: '../generated/default/params-serializer-options/model', + override: { + paramsSerializerOptions: { + qs: { + arrayFormat: 'repeat', + }, + }, + }, + }, + input: { + target: '../specifications/petstore.yaml', + }, + }, }); diff --git a/tests/configs/fetch.config.ts b/tests/configs/fetch.config.ts index 2494e1887..481ccb32f 100644 --- a/tests/configs/fetch.config.ts +++ b/tests/configs/fetch.config.ts @@ -189,4 +189,37 @@ export default defineConfig({ target: '../specifications/parameters.yaml', }, }, + paramsSerializer: { + output: { + target: '../generated/fetch/params-serializer/endpoints.ts', + schemas: '../generated/fetch/params-serializer/model', + client: 'fetch', + override: { + paramsSerializer: { + path: '../mutators/params-serializer.ts', + name: 'customParamsSerializer', + }, + }, + }, + input: { + target: '../specifications/petstore.yaml', + }, + }, + paramsSerializerOptions: { + output: { + target: '../generated/fetch/params-serializer-options/endpoints.ts', + schemas: '../generated/fetch/params-serializer-options/model', + client: 'fetch', + override: { + paramsSerializerOptions: { + qs: { + arrayFormat: 'repeat', + }, + }, + }, + }, + input: { + target: '../specifications/petstore.yaml', + }, + }, }); diff --git a/tests/mutators/params-serializer.ts b/tests/mutators/params-serializer.ts new file mode 100644 index 000000000..c233f3a9a --- /dev/null +++ b/tests/mutators/params-serializer.ts @@ -0,0 +1,11 @@ +export const customParamsSerializer = (params: Record): string => { + const normalizedParams = new URLSearchParams(); + + Object.entries(params || {}).forEach(([key, value]) => { + if (value !== undefined) { + normalizedParams.append(key, value === null ? 'null' : value.toString()); + } + }); + + return normalizedParams.toString(); +};