T
TanStack•7mo ago
continuing-cyan

Validators and schema

Hey, I'm reading through the getting started docs and have a question about validators - do they accept only zod schemas or do they allow any other validation libraries? https://tanstack.com/start/latest/docs/framework/react/learn-the-basics#mutations There's a nice spec Standard Schema, and was wondering if I could use other validation library here. https://github.com/standard-schema/standard-schema
GitHub
GitHub - standard-schema/standard-schema: A standard interface for ...
A standard interface for TypeScript schema validation libraries - standard-schema/standard-schema
Learn the Basics | TanStack Start React Docs
This guide will help you learn the basics behind how TanStack Start works, regardless of how you set up your project. Dependencies TanStack Start is (currently) powered by , and . TanStack Router: A...
86 Replies
extended-salmon
extended-salmon•7mo ago
maybe im missing something but isn't Zod done in the standard schema spec. But the Standard Schema website says tanstack router and tanstack form take the standard schema spec
continuing-cyan
continuing-cyanOP•7mo ago
Yes, zod is spec compliant, and router is on the list. However, I was under the impression that createServerFn comes from Start, so while it's still in beta, it can have a few rough edges such as (potentially) this one. This PR adds Standard Schema for search params, but the same doesn't have to be true about createServerFn https://github.com/TanStack/router/pull/2602
GitHub
feat(react-router): implement standard schema into router core by c...
This PR implements Standard Schema in router core. Currently only valibot implements this but the intention is for all validation libraries to implement it. Which means we don't need to sup...
vicious-gold
vicious-gold•7mo ago
StandardSchema is more for library authors. For validator libraries, it's about exposing the their validation callers using a single unified API with standardized type signatures. For external libraries, it's about consuming the validators using that standardized API. It's what allows us to easily plug in Zod, Valibot, Arktype, etc. without having to have some crazy diverging types for easy library. Create server function should be spec compliant. If not, please feel free to open an issue. That'd definitely be something we'd want resolved before 1.0
continuing-cyan
continuing-cyanOP•7mo ago
Exactly the case I had in mind - the Tanstack Start could support the Standard Schema for validators of server functions, just like the Router does support it for search params. I started to read the types to figure it out this way, but I wasn't prepared to se soo many of them, so I bailed out for now. I guess I should finally setup example project and just test it out - seems like an easier way to figure it out than just by looking at the router's monorepo (at least for now).
vicious-gold
vicious-gold•7mo ago
Looks like it already does. The type for ResolveValidatorInput, which is an export from router-core, already has StandardSchema implemented.
No description
No description
continuing-cyan
continuing-cyanOP•7mo ago
I created two server functions - one with Zod and another one with Arktype. I intentionally called both of them with invalid data to verify that the validation works. The one with Zod returned 500 and {"$error": { zod error here }}. The one with Arktype returned 200 if I used the Arktype in the same fashion. I had to wrap it with custom check in validator to ensure that it behaves the same way as the serverFn with Zod as validator.
const substractSchema = type({
count: "number",
someOtherProp: "string",
});

export const substractCountArktype = createServerFn({ method: "POST" })
.validator((data: unknown) => {
const result = substractSchema(data);

if (result instanceof type.errors) {
throw new Error(result.summary);
}

return result;
})
.handler(async ({ data }) => {
storage.updateCount(data.count);
});
const substractSchema = type({
count: "number",
someOtherProp: "string",
});

export const substractCountArktype = createServerFn({ method: "POST" })
.validator((data: unknown) => {
const result = substractSchema(data);

if (result instanceof type.errors) {
throw new Error(result.summary);
}

return result;
})
.handler(async ({ data }) => {
storage.updateCount(data.count);
});
Is this expected usage? Zod for comparison
const substractSchema = z.object({
count: z.number(),
someOtherProp: z.string(),
});

