T
TanStack•2y ago
adverse-sapphire

validateSearch with superstruct / valibot

trying to use superstruct to validateSearch because the of the zod bundle size, seems to give an error. Am I doing it wrong? or even better, is there a way to lazy load zod when doing validateSearch so it wont be in the initial bundle size?
No description
39 Replies
adverse-sapphire
adverse-sapphireOP•2y ago
same thing with valibot
No description
deep-jade
deep-jade•2y ago
validateSearch expects a SearchSchemaValidator:
export type SearchSchemaValidator<TInput, TReturn> =
| SearchSchemaValidatorObj<TInput, TReturn>
| SearchSchemaValidatorFn<TInput, TReturn>

export type SearchSchemaValidatorObj<TInput, TReturn> = {
parse?: SearchSchemaValidatorFn<TInput, TReturn>
}

export type SearchSchemaValidatorFn<TInput, TReturn> = (
searchObj: TInput,
) => TReturn
export type SearchSchemaValidator<TInput, TReturn> =
| SearchSchemaValidatorObj<TInput, TReturn>
| SearchSchemaValidatorFn<TInput, TReturn>

export type SearchSchemaValidatorObj<TInput, TReturn> = {
parse?: SearchSchemaValidatorFn<TInput, TReturn>
}

export type SearchSchemaValidatorFn<TInput, TReturn> = (
searchObj: TInput,
) => TReturn
So it's either a function or an object that has a parse method. Since a zod schema has a parse method, you can directly use it. For other validators, you have to wrap the schema parsing into a function. For valibot, this looks like this:
import { object, parse, string } from 'valibot';

const schema = object({
foo: string(),
bar: string(),
});

const postsRoute = new Route({
getParentRoute: () => rootRoute,
path: 'posts',
loader: () => fetchPosts(),
component: PostsComponent,
validateSearch: (input) => parse(schema, input),
});
import { object, parse, string } from 'valibot';

const schema = object({
foo: string(),
bar: string(),
});

const postsRoute = new Route({
getParentRoute: () => rootRoute,
path: 'posts',
loader: () => fetchPosts(),
component: PostsComponent,
validateSearch: (input) => parse(schema, input),
});
Full example https://codesandbox.io/p/devbox/hopeful-kowalevski-5gn5k2?file=%2Fsrc%2Fmain.tsx%3A214%2C27
adverse-sapphire
adverse-sapphireOP•2y ago
Hey, thank you for the help I followed the example, but there are no types using the "useSearch" seems to be the same in the full example you provided.
No description
No description
adverse-sapphire
adverse-sapphireOP•2y ago
No description
deep-jade
deep-jade•2y ago
I just tried it out locally, there I get the types as expected. Looks like the codesandbox has some issues with the valibot dependency, maybe I did not install it correctly in this environment.
No description
No description
adverse-sapphire
adverse-sapphireOP•2y ago
maybe its something with using const api = new RouteApi?
deep-jade
deep-jade•2y ago
can you provide a minimal example that does not work locally?
adverse-sapphire
adverse-sapphireOP•2y ago
import { FileRoute, redirect } from "@tanstack/react-router";
import { object, parse, string } from "valibot";
export const Route = new FileRoute("/login").createRoute({
beforeLoad: ({ context }) => {
if (context.session !== null) {
throw redirect({
to: "/",
});
}
},
validateSearch: (input) =>
parse(
object({
redirect: string(),
}),
input,
),
});
import { FileRoute, redirect } from "@tanstack/react-router";
import { object, parse, string } from "valibot";
export const Route = new FileRoute("/login").createRoute({
beforeLoad: ({ context }) => {
if (context.session !== null) {
throw redirect({
to: "/",
});
}
},
validateSearch: (input) =>
parse(
object({
redirect: string(),
}),
input,
),
});
import * as React from "react";
import LoginForm from "@/components/forms/LoginForm";
import { RouteApi } from "@tanstack/react-router";

const api = new RouteApi({ id: "/login" });

export const component = function RegisterComponent() {
const { supabase } = api.useRouteContext();
const { redirect } = api.useSearch(); // type error

return (
<div className="flex flex-col items-center justify-center p-16 ">
<h3>Login - Worker</h3>
<LoginForm auth={supabase} redirect={redirect ?? "/"} />
</div>
);
};
import * as React from "react";
import LoginForm from "@/components/forms/LoginForm";
import { RouteApi } from "@tanstack/react-router";

const api = new RouteApi({ id: "/login" });

