convex-backend icon indicating copy to clipboard operation
convex-backend copied to clipboard

useQuery via object

Open ianmacartney opened this issue 3 weeks ago • 6 comments

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.

ianmacartney avatar Nov 20 '25 18:11 ianmacartney

My biggest question right now is around the skip arg.

  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 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.

ianmacartney avatar Nov 20 '25 18:11 ianmacartney

I'm leaning towards only having useQuery(someCondition ? "skip" : { query, args: typeSafeArgs })

ianmacartney avatar Nov 20 '25 19:11 ianmacartney

My biggest question right now is around the skip arg.

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 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.

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 avatar Nov 20 '25 21:11 chenxin-yan

@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, ..}

ianmacartney avatar Nov 21 '25 03:11 ianmacartney

@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.

chenxin-yan avatar Nov 24 '25 21:11 chenxin-yan

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 avatar Nov 27 '25 22:11 ianmacartney

@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

chenxin-yan avatar Nov 28 '25 03:11 chenxin-yan