export const substractCountZod = createServerFn({ method: "POST" })
.validator((data: unknown) => {
return substractSchema.parse(data);
})
.handler(async ({ data }) => {
storage.updateCount(-data.count);
});
const substractSchema = z.object({
count: z.number(),
someOtherProp: z.string(),
});

export const substractCountZod = createServerFn({ method: "POST" })
.validator((data: unknown) => {
return substractSchema.parse(data);
})
.handler(async ({ data }) => {
storage.updateCount(-data.count);
});
correct-apricot
correct-apricot•7mo ago
wouldnt you just do this?
export const substractCountZod = createServerFn({ method: "POST" })
.validator(substractSchema)
.handler(async ({ data }) => {
storage.updateCount(-data.count);
});
export const substractCountZod = createServerFn({ method: "POST" })
.validator(substractSchema)
.handler(async ({ data }) => {
storage.updateCount(-data.count);
});
continuing-cyan
continuing-cyanOP•7mo ago
To be honest I just took it from the docs, and was more focused on getting it to work than look prettier
correct-apricot
correct-apricot•7mo ago
no there might be differences in execution standard schema validators use a different API internally than you did
continuing-cyan
continuing-cyanOP•7mo ago
Also in the meantime I figured out that arktype can be configured to throw an error when passing invalid data, as it doesn't do it by default
correct-apricot
correct-apricot•7mo ago
ah interesting
continuing-cyan
continuing-cyanOP•7mo ago
Let my try the code you've sent, and get back in a minute with observations Okay, now both of these return 500, even without configuring arktype to throw. However, in case of arktype the error when passing the schema directly to the validator becomes non-understandable. With the manual instanceof check and throw:
someOtherProp must be a string (was missing)
someOtherProp must be a string (was missing)
With directly passing arktype schema to the validator:
Converting circular structure to JSON\n --> starting at object with constructor 'ArkErrors'\n | index 0 -> object with constructor 'ArkError'\n | property 'ctx' -> object with constructor 'Traversal'\n --- property 'errors' closes the circle
Converting circular structure to JSON\n --> starting at object with constructor 'ArkErrors'\n | index 0 -> object with constructor 'ArkError'\n | property 'ctx' -> object with constructor 'Traversal'\n --- property 'errors' closes the circle
These come from the message field in error object.
{"$error":{"message":"here"}}
{"$error":{"message":"here"}}
correct-apricot
correct-apricot•7mo ago
do we maybe need to adapt this PR to start as well? https://github.com/TanStack/router/pull/3155
GitHub
fix(react-router): Pass SearchParamError#cause in validateSearch by...
What Ensure that SearchParamError#cause contains the original parsing error thrown/returned by the validateSearch implementation configured by the user. Notable example: passing valibot schema dire...
continuing-cyan
continuing-cyanOP•7mo ago
Might be, I don't know to be honest, but looks like worth trying
correct-apricot
correct-apricot•7mo ago
can you please create a github issue with a reproducer?
continuing-cyan
continuing-cyanOP•7mo ago
Sure! I'll try to apply similar fix to the one in above-linked PR. If I fail then I'll create an issue So far I've failed to build all packages on the Windows, but it seems to build fine in WSL
correct-apricot
correct-apricot•7mo ago
WSL is preferable really
continuing-cyan
continuing-cyanOP•7mo ago
Yeah, I figured it out 😆 Seems that the issue stems from Arktype errors not being compatible with JSON.stringify (at least in this case). I've tried to replace the basic Error with a custom one like it was in the PR, but when .stringify is called, a TypeError is thrown. However, I do not know if this is true for other libraries, but calling toString() yields the correct message:
// react-start-client/src/createServerFn.ts
function execValidator(validator: AnyValidator, input: unknown): unknown {
if (validator == null) return {}

if ('~standard' in validator) {
const result = validator['~standard'].validate(input)

if (result instanceof Promise)
throw new ServerFnValidatorError('Async validation not supported')

if (result.issues)
throw new ServerFnValidatorError(
// JSON.stringify(result.issues, undefined, 2), // this throws
result.issues.toString(), // this doesn't
{
cause: result,
},
)

return result.value
}

if ('parse' in validator) {
return validator.parse(input)
}

if (typeof validator === 'function') {
return validator(input)
}

throw new Error('Invalid validator type!')
}
// react-start-client/src/createServerFn.ts
function execValidator(validator: AnyValidator, input: unknown): unknown {
if (validator == null) return {}

if ('~standard' in validator) {
const result = validator['~standard'].validate(input)

if (result instanceof Promise)
throw new ServerFnValidatorError('Async validation not supported')

if (result.issues)
throw new ServerFnValidatorError(
// JSON.stringify(result.issues, undefined, 2), // this throws
result.issues.toString(), // this doesn't
{
cause: result,
},
)

return result.value
}

if ('parse' in validator) {
return validator.parse(input)
}

if (typeof validator === 'function') {
return validator(input)
}

throw new Error('Invalid validator type!')
}
But it seems to be not enough, as in the end the unreadable message is still being returned on 500 response
correct-apricot
correct-apricot•7mo ago
what happens if you use arktype in validateSearch? does this work?
continuing-cyan
continuing-cyanOP•7mo ago
Haven't tried, tbh I haven't used anything from tanstack other than query
correct-apricot
correct-apricot•7mo ago
hm? arent you using start?
continuing-cyan
continuing-cyanOP•7mo ago
Well, only since today 😛
correct-apricot
correct-apricot•7mo ago
ah
continuing-cyan
continuing-cyanOP•7mo ago
(I know, start is based on router) I'll try it tomorrow/Friday, as It's quite late for me If I do not provide the cause then I get the 200, but with the expected message
continuing-cyan
continuing-cyanOP•7mo ago
I was looking at other PRs that introduced Standard Schema support in other libraries. From what it seems, other libraries just structure the issues to their own format: T3-env https://github.com/t3-oss/t3-env/pull/299/files#diff-adae0bae3aba11742eacaa074b2d6107ed07f1e9e547ca373419587c1c2543c6R97-R105 React-Hook-Form https://github.com/react-hook-form/resolvers/pull/738/files#diff-24e62aaa55dccd464160d06783b58829c53f23b2d6e340fae16d24b41521757fR6-R21 This way it's possible to break free from errors that each library can throw differently, as the message and (probably path) are the ones which are most useful in case of validation (at least from my PoV). I personally do not care about the underlying error thrown on failed validation, but more about what does the error says about the data passed to it - I might be not seeing something there, so please correct me if I'm wrong.
/** The issue interface of the failure output. */
export interface Issue {
/** The error message of the issue. */
readonly message: string;
/** The path of the issue, if any. */
readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
}
/** The issue interface of the failure output. */
export interface Issue {
/** The error message of the issue. */
readonly message: string;
/** The path of the issue, if any. */
readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
}
Also Standard Schema itself offers some utilities such as getDotPath (more suited towards form-supporting libs) and a SchemaError, that can wrap the issues. Maybe the SchemaError should be used instead of calling the JSON.stringify directly? https://github.com/standard-schema/standard-schema/blob/main/packages/utils/src/SchemaError/SchemaError.ts
GitHub
standard-schema/packages/utils/src/SchemaError/SchemaError.ts at ma...
A standard interface for TypeScript schema validation libraries - standard-schema/standard-schema
continuing-cyan
continuing-cyanOP•7mo ago
I would appreciate some comment before I proceed further @Manuel Schiller
correct-apricot
correct-apricot•7mo ago
sorry I have not been too deeply involved in the runtime parts here. @Sean Cassiere do you have insights?
vicious-gold
vicious-gold•7mo ago
Fabian (author of Valibot and one of the authors of StandardSchema) did the integration of StandardSchema for Router. For any changes, we'd probably want to align with what Form is doing. Looking at Form's source, they look to be doing what you mentioned. I'd certainly be accepting of any PR's strengthening our StandardSchema usage.
continuing-cyan
continuing-cyanOP•7mo ago
By Form, you mean TanStack's Form or RHF (React-Hook-Form)? Because TSF also has a PR for it, also featuring a custom transform of issues, depending on whether they come from the form or particular fields
GitHub
feat: native support for standard schema libraries by Balastrong · ...
This PR closes #1015 Initial support for Standard Schema using a validatorAdapter-like structure embedded in form-core TODO: Make it work without explicitly adding an adapter Example Doc More ...
correct-apricot
correct-apricot•7mo ago
yes tanstack form pinging @Leonardo
continuing-cyan
continuing-cyanOP•7mo ago
Alright, wanted to make sure
correct-apricot
correct-apricot•7mo ago
@Leonardo would love to get your insights about standard schema here how to handle errors, stringify them etc see above
inland-turquoise
inland-turquoise•7mo ago
We implement the standard schema interface (aka we expect validators to have a validate function with the expected signature) but what happens inside the validator is out of our control With the Form latest refactor we directly exposed the error produced by the validator so users should have full control
correct-apricot
correct-apricot•7mo ago
so errors are not standardized?
inland-turquoise
inland-turquoise•7mo ago
They are, but it's the schema library doing that if I'm not mistaken
conscious-sapphire
conscious-sapphire•7mo ago
Hey 👋 happy to answer any Standard Schema questions. Feel free to ping me.
correct-apricot
correct-apricot•7mo ago
so the main question is how to stringify errors in a validator agnostic way other libs seem to extract validation errors like this into a custom structure. is this what we should do as well on the server and then JSON.stringify? but arktype's errors were not JSON stringifiable
continuing-cyan
continuing-cyanOP•7mo ago
The issue might lie with the arktype itself. For example invoking schema['~standard'].validate(value) on Zod schema, gives the expected spec-compliant issue array. But for Arktype returns ArkErrors
continuing-cyan
continuing-cyanOP•7mo ago
No description
No description
continuing-cyan
continuing-cyanOP•7mo ago
For Valibot issues are a bit more info-packed, but still compliant (have both the message and path props)
No description
continuing-cyan
continuing-cyanOP•7mo ago
Sorry, didn't notice on Arktype that it contains issues. But it is a function instead of an array of issues
// Standard Schema

