profile
viewpoint

chrbala/parse-redux 19

Drop-in replacement for Parse with added Redux goodness!

chrbala/react-audio 17

HTML 5 Audio API in React

chrbala/react-resume-boilerplate 9

a way to jump start writing your resume and cover letter in HTML and CSS

chrbala/horizon-react-webpack-hmr-boilerplate 4

A semi-opinionated boilerplate for horizon-react and webpack, with hot module reloading

chrbala/lmdb-leveldown 2

leveldown compatible binding to LMDB

chrbala/apollo-tooling 0

✏️ Tooling for development and production Apollo workflows

chrbala/chrbala-rollup 0

My rollup config

chrbala/create-react-app 0

Create React apps with no build configuration.

push eventchrbala/zod-full-stack-example

Chris Bala

commit sha f766047c316aee0fb977be3f84f844572d0fafe6

Add a server-only validation that will hit the UI

view details

push time in 4 days

push eventchrbala/zod-full-stack-example

Chris Bala

commit sha 3d8abe701c458c4dc6871a436a9186dabf0045c0

Add dryruns to mutations

view details

push time in 4 days

push eventchrbala/zod-full-stack-example

Chris Bala

commit sha aeb2d8a33a1fd54327e618270c935bb6df6f8a46

Add a simplified allLivingThings resolver

view details

push time in 4 days

push eventchrbala/zod-full-stack-example

Chris Bala

commit sha 4ca394adf09254f026b803cec3f357166b303c60

Fix a couple things

view details

Chris Bala

commit sha 71a58adb387de9deedc14213a38ed7292ae2fc68

Automatically strip unused keys

view details

Chris Bala

commit sha a2af995c53644475248e8e004f0f81d61c7f591e

add backend for update and delete

view details

Chris Bala

commit sha 56647864646f3e21544c31e7eed93708f1723a26

Partial progress with other resolvers

view details

Chris Bala

commit sha 14798aa03295e69d1db755eb8d5ef74eadd6b092

Fixes

view details

Chris Bala

commit sha 9e139a3497c9fdd410edbd31c18c54ebada8925b

Update diet

view details

Chris Bala

commit sha fea35720c99464de72b8c40f3586d86539f21249

Allow explicit nulls

view details

push time in 6 days

push eventchrbala/zod-full-stack-example

Chris Bala

commit sha ef63e9117414d0e9af1a0b56c1e2b6dbab8c3de5

initial server work done

view details

push time in 9 days

push eventchrbala/zod-full-stack-example

Chris Bala

commit sha bf86b2dc389028cbec59e05a481e486cb3f92de3

remove log (+11 squashed commits) Squashed commits: [4a95289] agh [9bfe782] hmm [b6057ba] Cap diet sizes [ec5169a] Looking pretty good [3ef614c] big progress [b94cb47] progress [9c64f54] Remove garbage [d96b89a] Simplify things [483e04b] progress [1f9e423] copy draw [1370592] Copy io-ts-reporters

view details

push time in 9 days

create barnchchrbala/zod-full-stack-example

branch : io-ts

created branch time in 11 days

startedMichalLytek/type-graphql

started time in a month

issue commentvriad/zod

Docs on integrating with other OSS

I have a bit of progress on an example. It uses the in-progress zod transformations API and not everything works – don't rely on anything in the repo for now. I'm adding this repo to share progress here, but it shouldn't be trusted to be stable at this point.

chrbala

comment created time in a month

create barnchchrbala/zod-full-stack-example

branch : work-in-progress

created branch time in a month

create barnchchrbala/zod-full-stack-example

branch : master

created branch time in a month

created repositorychrbala/zod-full-stack-example

created time in a month

created repositorychrbala/zod-full-stack-example

created time in a month

issue commentvriad/zod

Detailed errors for union schema

It seems like this could be improved with a union resolver function as I proposed in https://github.com/vriad/zod/issues/100#issuecomment-667584554. As long as the input matches well enough (e.g. with a discriminator field) to be recognized as an element of the union in userland, the errors could be simplified to a single case instead of needing to resolve unionErrors within an invalid_union error type.

