orval
orval copied to clipboard
Axios: cannot customize the headers for requests
I have generated my code, like this
but it seems I cannot configure headers for a specific API endpoint individually Content-Encoding': 'gzip. Is there any solution ?
My current temporary solution is:
export const useRecommendComponents = (queryOptions?: any) => {
const mutation = usePostApiProjectsIdSimilarity({
mutation: {
...queryOptions,
mutationFn: async ({ id, data }) => {
const paramsStr = JSON.stringify(data);
const compressed = pako.gzip(encodeURIComponent(paramsStr));
return axiosInstance({
url: `/api/Projects/${id}/similarity`,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Encoding': 'gzip',
},
data: compressed,
});
},
},
});
return mutation;
};
i use Axios Request Interceptors to add custom headers. You could check the path and add the right content encoding.
/**
* Axios utlity class for adding token handling and Date handling to all request/responses.
*/
export default class AxiosInterceptors {
// hold instances of interceptor by their unique URL
static requestInterceptors = new Map<string, number>();
static responseInterceptors = new Map<string, number>();
/**
* Configures Axios request/reponse interceptors to add JWT token.
*
* @param {AxiosInstance} instance the Axios instance to remove interceptors
* @param {string} token the JWT token to add to all Axios requests
*/
static setupAxiosInstance = (instance: AxiosInstance, token: string) => {
const appKey = instance.defaults.baseURL!;
EnvUtils.debug(`Configuring Axios request/response interceptors for: ${appKey}`);
// Axios request interceptor to add JWT token
const tokenRequestInterceptor = instance.interceptors.request.use(
(config) => {
if (token) {
const headers = config.headers || {};
headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
EnvUtils.debug(`Axios Token Request Interceptor: ${tokenRequestInterceptor}`);
AxiosInterceptors.requestInterceptors.set(appKey, tokenRequestInterceptor);
// Axios response interceptor to translate String to Date
const dateResponseInterceptor = instance.interceptors.response.use((originalResponse) => {
const auditHeader = originalResponse.headers['audit-event-id'];
if (auditHeader) {
window.sessionStorage.setItem('audit-event-id', auditHeader);
window.localStorage.setItem('audit-event-id', auditHeader);
}
FormatUtils.translateJsonDates(originalResponse.data);
return originalResponse;
});
EnvUtils.debug(`Axios Date Response Interceptor: ${dateResponseInterceptor}`);
AxiosInterceptors.responseInterceptors.set(appKey, dateResponseInterceptor);
};
/**
* Cleanup Axios on sign out of application.
*
* @param {AxiosInstance} instance the Axios instance to remove interceptors
*/
static teardownAxiosInstance = (instance: AxiosInstance) => {
const appKey = instance.defaults.baseURL!;
EnvUtils.warn(`Cleaning up Axios removing all interceptors for: ${appKey}`);
instance.interceptors.request.clear();
instance.interceptors.response.clear();
};
}
Thank you very much for your answer. It's a good idea to add specific headers by checking the path in the axiosInstance interceptor. However, it would be even better if we could extend the orval-generated react-query request hooks to support passing axios configurations,
for usage example:
import {
postApiProjectsIdSimilarity,
usePostApiProjectsIdSimilarity,
} from '@/api/projects/projects';
export const useRecommendComponents = (queryOptions?: any) => {
const mutation = usePostApiProjectsIdSimilarity({
mutation: {
...queryOptions,
mutationFn: async ({ id, data }) => {
const paramsStr = JSON.stringify(data);
const compressed = pako.gzip(encodeURIComponent(paramsStr));
return postApiProjectsIdSimilarity(id, compressed);
},
},
axiosConfig: {
headers: {
'Content-Type': 'application/json',
'Content-Encoding': 'gzip',
},
},
});
return mutation;
};
for orval generate api:
With orval you can set your own client/fetch in generating options by overriding the mutator being used. For example we use timeout on some default operation ids, and we can add a header on any request/mutation from the orval generated hooks. Both can be set buildtime and used runtime.
Our custom Axios client looks like:
const baseUrl = 'https://foo.test'
type Arg = {
url: string
method: AxiosMethod
params?: { [key: string]: string | string[] | number | boolean | undefined }
data?: unknown
headers?: Record<string, string>
signal?: AbortSignal
responseType?: string | Blob
}
type Options = {
headers?: Record<string, string>
timeout?: number
}
export const client = async <T>(
{ url, method = 'GET', params, data, headers, signal }: Arg,
options?: Options
): Promise<T> => {
const urlParams = params
? new URLSearchParams(
Object.keys(params).reduce(
(acc, key) => (params[key] === undefined ? { ...acc } : { ...acc, [key]: params[key] }),
{}
)
)
: ''
return fetchNoCatch({
url: `${baseUrl}${url}${params ? `?${urlParams}` : ''}`,
method,
headers: { ...headers, ...(options?.headers || {}) },
...(data ? { body: JSON.stringify(data) } : {}),
signal,
timeout: options?.timeout,
})
}
where fetchNoCatch is just the axios request. Hope this helps.
I now have these options everywhere:
type Options = {
headers?: Record<string, string>
timeout?: number
}
Now you can do
useGeneratedHook({ query: {}, request: { headers: {'foo':'bar'}}})
in your Orval config you can also now pass these two Options, for example based on operation id from your open api spec:
override: {
mutator: {
path: '../mutator/client.ts',
name: 'client',
},
operations: {
getFooUnreachable: {
requestOptions: {
timeout: 30000,
},
},
},
...
now in the generated hook this will be added standard, same you can do for headers :)
so option to keep it dynamic (consumer hook), or in the already generated version (which you can still overwrite, so can see it as a default... :)).
generated code with orval:
export const getFooUnreachable = (options?: SecondParameter<typeof client>, signal?: AbortSignal) => {
return client<number[]>(
{ url: `/snip/unreachable`, method: 'GET', signal },
{ timeout: 30000, ...options }
)
}
examples consumer:
useGetFooUnreachable({ request: { timeout: 10_000 } })
useGetFooUnreachable({ query: { gcTime: 0 }, request: { headers: { 'foo': 'bar' } })