/** The result interface if validation fails. */
export interface FailureResult {
/** The issues of failed validation. */
readonly issues: ReadonlyArray<Issue>;
}
// Standard Schema

/** The result interface if validation fails. */
export interface FailureResult {
/** The issues of failed validation. */
readonly issues: ReadonlyArray<Issue>;
}
I've tried with the newest version of arktype too. I'm going to ask about it in the arktype's community
correct-apricot
correct-apricot•7mo ago
asking it its discord? please tag me there
continuing-cyan
continuing-cyanOP•7mo ago
Yes, in discord, but I will probably get to it in the afternoon. I'll tag you there once I post it Posted and pinged https://discord.com/channels/957797212103016458/1347584777179365426
adverse-sapphire
adverse-sapphire•7mo ago
This seems to suggest the issues are stringifyable https://standardschema.dev/
Standard Schema
Standard Schema
A common interface for TypeScript validation libraries
continuing-cyan
continuing-cyanOP•7mo ago
It kinda does, but could this example usage be written after the spec was established? Anyway, for now the Arktype doesn't support toJSON on ArkError, so it cannot be used in such way. A one solution that comes to my mind for time being is to create a custom utility or exception that handles the ArkError, but this is rather a workaround for underlying issue that will be solved eventually (probably)
continuing-cyan
continuing-cyanOP•7mo ago
GitHub
JSON serializable ArkErrors · Issue #1352 · arktypeio/arktype
Would make some Standard Schema integrations easier, see standard-schema/standard-schema#49
adverse-sapphire
adverse-sapphire•7mo ago
The example here shows stringify. https://github.com/standard-schema/standard-schema/tree/main?tab=readme-ov-file#how-do-i-accept-standard-schemas-in-my-library . hm, so when using arktype these are not meant to be serialised? The problem from our point of view is if one library does not support this then we cannot assume any of them do
GitHub
GitHub - standard-schema/standard-schema: A standard interface for ...
A standard interface for TypeScript schema validation libraries - standard-schema/standard-schema
continuing-cyan
continuing-cyanOP•7mo ago
I had the same example in mind but on the website
The problem from our point of view is if one library does not support this then we cannot assume any of them do
True, that's why I was thinking in the meantime that maybe pulling out just the fields that are guaranteed by the spec to be there could be a better approach. This way Tanstack wouldn't have to rely on whether the validation library of choice supports serializable errors or not I do not know what to do with the error itself then, but another question is whether the whole error with stacktrace should really be returned in the response when validation fails As I illustrated here, the issues can have much more props than just message and path https://discord.com/channels/719702312431386674/1346288913521705002/1347482036872417320 This might also clarify why some libraries have chosen structuring these errors on their own https://discord.com/channels/719702312431386674/1346288913521705002/1347165037172883486 (but doesn't necessarily mean that was the motivation, could be just a coincidental decision) At the end of the day, I think this is more a responsibility of authors of validation libraries, but I also agree with the quoted statement
conscious-sapphire
conscious-sapphire•7mo ago
This is probably what TanStack Form should do:
import type { StandardSchemaV1 } from "@standard-schema/spec";
import { getDotPath } from "@standard-schema/utils";