enum Number {
  FLOAT,
  INT,
}
const floatObj = z.object({
  kind: z.literal(Number.FLOAT),
  value: z.number(),
});
const intObj = z.object({
  kind: z.literal(Number.INT),
  value: z.number().refine(n => n % 1 === 0, "not_int"),
});

const resolver = (val: unknown) =>
  val && typeof val === 'object'
    ? val.kind === Number.FLOAT
      ? floatObj
      : intObj
    : null;

const union = z.union([floatObj, intObj], resolver);

const input = {
  kind: Number.INT,
  value: 5.5,
};

union.parse(input); // same result as intObj.parse(input);

So here, parsing input with into either the union or intObj schemas should result in:

Error: 1 validation issue(s)

  Issue #0: custom_error at value
  not_int
Invertisment

comment created time in a month

issue commentvriad/zod

Contextual validations

Okay to just leave this one hanging for a while? I think things may get clearer once more work has been done on a full-stack example for #86. I've been working on that one for some time - it's been going back and forth between that and your work on #100 which my implementation of #86 is dependent on.

chrbala

comment created time in a month

issue commentvriad/zod

Generic for parsing objects

Yeah, that seems about right for the sync implementation. It does look like params is unused though - not sure if that was intentional.

kevinsimper

comment created time in a month

issue commentvriad/zod

RFC: Transformations (e.g. casting/coercion)

I have done more investigations and have come across the following 3 use cases, divided by separators:


Is there something that could be done to improve deep structural sharing of the output types? It seems like allowing something like this would make certain patterns much easier.

import * as z from 'zod';

const id = z.transformer(z.number(), z.string(), n => String(n));
const node = z.transformer(
  z.object({
    id,
  }),
  z.object({
    ID: id,
  }),
  ({ id }) => ({ ID: id })
);

node.parse({ id: 5 });  // would like { ID: 5 }, but this throws "Expected number, received string"

How would you get both the input and output types for a schema? z.TypeOf is only one type. Maybe introspection into the schema to get the input and output runtime schemas would work well for both getting the schemas themselves and their associated TS types. e.g.

const num = z.number();
const str = z.string();
const field = z.transformer(num, string, n => String(n));
const obj = z.object({
  field,
});

field.getInput(); // num
field.getOutput(); // str

obj.getInput(); // equivalent of z.object({ field: z.number() });
obj.getOutput(); // equivalent of z.object({ field: z.string() });

And in this case, getOutput() would preferably return the output types of all children in the schema, like the first section of this comment mentions. I see that there are input, output, _input, and _output fields on some zod types, but my guess is that they don't work quite like this, and it's not clear what the intention is.


  1. What about two-way transformations, e.g. for serialization and deserialization? This could be pretty common for lossless transformations. Should A -> B -> A require two fully separate schemas, or should there be a mechanism to enable this?
  2. What about arbitrary length transformation chains? A -> B -> C -> D (etc)?

My inclination is that (1) would probably be useful and (2) might get overcomplicated quickly. And what’s to say that a sequence of transformations is what makes sense in (2), rather than a tree or graph shape. (2) seems too complex for zod to incorporate.

vriad

comment created time in a month

issue commentvriad/zod

RFC: Make object schemas nonstrict by default

The primary thing I care about regarding this issue is that there's a path to getting a clean object without erroneous keys. If you use zod to validate before a schemaless database, you might find out you have a bunch of unwanted fields if there aren't good protections in place. Perhaps making this explicit like a ZodSchema.parse(val, { clean: true }) which would allow for this.

Even right now, this is a bit cumbersome because unused fields have to be manually removed. A value cleaner would make this nicer.

vriad

comment created time in a month

issue commentvriad/zod

RFC: Transformations (e.g. casting/coercion)

Makes sense! I have a couple thoughts:

  1. Generally I'd hope that ctx has bound functions, or is an object literal, so people could destructure it without getting class errors. (num, {makeError}) => ...
  2. Would the API be ctx.customError(message: string, params?: { [key: string]: any })?
  3. I'm not sure I know of any kind of use case for this (perhaps transforms for tests), but is there any chance someone would want to actually transform to a ZodError? If that's a real use case, should ctx.makeError return some special private error type that end-users don't have access to through normal means?
