Allow remote function argument to be optional
Describe the problem
I'd like to enhance a remote function with additional but optional behavior.
In my remote function, I'm getting the user's session. By default, this function validates the session based on local JWT information from a cookie (i.e. no additional network request). However, there may be scenarios where I still want the session data but also need to verify the user of that session against a third-party backend server (they could have been banned, signed out globally from another device, etc, but their JWT is still valid).
import { getRequestEvent, query } from "$app/server"
import type { Session } from "@supabase/supabase-js"
import { getValidatedSession } from "./shared.js"
import * as v from "valibot"
export const getSession = query(
v.optional(v.boolean()),
async (validate_user): Promise<Session | null> => {
const { locals } = getRequestEvent()
return await getValidatedSession(locals.supabase, validate_user)
}
)
The code works, but throws a type error whenever I'm not passing an argument.
Describe the proposed solution
This can be resolved if we make a change to the type (used AI here):
export type RemoteQueryFunction<Input, Output> = (
- arg: Input
+ ...args: undefined extends Input ? [arg?: Input] : [arg: Input]
) => RemoteQuery<Output>;
Which yields no type error when passing in no arg,
but still, correctly, shows a type error if a passed-in arg doesn't match the schema
Rinse and repeat for any other remote function types this should apply to.
Importance
would make my life easier
The proposed solution is imo better than what we currently have, but does have the limitation that now it's impossible to have required but possibly undefined arguments, which are sometimes useful. I bet there's some trickery we could do to allow both...
Input extends void might work
I tried to figure this out on my own, but had no luck. Then went to AI, but still couldn't get it working. I'll leave it up to the experts at this point. Thanks for considering this!
Was good to learn the differences between things like Input extends undefined, undefined extends Input, etc
With more testing and prodding, I still can't find a solution that satisfies the "required, but undefinedable" case.
Regarding my immediate use case: after watching Simon's latest video on remote functions, I realized I can create remote function B that implements remote function A and does the additional checking.
If you'd like to continue pursuing a type solution, then the issue can remain open; if not, I'm good with it being closed.
i feel like the fix would definitely be a dx improvement and in the rare case someone would really want to require an explicit arg (even if undefined) they could wrap it in an object instead: z.object({ value: z.string().optional() })
vs forcing the use of the current workaround for the common case:
const filterSchema = z.union([z.void(), z.object({...})]);
export const getCategories = query(filterSchema, async (filter) => { })
more importantly, it also prevents passing some arbitrary args like ({}) to bypass the ts error resulting in inconsistent cache keys