async function getFormErrors(schema: StandardSchemaV1, data: unknown) {
const result = await schema["~standard"].validate(data);
const formErrors: string[] = [];
const fieldErrors: Record<string, string[]> = {};
if (result.issues) {
for (const issue of result.issues) {
const dotPath = getDotPath(issue);
if (dotPath) {
if (fieldErrors[dotPath]) {
fieldErrors[dotPath].push(issue.message);
} else {
fieldErrors[dotPath] = [issue.message];
}
} else {
formErrors.push(issue.message);
}
}
}
return { formErrors, fieldErrors };
}
import type { StandardSchemaV1 } from "@standard-schema/spec";
import { getDotPath } from "@standard-schema/utils";

async function getFormErrors(schema: StandardSchemaV1, data: unknown) {
const result = await schema["~standard"].validate(data);
const formErrors: string[] = [];
const fieldErrors: Record<string, string[]> = {};
if (result.issues) {
for (const issue of result.issues) {
const dotPath = getDotPath(issue);
if (dotPath) {
if (fieldErrors[dotPath]) {
fieldErrors[dotPath].push(issue.message);
} else {
fieldErrors[dotPath] = [issue.message];
}
} else {
formErrors.push(issue.message);
}
}
}
return { formErrors, fieldErrors };
}
You don't have to use our utils package. You can also write the getDotPath on your own.
continuing-cyan
continuing-cyanOP•7mo ago
Form actually does something like that. It uses custom path-to-dot function, but the idea is implemented If you want something similar in tanstack start, then I'm willing to contribute a PR
correct-apricot
correct-apricot•7mo ago
bit out of the loop right now, would that solve the JSON issue?
continuing-cyan
continuing-cyanOP•7mo ago
correct-apricot
correct-apricot•7mo ago
then we should do it just a heads up, we are currently freezing commits to main as we are migrating off vinxi. however, you could create that PR now and resolve any merge conflicts once we are ready we might even merge earlier, just needs coordination so would be good to have that PR in any case
continuing-cyan
continuing-cyanOP•7mo ago
That's nice! I'll create a PR and then resolve the conflicts once the migration is over I should probably notify David from arktype to deprioritize the mentioned issue/task, as this won't be relevant for tanstack anymore, and I felt that this issue put a bit of haste on him
continuing-cyan
continuing-cyanOP•7mo ago
GitHub
fix: extract props from validatior issues by jkazimierczak · Pull R...
Changes Summary Replaces naive JSON serialization of validator issues by extracting known properties first, and then serializing extracted values instead of the error itself. This makes is possible...
continuing-cyan
continuing-cyanOP•7mo ago
While I'm at it, is the 500 response code really appropriate in this case? I think I always seen 400 or 422 for validation error of the request, while 500 was that something failed within the server itself. I guess the 500 comes from throwing the Error within execValidator
correct-apricot
correct-apricot•7mo ago
can we easily change this?
continuing-cyan
continuing-cyanOP•7mo ago
Maybe through rising a custom error instead of just plain error and catching it somewhere? I remember there were some properties on ctx, but would have to look into them to see if response status code is there I'll look into it and let you know
continuing-cyan
continuing-cyanOP•7mo ago
Soo, the error bubbles up to the server function handler, and response is constructed here: https://github.com/TanStack/router/blob/a73bf99f1437bc3f17914f576ae321bee11f7560/packages/react-start-server-functions-handler/src/index.ts#L270 Before that there are functions isRedirect and isNotFound. Validation error could possibly be handled in similar fashion
GitHub
router/packages/react-start-server-functions-handler/src/index.ts a...
🤖 Fully typesafe Router for React (and friends) w/ built-in caching, 1st class search-param APIs, client-side cache integration and isomorphic rendering. - TanStack/router
correct-apricot
correct-apricot•7mo ago
yeah we also had the request from users to throw errors with custom status codes not sure really how to handle this in a generic fashion so let's split this up let's focus on the things you did already and tackle status codes separately
continuing-cyan
continuing-cyanOP•7mo ago
I already have a working version though 😛
correct-apricot
correct-apricot•7mo ago
great for status codes?
continuing-cyan
continuing-cyanOP•7mo ago
Yup
correct-apricot
correct-apricot•7mo ago
as I wrote above we probably (?) need a generic solution so that we dont have special treatment for validator errors but rather allow custom error status codes and validator errors leverage that
continuing-cyan
continuing-cyanOP•7mo ago
Ah, in that sense I'll put my initial scratchpad version into a separate branch and link it here. It won't be included in the PR and you'll see how I've handled that But in case of making it more generic I do not have any opinion nor idea how this should be approached
correct-apricot
correct-apricot•7mo ago
me neither ,yet 😄
continuing-cyan
continuing-cyanOP•7mo ago
Hmm, are we speaking of custom errors thrown by users within the serverFn?
correct-apricot
correct-apricot•7mo ago
yes we might have a github issue for that, let me check no I dont find anything
continuing-cyan
continuing-cyanOP•7mo ago
Then I can see a few approaches: - expose a base error class that users can extend - then it could be detected through instanceof check in the server function handler - provide a set of default exceptions - this is how it's done in Nest.js. This let's user throw one of provided exceptions. But again, this comes down to having a single base error class which is subclassed further
Documentation | NestJS - A progressive Node.js framework
Documentation | NestJS - A progressive Node.js framework
Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).
correct-apricot
correct-apricot•7mo ago
we also would want to install error interceptors, similar to nestjs
continuing-cyan
continuing-cyanOP•7mo ago
I can't give any advice on that, as I was only end user of frameworks so far Standing on shoulders of giants:P But sounds like quite nice feature to be present
correct-apricot
correct-apricot•7mo ago
just saying this needs more thought to be put into
continuing-cyan
continuing-cyanOP•7mo ago
Definitely
continuing-cyan
continuing-cyanOP•7mo ago
continuing-cyan
continuing-cyanOP•7mo ago
And this is how the response looks
No description
correct-apricot
correct-apricot•7mo ago
can we do instanceof ValidatorError on the client?
continuing-cyan
continuing-cyanOP•7mo ago
Haven't checked that I assume the response has to have some other structure in order for error to be rethrown on client, right? Currently I wrapped the call of serverFn with try catch, but nothing happens Probably $error It fails on deserialization
continuing-cyan
continuing-cyanOP•7mo ago
Btw, nice thing about the custom fields on error is that they're visible in the response (issues), but again, I haven't figured why it fails to deserialize
No description
continuing-cyan
continuing-cyanOP•7mo ago
Namely this line throws: https://github.com/TanStack/router/blob/a73bf99f1437bc3f17914f576ae321bee11f7560/packages/react-start-client/src/serializer.ts#L127
serializer.ts:127:25 // error origin