vriad

comment created time in a month

issue commentvriad/zod

Generic for parsing objects

Seems to make sense to me at least. Would it be .safeParse() and .safeParseAsync() when async parsing is added then?

kevinsimper

comment created time in 2 months

issue commentvriad/zod

Guidance on creating slight schema variants for varied contexts

Yeah, agreed that at this point there isn't really an obvious action to take here. From investigating this, I've found that it's mostly easiest to make shared types from the leaf nodes up as high as what is fully shared, then do a hard fork where the types diverge. It can get pretty redundant with deeply nested types that diverge near the leaves, but I've found that the redundancy is the clearest way to represent this, rather than abstractions or core feature additions.

chrbala

comment created time in 2 months

issue commentvriad/zod

Demand for VSCode Extension?

Interesting concept! I would personally prefer to see GraphQL schema -> Zod types, but interesting either way! I suppose it's also possible to generate GraphQL -> TS -> Zod in some kind of pipeline as well. In general, I'd expect to auto-generate Zod types which then get manually edited. Certainly it's more complicated than just the proposal here, and I'm not sure exactly what it would look like in terms of UX.

bradennapier

comment created time in 2 months

issue commentvriad/zod

RFC: Transformations (e.g. casting/coercion)

I guess I was actually skipping a step there. That's the value that gets passed in to the schema, and I was forgetting that it wouldn't have yet been parsed to a number. So, more accurately:

const float = z.transformer(z.string(), z.number(), x => parseFloat(x));
const int = z.transformer(z.string(), z.number().int(), x => parseInt(x));

const resolveType = (val: unknown) =>
  typeof val === 'string' && parseFloat(val) % 1 === 0 ? int : float;

const number = z.union([float, int], resolveType);

number.parse('12.5');

val === '12.5' in this case, but resolveType doesn't know anything about the type yet, so the type resolver has to operate under the assumption that val is unknown.

vriad

comment created time in 2 months

issue commentvriad/zod

RFC: Transformations (e.g. casting/coercion)

Returning back to the issue of unions - what if there was an explicit resolver function like resolveType in GraphQL?

Perhaps z.union could be a variadic function that by default picks the first schema that matches left-to-right, but optionally can specify a second resolver argument.

const float = z.transformer(z.string(), z.number(), x => parseFloat(x));
const int = z.transformer(z.string(), z.number().int(), x => parseInt(x));

const leftToRightNum = z.union([float, int]);
const withResolverNum = z.union([float, int], val =>
  val % 1 === 0 ? int : float
);

This is kind of a trivial example, but it would line up well for people who already do type resolutions with GraphQL in some way, and the explicitness is nice.

vriad

comment created time in 2 months

issue commentvriad/zod

Generic for parsing objects

I have written this which has somewhat different goals as the above example, which may be of interest to people:

  • Intentional (validation) errors returned, not thrown. This distinguishes them from unintentional errors which are still thrown.
  • Semi-strongly typed errors (properties known, but not enumerated).
  • Always async
  • Not concerned with creating factories, can curry if desired
  • Decoupled from Zod. Returns errors that are not library specific.
import { Schema, ZodError, ZodErrorCode } from 'zod';

const asError = (e: unknown) =>
  e instanceof Error ? e : typeof e === 'string' ? new Error(e) : new Error();

export type ExecResult<T> =
  | {
      success: true;
      data: T;
    }
  | {
      success: false;
      error: Error;
    };

export const safeExec = <T extends any>(cb: () => T): ExecResult<T> => {
  try {
    return { success: true, data: cb() };
  } catch (e) {
    return { success: false, error: asError(e) };
  }
};

export const safeExecAsync = <T extends any>(
  cb: () => Promise<T>
): Promise<ExecResult<T>> => {
  // check for sync errors
  const res = safeExec(cb);
  if (!res.success) return Promise.resolve(res);

  return Promise.resolve(res.data).then(
    data => ({ success: true, data }),
    error => ({ success: false, error: asError(error) })
  );
};

