t3-env icon indicating copy to clipboard operation
t3-env copied to clipboard

feat: support more validation libraries

Open juliusmarminge opened this issue 1 year ago • 22 comments

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 🤔

juliusmarminge avatar Apr 25 '23 10:04 juliusmarminge

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

  • To tell the core how to extract the typing, mainly for validating client prefix, and type infer in general
  • To tell the core how to parse the env values. As we call .safeParse in zod, but we need to do it in different way for other validator.

chungweileong94 avatar Apr 26 '23 09:04 chungweileong94

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)
  ),
});

juliusmarminge avatar Apr 26 '23 09:04 juliusmarminge

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

taylorallen0913 avatar Apr 27 '23 19:04 taylorallen0913

sure!

juliusmarminge avatar Apr 27 '23 19:04 juliusmarminge

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(),
  }
})

aalakoski avatar Apr 28 '23 09:04 aalakoski

@aalakoski Most probably no.

chungweileong94 avatar Apr 28 '23 10:04 chungweileong94

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.

SeanCassiere avatar Apr 29 '23 11:04 SeanCassiere

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

juliusmarminge avatar May 06 '23 15:05 juliusmarminge

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

Bekacru avatar Jun 02 '23 12:06 Bekacru

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

mattddean avatar Jul 31 '23 02:07 mattddean

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

kaptinlin avatar Aug 05 '23 07:08 kaptinlin

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.

estubmo avatar Aug 28 '23 01:08 estubmo

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

stunaz avatar Aug 28 '23 01:08 stunaz

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.

chungweileong94 avatar Aug 28 '23 02:08 chungweileong94

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.

AlexisWalravens avatar Jan 13 '24 18:01 AlexisWalravens

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

Talent30 avatar Jan 29 '24 00:01 Talent30

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

decs avatar Feb 23 '24 17:02 decs

I think we'd make a createEnvAsync in that case

juliusmarminge avatar Feb 23 '24 18:02 juliusmarminge

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.

mwskwong avatar Apr 09 '24 02:04 mwskwong