export const component = function RegisterComponent() {
const { supabase } = api.useRouteContext();
const { redirect } = api.useSearch(); // type error

return (
<div className="flex flex-col items-center justify-center p-16 ">
<h3>Login - Worker</h3>
<LoginForm auth={supabase} redirect={redirect ?? "/"} />
</div>
);
};
these are the files code (if it helps), I will try to make a stackblitz a bit later, im playing with a friend rn
deep-jade
deep-jade•2y ago
which type error do you get there?
adverse-sapphire
adverse-sapphireOP•2y ago
No description
adverse-sapphire
adverse-sapphireOP•2y ago
ok i will try to reproduce it welp, seems to work on the stackblitz. I hate when that happens unless thats another windows specific issue, which will be weird cuz no idea what the validation has to do with it, but dont really know what causes this
deep-jade
deep-jade•2y ago
try restarting TS Server in VS Code?
adverse-sapphire
adverse-sapphireOP•2y ago
tried reopening vscode, reloading a bunch of times, doesnt help
adverse-sapphire
adverse-sapphireOP•2y ago
this seems to get it right, but using the api.useSearch() doesnt work and gives me an empty {} as the type. did the exact thing in stackblitz and it worked there fine 😭
No description
deep-jade
deep-jade•2y ago
this screenshot does not show an error though
adverse-sapphire
adverse-sapphireOP•2y ago
thats the validate search, that part seems to work
adverse-sapphire
adverse-sapphireOP•2y ago
the error is here
No description
deep-jade
deep-jade•2y ago
is your routeTree.gen.ts regenerated? if you can share the whole repo, I can quickly clone and try it on a mac
adverse-sapphire
adverse-sapphireOP•2y ago
yep sadly its a private repo, its job stuff its a pretty big repo as well, mono repo with like.. 13 packages, not sure if you can run it without the envs too would a vc help?
deep-jade
deep-jade•2y ago
what's that?
adverse-sapphire
adverse-sapphireOP•2y ago
voice chat I did one with Tanner like a week ago with some other issue I couldnt reproduce on stackblitz (since it was a windows specific issue) and he managed to figure the problem looking at the node modules for the router No pressure tho, ofc. Im just looking for ways to get this solved
deep-jade
deep-jade•2y ago
I doubt I can spot stuff just by looking at it, usuallly I need to poke by removing stuff until it breaks/works
deep-jade
deep-jade•2y ago
did you try removing the node_modules folder and reinstalling?
No description
adverse-sapphire
adverse-sapphireOP•2y ago
will try that Didnt fix it 😔 the minute I return to zod, it works great. only issue with zod is that it makes my index bundle size bigger by quite a bit, and I would like to avoid that
deep-jade
deep-jade•2y ago
did you try with superstruct? just trying to find out if this is a valibot special issue
adverse-sapphire
adverse-sapphireOP•2y ago
I could try, do you know how I parse it?
deep-jade
deep-jade•2y ago
GitHub
GitHub - ianstormtaylor/superstruct: A simple and composable way to...
A simple and composable way to validate data in JavaScript (and TypeScript). - GitHub - ianstormtaylor/superstruct: A simple and composable way to validate data in JavaScript (and TypeScript).
deep-jade
deep-jade•2y ago
assert(data, schema)
adverse-sapphire
adverse-sapphireOP•2y ago
No description
deep-jade
deep-jade•2y ago
import { is, object, number, string } from 'superstruct'

const schema = object({
redirect: string()
})

...

validateSearch: (input) => {
if (is(input, schema)) {
return input;
}
throw new Error('invalid')
}
import { is, object, number, string } from 'superstruct'

const schema = object({
redirect: string()
})

...

validateSearch: (input) => {
if (is(input, schema)) {
return input;
}
throw new Error('invalid')
}
maybe?
adverse-sapphire
adverse-sapphireOP•2y ago
I gotta go to bed, I need to wake up early 😴 Will try that tomorrow, and update you, for now I'll stick to the zod version
deep-jade
deep-jade•2y ago
adverse-sapphire
adverse-sapphireOP•2y ago
Hey, update. the issue remains.
No description
adverse-sapphire
adverse-sapphireOP•2y ago
No description
adverse-sapphire
adverse-sapphireOP•2y ago
reloaded too. and the moment I return to zod its fixed
deep-jade
deep-jade•2y ago
what happens if you use superstruct outside of react router? so just this:
import { is, object, number, string } from 'superstruct'

const schema = object({
redirect: string()
})


const validate = (input) => {
if (is(input, schema)) {
return input;
}
throw new Error('invalid')
}

const result = validate({redirect:"foo"});
import { is, object, number, string } from 'superstruct'

const schema = object({
redirect: string()
})


const validate = (input) => {
if (is(input, schema)) {
return input;
}
throw new Error('invalid')
}

const result = validate({redirect:"foo"});
what's the type of result?
adverse-sapphire
adverse-sapphireOP•2y ago
well I get type error in input but result works
No description
No description
deep-jade
deep-jade•2y ago
so since it works on stackblitz in the simplified example, and since types are correct when using outside of router, you neeed to remove stuff from your application until it works. e.g. remove all other routes. does it work then?
rival-black
rival-black•2y ago
So i was reading this because i also had some inference Problems with search. Apparently (atleast for TS in VSC) the order of options in Route/FileRuute makes a difference. Putting validateSearch first, makes search in beforeLoad gets inferred correctly, while putting it last TS is unable to do so (resulting in an empty object)

Did you find this page helpful?