type ParseError = {
  code: string;
  path: Array<string>;
};
type ParseResult<T> =
  | { success: true; data: T }
  | { success: false; errors: Array<ParseError> };
export const parse = async <T>(
  schema: Schema<T>,
  data: unknown
): Promise<ParseResult<T>> => {
  const res = await safeExecAsync(() => schema.parseAsync(data));
  if (res.success) return res;

  if (res.error instanceof ZodError) {
    return {
      success: false,
      errors: res.error.errors.map(({ code, message, path }) => ({
        path: path.map(String),
        code: code === ZodErrorCode.custom_error ? message : code,
      })),
    };
  }

  throw res.error;
};

Usage:

const parsed = await parse(MySchema, someValue);

if (!parsed.success) {
  return {
    __typename: 'InputError',
    errors: parsed.errors,
  };
}

// parsed.data is known to exist statically now because of the guard above.
kevinsimper

comment created time in 2 months

issue commentvriad/zod

RFC: Make object schemas nonstrict by default

If object schemas become nonstrict by default (which generally makes sense to me), how would parsing work? I see some advantages in being able to give an object with extra keys, then parsing removes them.

const Obj = z.object({
  key: z.string(),
});

Obj.parse({key: 'value', key2: 'value2'})  // could return { key: 'value' }
vriad

comment created time in 2 months

startedautomerge/automerge

started time in 2 months

issue commentvriad/zod

RFC: Transformations (e.g. casting/coercion)

Perhaps Date was a poor example because it has a builtin type in Zod. I'm more interested in the broader case where parsing, error code generation, and transforming can all happen in the same closure (or provide sufficient context sharing, etc) with arbitrary types for convenience and performance.