Uncaught (in promise) ValidatorError: {
"root": [
"must be an object (was a number)"
],
"issues": {}
}
at async file:///home/kuba/projects/ts-router/node_modules/.pnpm/h3 1.13.0/node_modules/h3/dist/index.mjs:1978
Caused by: Object { … }
serializer.ts:127:25 // error origin

Uncaught (in promise) ValidatorError: {
"root": [
"must be an object (was a number)"
],
"issues": {}
}
at async file:///home/kuba/projects/ts-router/node_modules/.pnpm/h3 1.13.0/node_modules/h3/dist/index.mjs:1978
Caused by: Object { … }
GitHub
router/packages/react-start-client/src/serializer.ts at a73bf99f143...
🤖 Fully typesafe Router for React (and friends) w/ built-in caching, 1st class search-param APIs, client-side cache integration and isomorphic rendering. - TanStack/router
continuing-cyan
continuing-cyanOP•7mo ago
Well, the instanceof fails on client, no matter if the custom error is thrown directly, or provided as a cause to the regular Error. I've disabled the code in the serverfn handler for now, and used the try catch as described in docs. The instanceof fails
Server Functions | TanStack Start React Docs
What are Server Functions? Server functions allow you to specify logic that can be invoked almost anywhere (even the client), but run only on the server. In fact, they are not so different from an API...
correct-apricot
correct-apricot•7mo ago
yeah we would need to recreate the actual error class on the client @lxsmnsyc wondering whether seroval could help here
continuing-cyan
continuing-cyanOP•7mo ago
Btw, I like how from a simple question about standard schema support this thread turned into a journey of discussing API and implementations, launching debugger session and figuring things out the hard way 😆 I just wanted to spin up a simple Start project and here we are XD
continuing-cyan
continuing-cyanOP•7mo ago
Okay, instanceof fails due to the prototype chain missing. So the deserialization would have to restore the prototype for the newly created Error. This makes instanceof work on the client. But the drawback is that I do not see how this should be made more generic, as for now I passed the ValidatorError directly
// serializer.ts

