t3-oss / t3-env

https://env.t3.gg
MIT License
2.81k stars 88 forks source link

feat: support more validation libraries #6

Open juliusmarminge opened 1 year ago

juliusmarminge commented 1 year ago

Instead of

createEnv({
  server: {
    KEY: z.string(),
  }
});

it should be

createEnv({
  server: z.object({
    KEY: z.string(),
  })
});

and it should not be tied to zod, meaning something like scale-ts could work for example: import * as $ from 'scale-codec';

createEnv({
  server: $.object(
    $.field('text', $.str)
  ),
});

this would require some more considerations for typings as we'd need to pull out the shape of each to get access to the raw keys to compare against the client prefix

Should probably implement #169 first in some half-generic way 🤔

chungweileong94 commented 1 year ago

Probably need an adapter for each validator, for couple of reasons:

juliusmarminge commented 1 year ago

We support a bunch of validators in tRPC:

https://github.com/trpc/trpc/blob/main/packages/server/src/core/parser.ts

https://github.com/trpc/trpc/blob/main/packages/server/src/core/internals/getParseFn.ts

But separate entrypoints could also work i guess:

import { createEnv } from "@t3-oss/env-core/zod";
const env = createEnv({
  // ...
  server: {
    FOO: z.string(),
  }
})

import { createEnv } from "@t3-oss/env-core/scale";
const env = createEnv({
  server: $.object(
    $.field('FOO', $.str)
  ),
});
taylorallen0913 commented 1 year ago

Can I look into integrating Yup as an option alongside Zod?

juliusmarminge commented 1 year ago

sure!

aalakoski commented 1 year ago

Will it also export zod / other validators from within t3-env or would you still need to add zod as a dependency? If not, could it be done?

Something like:

import { createEnv, z } from "@t3-oss/env-core/zod";

const env = createEnv({
  server: {
    FOO: z.string(),
  }
})
chungweileong94 commented 1 year ago

@aalakoski Most probably no.

SeanCassiere commented 1 year ago

Tanner's pattern of validating the router search params, could be something to consider here.

The process in t3-env would look like this.

  1. Make the client and server keys in the init of createEnv accept validation functions, that receive the values of process.env (or import.meta, ... etc.) as its argument.
  2. Then call the user-defined client and server validation functions to the ensure the values are adhere to the schema.

Code-example with zod:

import { createEnv } from "@t3-oss/env-core";
import { z } from "zod";

const env = createEnv({
  server: z.object({ FOO: z.string().min(1) }).parse,
  client: z.object({ BAR: z.string().min(1) }).parse
});

Code example with a custom validator

import { createEnv } from "@t3-oss/env-core";

// custom validator which could be swapped for Yup, Joi, etc....
type ServerVars = { FOO: string }
function serverValidator(values: any): ServerVars {
  // validation magic
  return { FOO: "FOOOOOOOOO" }
};

type ClientVars = { BAR: string }
function clientValidator(values: any): ClientVars {
  // validation magic
  return { BAR: "BOOOOOOOOO" }
};

const env = createEnv({
  server: serverValidator, // (values) => serverValidator(values)
  client: clientValidator // (values) => clientValidator(values)
});

This pattern completely opens it up to the user to bring their own validation libraries and logic into the picture, with t3-env making use of the type-safety returned by the defined validators as well as the libs' client-server separation stuff.

juliusmarminge commented 1 year ago

Will it also export zod / other validators from within t3-env or would you still need to add zod as a dependency? If not, could it be done?

Something like:

import { createEnv, z } from "@t3-oss/env-core/zod";

const env = createEnv({
  server: {
    FOO: z.string(),
  }
})

No - we won't bundle the validator

Bekacru commented 1 year ago

why does it need validation library tho isn't is simple enough task to do plainly? what am I missing?

mattddean commented 1 year ago

Did a very naiive valibot replacement. Obviously it doesn't accomplish the goal here of not shipping a validation library, but it might be useful to someone. https://github.com/mattddean/t3-env-valibot/tree/feature/valibot

kaptinlin commented 1 year ago

any plan support more validation libraies? i want use valibot instead of zod

estubmo commented 1 year ago

Did a very naiive valibot replacement. Obviously it doesn't accomplish the goal here of not shipping a validation library, but it might be useful to someone. https://github.com/mattddean/t3-env-valibot/tree/feature/valibot

This seems to be no longer working, as parse is no longer a function of schemas.

stunaz commented 1 year ago

why not using the patterns as in https://github.com/react-hook-form/resolvers/tree/master ?

chungweileong94 commented 1 year ago

why not using the patterns as in https://github.com/react-hook-form/resolvers/tree/master ?

Yeah, maybe we have to, as Valibot provides a separate parse method instead of from the scheme. Of course I also aware that they do have method from the scheme called _parse, but it's for internal use only, and I heard that it only provides some raw behavior.

AlexisWalravens commented 10 months ago

I just stumbled upon this issue, and maybe this could be useful https://typeschema.com ? next-safe-action just implemented it in it's last version, allowing people to use almost every schema validator.

Talent30 commented 10 months ago

I just stumbled upon this issue, and maybe this could be useful https://typeschema.com ? next-safe-action just implemented it in it's last version, allowing people to use almost every schema validator.

but it only supports async parse

decs commented 9 months ago

would you guys be open to making createEnv an async function? there are some considerations regarding top-level awaits, but maye we can figure something out

juliusmarminge commented 9 months ago

I think we'd make a createEnvAsync in that case

mwskwong commented 7 months ago

why not using the patterns as in https://github.com/react-hook-form/resolvers/tree/master ?

Yeah, maybe we have to, as Valibot provides a separate parse method instead of from the scheme. Of course I also aware that they do have method from the scheme called _parse, but it's for internal use only, and I heard that it only provides some raw behavior.

+1 for this pattern. It also opens for opportunity for contributors to contribute to more resolvers for various schema validation libraries, once the API of the resolver has been standardized.

BTW, is this the next big thing the team is planning? Is there any rough target date for making it happen? I would be happy to contribute (I really want a Valibot resolver) if this is the route the team is going.

chungweileong94 commented 2 months ago

I very interested in getting my hand dirty on this one, but the only problem is that how will the present is going to fit in, as now all the presents are defined with zod schema.

juliusmarminge commented 2 months ago

I very interested in getting my hand dirty on this one, but the only problem is that how will the present is going to fit in, as now all the presents are defined with zod schema.

We can have entrypoint for each @t3-oss/env-core/preset-zod for example

fabian-hiller commented 2 months ago

A few schema library authors are currently working on a common interface. This may allow this library to support any schema library that supports this interface in the future. This will simplify the integration of schema libraries and prevent vendor lock-in, which could lead to more innovation in the long run.

chungweileong94 commented 2 weeks ago

A few schema library authors are currently working on a common interface.

So this is the common interface https://github.com/standard-schema/standard-schema, which is pretty cool. However, zod hasn’t adapted to it just yet, so I think we either wait or implement an adapter with standard-schema.