Skip to content

Commit

Permalink
feat: emptyStringAsUndefined option (#113)
Browse files Browse the repository at this point in the history
Co-authored-by: juliusmarminge <julius0216@outlook.com>
  • Loading branch information
Zamiell and juliusmarminge committed Sep 29, 2023
1 parent 80e27dd commit 4754db7
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 12 deletions.
5 changes: 5 additions & 0 deletions .changeset/fifty-flowers-mix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@t3-oss/env-core": minor
---

feat: add option `emptyStringAsUndefined`
36 changes: 28 additions & 8 deletions docs/src/app/docs/core/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Then, you can create your schema like so:

<Callout>

The file below is named `env.ts`, but you can name it whatever you want. Some frameworks even generate a `env.d.ts` file that will collide with `env.ts` which means you'll have to name it something else.
The file below is named `env.ts`, but you can name it whatever you want. Some frameworks even generate a `env.d.ts` file that will collide with `env.ts`, which means you will have to name it something else.

</Callout>

Expand All @@ -39,26 +39,46 @@ import { createEnv } from "@t3-oss/env-core";
import { z } from "zod";

export const env = createEnv({
/**
* Specify what prefix the client-side variables must have.
* This is enforced both on type-level and at runtime.
*/
clientPrefix: "PUBLIC_",
server: {
DATABASE_URL: z.string().url(),
OPEN_AI_API_KEY: z.string().min(1),
},

/**
* The prefix that client-side variables must have. This is enforced both at
* a type-level and at runtime.
*/
clientPrefix: "PUBLIC_",

client: {
PUBLIC_CLERK_PUBLISHABLE_KEY: z.string().min(1),
},

/**
* What object holds the environment variables at runtime.
* Often `process.env` or `import.meta.env`
* What object holds the environment variables at runtime. This is usually
* `process.env` or `import.meta.env`.
*/
runtimeEnv: process.env,

/**
* By default, this library will feed the environment variables directly to
* the Zod validator.
*
* This means that if you have an empty string for a value that is supposed
* to be a number (e.g. `PORT=` in a ".env" file), Zod will incorrectly flag
* it as a type mismatch violation. Additionally, if you have an empty string
* for a value that is supposed to be a string with a default value (e.g.
* `DOMAIN=` in an ".env" file), the default value will never be applied.
*
* In order to solve these issues, we recommend that all new projects
* explicitly specify this option as true.
*/
emptyStringAsUndefined: true,
});
```

Remove the `clientPrefix` and `client` properties if you only want the environment variables to exist on the server.

<Callout type="warning">

While defining both the client and server schemas in a single file provides the best developer experience,
Expand Down
35 changes: 31 additions & 4 deletions packages/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,12 @@ export interface BaseOptions<TShared extends Record<string, ZodType>> {
export interface LooseOptions<TShared extends Record<string, ZodType>>
extends BaseOptions<TShared> {
runtimeEnvStrict?: never;

/**
* Runtime Environment variables to use for validation - `process.env`, `import.meta.env` or similar.
* Unlike `runtimeEnvStrict`, this doesn't enforce that all environment variables are set.
* What object holds the environment variables at runtime. This is usually
* `process.env` or `import.meta.env`.
*/
// Unlike `runtimeEnvStrict`, this doesn't enforce that all environment variables are set.
runtimeEnv: Record<string, string | boolean | number | undefined>;
}

Expand Down Expand Up @@ -87,13 +89,14 @@ export interface ClientOptions<
TClient extends Record<string, ZodType>
> {
/**
* Client-side environment variables are exposed to the client by default. Set what prefix they have
* The prefix that client-side variables must have. This is enforced both at
* a type-level and at runtime.
*/
clientPrefix: TPrefix;

/**
* Specify your client-side environment variables schema here. This way you can ensure the app isn't
* built with invalid env vars. To expose them to the client, prefix them with `NEXT_PUBLIC_`.
* built with invalid env vars.
*/
client: Partial<{
[TKey in keyof TClient]: TKey extends `${TPrefix}${string}`
Expand Down Expand Up @@ -121,6 +124,21 @@ export interface ServerOptions<
: never} should not prefixed with ${TPrefix}.`>
: TServer[TKey];
}>;

/**
* By default, this library will feed the environment variables directly to
* the Zod validator.
*
* This means that if you have an empty string for a value that is supposed
* to be a number (e.g. `PORT=` in a ".env" file), Zod will incorrectly flag
* it as a type mismatch violation. Additionally, if you have an empty string
* for a value that is supposed to be a string with a default value (e.g.
* `DOMAIN=` in an ".env" file), the default value will never be applied.
*
* In order to solve these issues, we recommend that all new projects
* explicitly specify this option as true.
*/
emptyStringAsUndefined?: boolean;
}

export type ServerClientOptions<
Expand Down Expand Up @@ -158,6 +176,15 @@ export function createEnv<
> {
const runtimeEnv = opts.runtimeEnvStrict ?? opts.runtimeEnv ?? process.env;

const emptyStringAsUndefined = opts.emptyStringAsUndefined ?? false;
if (emptyStringAsUndefined) {
for (const [key, value] of Object.entries(runtimeEnv)) {
if (value === "") {
delete runtimeEnv[key];
}
}
}

const skip = !!opts.skipValidation;
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-explicit-any
if (skip) return runtimeEnv as any;
Expand Down

2 comments on commit 4754db7

@vercel
Copy link

@vercel vercel bot commented on 4754db7 Sep 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

t3-env – ./docs

t3-env.vercel.app
t3-env-git-main-t3-oss.vercel.app
env.t3.gg
t3-env-t3-oss.vercel.app
env.t3.wtf

@vercel
Copy link

@vercel vercel bot commented on 4754db7 Sep 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

t3-env-nextjs – ./examples/nextjs

t3-env-nextjs-t3-oss.vercel.app
t3-env-nextjs.vercel.app
t3-env-nextjs-git-main-t3-oss.vercel.app

Please sign in to comment.