// From
(v) => {
const err = Object.assign(new Error(v.message), v)
Object.setPrototypeOf(err, ValidatorError.prototype)
return err
}
// serializer.ts

// From
(v) => {
const err = Object.assign(new Error(v.message), v)
Object.setPrototypeOf(err, ValidatorError.prototype)
return err
}
correct-apricot
correct-apricot•7mo ago
if we move over to seroval (https://github.com/lxsmnsyc/seroval) users could register their own "plugins" to serialize their error classes
GitHub
GitHub - lxsmnsyc/seroval: Stringify JS values
Stringify JS values. Contribute to lxsmnsyc/seroval development by creating an account on GitHub.
continuing-cyan
continuing-cyanOP•7mo ago
Also, if you will go with the route of having custom exception hierarchy, remember to preserve the prototype chain. In one of my projects I've used this as a base exception and it worked fine
export class ApplicationError extends Error {
constructor(message: string) {
super(message);

Object.setPrototypeOf(this, new.target.prototype);
this.name = this.constructor.name;

Error.captureStackTrace(this, this.constructor);
}
}
export class ApplicationError extends Error {
constructor(message: string) {
super(message);

Object.setPrototypeOf(this, new.target.prototype);
this.name = this.constructor.name;

Error.captureStackTrace(this, this.constructor);
}
}
Registering "plugins" for de/serialization of custom errors sounds like possible solution. I don't see a way to provide correct error class, but it's necessary for setting the correct prototype Anyway, ending for today 👋

Did you find this page helpful?