e.g. with simple-duration. (Again, let's say simple-duration has an error code which we can use in the refinement.)

import * as sd from 'simple-duration';
import * as z from 'zod';

const DurationString = z.string().refine(s => {
  try {
    sd.parse(s);
    return true;
  } catch (e) {
    return false;
  }
}, 'need e.code here');

z.transformer(DurationString, z.number(), s => sd.parse(s));

If the type refine fails, sd.parse is only run once here, but if it passes, it gets run in both the refine and the transformer. You can see it on this RunKit where the single t.parse('5m') results in the sd.parse getting parsed twice, evidenced by the two console.log statements.

Dates and simple-duration are of course fairly trivial data types, but conceivably there could be sufficiently complex data types (e.g. zipped data, etc) where doing multiple parses would eat into performance.

Perhaps this point could be moved to a different issue, however. I could see z.transformer working as it currently does, and perhaps something like z.parser which could combine parsing, error codes, and transformations like this, where Error could have an error code and additional properties that get rolled up into the list of ZodError at the top level parse function:

.parser<A, B>(parserFn: (data:A)=> B | Error)
vriad

comment created time in 2 months

issue openedvriad/zod

Create a zod type from a TS enum

Right now if you want to create a type from a TS enum, you have do something like the following:

enum PlantLifecycle {
  Evergreen = 'EVERGREEN',
  Deciduous = 'DECIDUOUS',
  SemiDeciduous = 'SEMI_DECIDUOUS'
};

const lifecycle = z.union([
  z.literal(PlantLifecycle.Deciduous),
  z.literal(PlantLifecycle.Evergreen),
  z.literal(PlantLifecycle.SemiDeciduous),
]);

There should be an easier way of doing this, like:

enum PlantLifecycle {
  Evergreen = 'EVERGREEN',
  Deciduous = 'DECIDUOUS',
  SemiDeciduous = 'SEMI_DECIDUOUS'
};

z.enum(PlantLifecycle);

created time in 2 months

issue commentvriad/zod

RFC: Transformations (e.g. casting/coercion)

Another thought I'm having on this one is similar to #88. That is, it would be a pretty reasonable case to see an external tool parsing, validating, and creating error messages. Right now, all these things are run in zod in separate closures, which means that you have to plumb these things in multiple times, and pay the runtime cost multiple times as well.

For the sake of example, let's say Date.parse throws an error that includes a "code" property instead of returning NaN when there is an invalid input. This is similar to what npm libraries sometimes do.

const DateString = z.string().refine(s => {
  try {
    Date.parse(s);
    return true;
  } catch (e) {
    return false;
  }
}, 'need e.code here');

z.transformer(DateString, z.number(), s => Date.parse(s));

I don't want to have to run Date.parse 2-3 times to parse each input. This example is fairly small, but for some types, the runtime cost could start adding up. It would be nice to have a single closure that can encompass (1) validation (2) error code generation, and (3) transformations.

vriad

comment created time in 2 months

issue commentvriad/zod

RFC: Transformations (e.g. casting/coercion)

Are transforms supposed to bubble up the object trees? I'm not getting what I'm expecting.

const ID = z.transformer(z.number(), z.string(), n => String(n));

const node = z.object({
  id: ID
});

const result = node.parse({ id: 5 });  // returns { id: 5 } not { id: '5' }

Interestingly, result is typed as you'd expect here: { id: string }.

vriad

comment created time in 2 months

issue commentvriad/zod

RFC: Transformations (e.g. casting/coercion)

I'll be digging deeper into this as I have time, but my first thought is that it would be nice to have async transformations. That is, you could do something like z.transformer(z.string(), z.string(), s => Promise.resolve(s));. Then the async logic for deep fields would get hidden away with whatever solution that results from #87.

vriad

comment created time in 2 months

issue commentvriad/zod

Similar to Yup’s meta properties?

Seems like there are quite of lot of arbitrary places where UI could be driven from metadata in some way:

  • max length on a field gets set on the input
  • max length on an array disables or hides an "add row" button

More complex metadata could be something like data driven UI allowing for selection of various complex forms based on a union type.

At some level it seems like there could be a question of whether zod:

  • allows arbitrary fields like this suggestion
  • generates user-accessible metadata based on the built-ins like z.string().max(5)
  • some combination of the above
cybervaldez

comment created time in 2 months

issue commentvriad/zod

Contextual validations

It seems to me that patching could get out of hand pretty quickly. Like if you have something like object -> union -> object -> string with custom validations, how would you readily do a deep patch to target the string but not on other variants in the union? It seems like that would get pretty complex both on the implementation side, and might be hard to follow on the application side. This is why I'm wondering if my suggestion in https://github.com/vriad/zod/issues/85#issuecomment-660466100 is the right approach.

What kind of tool do you see on top of zod? Are you envisioning a library that define the structure and create a zod schema based on that, or would it be more application logic? I guess it's not clear if zod should be considered a low-level primitive, or if it's something that applications would more commonly depend on directly. If it is a low-level primitive, guidance on the best ways to create tools on top of it would be helpful.

What I mean by "different contexts" is less about how or where the schema is used, but on a conceptual level. There may be validations that can not or should not be run in some circumstances. A couple examples:

  • Validations that only the server can do because of access to resources.
  • Validations with a CPU performance hit, so the validations need to be debounced, run on field blur, form submission, etc so as to not drop frames
  • Validations that call out to the backend (assuming #87 is done) need to be debounced, run on field blur, form submission, etc so as to preserve server resources/bandwidth/etc

So for a full-stack example, there could be 3+ schemas in operation with lots of shared logic:

  • API validations
  • Validations that happen frequently on user input
  • Validations that happen infrequently on the client
chrbala

comment created time in 2 months

issue commentvriad/zod

Guidance on creating slight schema variants for varied contexts

Thinking about this one a bit more. I'm wondering if it might make sense to generally solve this type of thing on the application side like this:

// commonValidations.ts
import * as z from 'zod';

export const id = z.string().refine(val => val.length == 8, {
  message: "Foreign keys should be 8 characters",
});
// clientValidations.ts
import * as z from 'zod';
import * as commonValidations from './commonValidations';

/*
  Client does not have any extra validations for ID, so it uses common directly
 */
export const user = z.object({
  id: commonValidations.id,
});
// serverValidations.ts
import * as z from 'zod';

// model only exists on the server, not implemented here
import { isValidId } from './applicationModel';
import * as commonValidations from './commonValidations';

export const id = commonValidations.id.refine(isValidId, {
  message: 'ID is invalid',
});

export const user = z.object({
  id,
});

What I like about this design is that everything is very explicit, clear, and avoids tight coupling between client and server. What I don't like is that it could get pretty verbose with lots of redundancy across the client and server as the schema gets more complex. That is, if user had a schema refinement, it would need to be shared across the client and server as well. Any kind of schema patching would be more concise in exchange for tighter coupling between client and server in this type of situation.

chrbala

comment created time in 2 months

issue commentvriad/zod

Asynchronous validations

I agree that the default should be to validate async if that would be okay as a breaking change. It may still make sense to have a synchronous version that requires all parts of the schema to operate synchronously. So maybe parse and parseSync would make sense.

chrbala

comment created time in 2 months

issue commentvriad/zod

Docs on integrating with other OSS

I am not using Formik + zod, but I am trying to establish what these integrations would look like before really investing in the stack. So, "not yet" is the short answer. I'm also not necessarily sold on either component just yet, since I'm trying to evaluate all the components together before making decisions.

I made a library some time ago with the main idea that it would be a single place to define the structure of objects. It was designed to be a pluggable system that allowed for modular integrations with various functions. It's old and crusty now, there are flaws in the design, it wouldn't play nicely with TS, and I don't actually use it for anything, but there may be some ideas worth taking a look at.

Library: https://github.com/chrbala/single-schema GraphQL integration module: https://github.com/chrbala/single-schema/blob/master/src/flatteners/graphql/README.md Other modules, like PropTypes: https://github.com/chrbala/single-schema#modules

From a high level, a takeaway I had from building that was that if you create a single core schema with a number of tools coming out of it, you're bound to the lowest-common-denominator of functionality across the systems. e.g. if a module doesn't support unions, you can't have unions in any part of the schema unless you have a way of failing over to something that does something similar enough to unions to be compatible.

chrbala

comment created time in 2 months

issue commentvriad/zod

Value transformation / object mapping

Seems like this could be really useful when it comes to more complex objects. For a specific use-case I'm thinking of, take GraphQL unions that are generated from inputs. Union types only exist on outputs, so it's required to use some workarounds to have varied inputs.

The following is a common solution for this problem illustrated in TypeScript:

type Animal = {
  lifespan: number;
};

type Mineral = {
  elements: Array<string>;
};

type Vegetable = {
  calories: number;
};

type Answer = Animal | Mineral | Vegetable;

type TwentyQuestions = {
  answer: Answer;
};

// This is equivalent to TwentyQuestions shown above, but structured differently.
// It is also possible to represent invalid values with this structure.
type TwentyQuestionsInput = {
  animal?: Animal;
  mineral?: Mineral;
  vegetable?: Vegetable;
};

The pattern here is to make each variant of the union a unique field on the input type which is then validated in the API logic to ensure that only one is provided and persisted.

Concretely for zod, this would look like a union on one side and an object on the other side with some additional logic (with zod refine) to ensure that only one key is provided.

There are some patterns in GraphQL and in other systems that require transformations like this. Creating complex structures for both validation/parsing and mapping would be fairly redundant, so it would be ideal to include this type of logic in a library like zod.

A couple related thoughts:

  • What actually gets validated? If the parser maps A -> B, does the validation logic operate on A or B?
  • What do you do if you want to have mappings from both A -> B as well as A -> C? There’s likely a lot of common logic and structure there. What is the best way of sharing it?
  • Should there be library logic for composing transformations? That is, if you have logic for A -> B and B -> C, should there be something like a z.compose(a, b) that creates an A -> C mapping from the separate mappers? How would that work?
  • What about reversible operations? How would you easily create both A -> B and B -> A?
krzkaczor

comment created time in 2 months

issue openedvriad/zod

Option to create params from input in custom validation

zod custom validation allows params to be passed in to the error object. It would be nice if these could be created based on the initial data.

That is, instead of this type signature: .refine(validator: (data:T)=>any, params?: RefineParams)

zod could use: .refine(validator: (data:T)=>any, params?: RefineParams | (data: T) => RefineParams

created time in 2 months

issue openedvriad/zod

Asynchronous validations

There's support for validating promise types, but I don't see a way to create async validators.

What I'm thinking of is something like this:

const foreignKey = z.string().refine(idExistsAsync, {
  message: "ID not found",
});
foreignKey.parseAsync("1234")

Where idExistsAsync is some function that asynchronously checks some backend to make sure that the value is valid. It returns a promise. Of course this would mean that the entire schema becomes async, so there are some other concerns here when async values are used as fields on objects.

created time in 2 months

issue openedvriad/zod

Docs on integrating with other OSS

It would be nice to have docs on how to integrate zod with other OSS. This would help people:

  • identify whether zod will work well with their tech stack
  • figure out how to use zod with their tech stack
  • help zod validate its feature set. If the docs are hard to write, zod is probably missing something.

GraphQL

It seems like zod could be well suited for GraphQL validations. It would be handy to have a reference for the parallels of GraphQL types and their corresponding zod types, as well as any other suggestions for integrating zod into the GraphQL environment.

As a single small example:

union Fish = Salmon | Tuna | Trout
type Catch {
  fish: Fish!
}
const FishEnum = z.union([z.literal('Salmon'), z.literal('Tuna'), z.literal('Trout')]);
const Catch = z.object({
  fish: FishEnum,
});

Of course it's a bit less obvious when it comes to nullable types because GraphQL is nullable by default, and zod is required by default.

React PropTypes

Seems like an integration that would make sense for libraries. Similar docs like those for GraphQL could make sense.

formik

Formik has native support for Yup schemas. Docs on integrating zod with formik would be nice!

created time in 2 months

issue openedvriad/zod

Guidance on creating slight schema variants for varied contexts

I am interested in using zod for frontend, API, and backend validation.

For example, a browser might validate user inputs live as they are changed on the page. The user hits "submit" which sends the data to the API. The API runs its own validations on this data that may be somewhat different. The API changes the data slightly to conform with the backend needs. The backend has a similar schema, but there are some small differences.

Differences might be things like:

  • Adding a field deep in the schema
  • Swapping a foreign key ID for an object pulled from some other location
  • API allows partial inputs for updates, but the backend sometimes requires complete inputs. For example, take a blog post draft that can be missing a title field, but the backend requires a title to be persisted before publishing.

I'm not necessarily looking for changes to the library, but rather recommended patterns through documentation, blog posts, etc. I see that zod supports merging, masking, and extending, but am looking for higher level guidance on patterns for this type of use case. I can see schema sharing as something that could potentially make a codebase much more complex without due care. The guidance here might be to create separate schemas because the costs of coupling schemas across contexts is too high!

There may also be features that could be added to zod that make this use case easier.

created time in 2 months

issue openedvriad/zod

Contextual validations

I'm looking for a way to validate in different ways in different contexts. For example, take a complex object on the client (browser) with foreign-key fields (IDs) that map to other objects on the backend. The client has a mechanism for retrieving these IDs and naturally trusts them. The client also may not have a reasonable mechanism for validating them. On the server, we don't trust these to be valid IDs by default. We need to ensure that they map to real objects.

How would we share most of the schema, but have slightly differing validations in different contexts?

From a high level, I'm thinking of a context object that can be passed down and used in the validators similar to how a lot of GraphQL server implementations work.

created time in 2 months

PR opened vriad/zod

Remove erroneous character from readme

It looks like this j wasn't intended to be in the doc.

+0 -2

0 comment

1 changed file

pr created time in 2 months

push eventchrbala/zod

chrbala

commit sha 20aaa0406475ad76ecbe765187bc8609c7ad2c7b

Remove erroneous character from readme

view details

push time in 2 months

fork chrbala/zod

TypeScript-first schema validation with static type inference

fork in 2 months

startedvriad/zod

started time in 3 months

more