skip-token support
Description
Would be nice to be able to do this:
useQuery({
...heyApiOptions(!deploymentId ? skipToken : { path: { deploymentId } })
})
I am also looking forward to a feature like that! I currently solve that with enabled and ! operator but that isn't really ts friendly for me and my setup.
@jofflin one limitation I pointed out is it needs to be figured out how to make this feature work with SDK calls such as foo(id, query, body), i.e. multiple arguments as opposed to the single argument they support today. As such, this feature is blocked by that to avoid back and forth
@jofflin one limitation I pointed out is it needs to be figured out how to make this feature work with SDK calls such as
foo(id, query, body), i.e. multiple arguments as opposed to the single argument they support today. As such, this feature is blocked by that to avoid back and forth
Makes sense. I am not too deep into the topic but a solution i could think of is something like:
- Provide configuration Option like automaticSkipToken
- This converts the
queryOptions(options: Options<GetUserOrganizationsData>) => return queryOptions(...)queryOptions(options: Options<Partial<GetUserOrganizationsData>>) => return queryOptions(...)inside the queryOptions there is then the comparison if all mandatory fields are set, if not it uses skipToken in the react-query.gen.ts directly.
I created this helper for now that does exactly that
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object
? DeepPartial<T[P]> | null | undefined
: T[P] | null | undefined;
};
type OptionalOptions<TData extends TDataShape> = Partial<
Omit<Options<TData>, 'path' | 'query' | 'body'>
> & {
path?: DeepPartial<TData['path']>;
query?: DeepPartial<TData['query']>;
body?: DeepPartial<TData['body']>;
};
function hasAllRequiredValues(obj: unknown): boolean {
if (obj === null || obj === undefined) {
return false;
}
if (typeof obj !== 'object') {
return true;
}
if (Array.isArray(obj)) {
return obj.every((item) => hasAllRequiredValues(item));
}
// For objects, check all values recursively
return Object.values(obj).every((value) => {
if (value === null || value === undefined) {
return false;
}
if (typeof value === 'object') {
return hasAllRequiredValues(value);
}
return true;
});
}
export function createSafeQueryOptions<
TData extends TDataShape,
TQueryOptionsResult,
>(
queryOptionsFn: (options: Options<TData>) => TQueryOptionsResult,
options?: OptionalOptions<TData>
): TQueryOptionsResult {
// If no options provided or options is empty, use skipToken
if (!options || Object.keys(options).length === 0) {
return { ...queryOptionsFn(options as Options<TData>), queryFn: skipToken };
}
// Check if all required values in path, query, and body are defined
const pathValid = !options.path || hasAllRequiredValues(options.path);
const queryValid = !options.query || hasAllRequiredValues(options.query);
const bodyValid = !options.body || hasAllRequiredValues(options.body);
// If any required parameter is missing, use skipToken
if (!pathValid || !queryValid || !bodyValid) {
return { ...queryOptionsFn(options as Options<TData>), queryFn: skipToken };
}
// All required parameters are present, call the queryOptions function
// TypeScript knows this is safe because we've validated all values
return queryOptionsFn(options as Options<TData>);
}