useQuery via object
const { status, value, error } = useQuery({
query: api.foo.bar,
args: { ... },
initialValue: someCachedValue,
throwOnError: true,
});
Allow calling useQuery with an object instead of (api.foo.bar, args)
- adds initialValue support
- allows returning the error instead of throwing
- unifies usePreloadedQuery, enabling skipping
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.
My biggest question right now is around the skip arg.
- Passing
{ skip: shouldSkip, ... }or"skip"is redundant - ideally we start with just one. - If it's true, the args type might not match (e.g.
{ skip: !!userId, args: { userId }, ...}- if userId is required, then we need a more complicated type here such that when skip is truthy args isn't type-checked. I'm hesitant to complicate the types if avoidable. - Passing
shouldSkip? "skip" : {...}makes the types easier
I'm also hesitant to make args optional when they can be empty - seems like more trouble than it's worth for libraries to have to support.
I'm leaning towards only having useQuery(someCondition ? "skip" : { query, args: typeSafeArgs })
My biggest question right now is around the
skiparg.1. Passing `{ skip: shouldSkip, ... }` or `"skip"` is redundant - ideally we start with just one. 2. If it's true, the args type might not match (e.g. `{ skip: !!userId, args: { userId }, ...}` - if userId is required, then we need a more complicated type here such that when skip is truthy args isn't type-checked. I'm hesitant to complicate the types if avoidable. 3. Passing `shouldSkip? "skip" : {...}` makes the types easierI'm also hesitant to make
argsoptional when they can be empty - seems like more trouble than it's worth for libraries to have to support.
Agree that shouldSkip? "skip" : {...} might be the best for now, and have you take a look at tanstack query's enabled implementation yet?
Also, if we implement skip option, do you think its a good idea to use discriminated union so that when skip is true, the args field would not exists. args only exists when skip is false or is undefined. will this make sure args isn't type-checked when the query is skipped?
@chenxin-yan I explored it in #262 (not making args missing, but unknown, since you want to do something like useQuery({query: api.foo.bar, args: { userId }, skip: !userId }). Unfortunately the type discrimination doesn't work, so you're left with !userId ? { skip: true } : {args: { userId}, ...} which may as well be the "skip" sentinel.
One way to alleviate the "I don't like that it's a magic string" would be to export const skipToken = "skip" as const"; and use !userId? skipToken : { query, args, ..}
@chenxin-yan I explored it in #262 (not making args missing, but unknown, since you want to do something like
useQuery({query: api.foo.bar, args: { userId }, skip: !userId }). Unfortunately the type discrimination doesn't work, so you're left with!userId ? { skip: true } : {args: { userId}, ...}which may as well be the "skip" sentinel.One way to alleviate the "I don't like that it's a magic string" would be to
export const skipToken = "skip" as const";and use!userId? skipToken : { query, args, ..}
I see. Although I'm not a big fan of magic string but I think just use "skip" instead of export const skipToken = "skip" as const"; is superior because your editor will autocomplete for "skip" and you will have to remember to use skipToken in contrast. Anyways, using "skip" in this case if all good imo.
As I was discussing this with a colleague, we realized that skipping when
you already have preloaded data is less common of a need, versus the more
specific need of requiring auth. If usePreloadedQuery accepted a
requireAuth: true flag which returned the preloaded data until it was
authenticated, that would be another way to solve your initial issue. Is
that right?
On Mon, Nov 24, 2025 at 1:25 PM Chenxin Yan @.***> wrote:
chenxin-yan left a comment (get-convex/convex-backend#261) https://github.com/get-convex/convex-backend/pull/261#issuecomment-3572787847
@chenxin-yan https://github.com/chenxin-yan I explored it in #262 https://github.com/get-convex/convex-backend/pull/262 (not making args missing, but unknown, since you want to do something like useQuery({query: api.foo.bar, args: { userId }, skip: !userId }). Unfortunately the type discrimination doesn't work, so you're left with !userId ? { skip: true } : {args: { userId}, ...} which may as well be the "skip" sentinel.
One way to alleviate the "I don't like that it's a magic string" would be to export const skipToken = "skip" as const"; and use !userId? skipToken : { query, args, ..}
I see. Although I'm not a big fan of magic string but I think just use "skip" instead of export const skipToken = "skip" as const"; is superior because your editor will autocomplete for "skip" and you will have to remember to use skipToken in contrast. Anyways, using "skip" in this case if all good imo.
— Reply to this email directly, view it on GitHub https://github.com/get-convex/convex-backend/pull/261#issuecomment-3572787847, or unsubscribe https://github.com/notifications/unsubscribe-auth/AACZQWYLZEQF4JSOTQ7K6SL36NZTXAVCNFSM6AAAAACMXIBBXKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZTKNZSG44DOOBUG4 . You are receiving this because you were mentioned.Message ID: @.***>
@ianmacartney Agree, and there should be a requireAuth option when initialing the convex client and user can override that with individual useQuery call with their own requireAuth