Skip to content

Commit 8497451

Browse files
committed
update readme
1 parent 66714f3 commit 8497451

1 file changed

Lines changed: 36 additions & 129 deletions

File tree

README.md

Lines changed: 36 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -55,32 +55,28 @@ Where your server and client schemas live for typesafe environment variables
5555
```js
5656
//@ts-check
5757
import { validateEnvironmentVariables } from "next-validenv";
58+
5859
import { z } from "zod";
5960

6061
/**
61-
* Specify your server-side environment variables schema here.
62-
* This way you can ensure the app isn't built with invalid env vars.
63-
* @template {ReturnType<typeof z.object>} T
64-
* @param {T} serverSchema
65-
* @returns {z.infer<T>}
62+
* Specify your environment variables schema here.
63+
* This way, you can ensure the app isn't built with invalid environment variables.
64+
* By default, environment variables are only available in the Node.js environment, meaning they won't be exposed to the browser. In order to expose a variable to the browser you have to prefix the variable with NEXT_PUBLIC_.
65+
* -> Don't use any Zod .transform() methods in the schema (will cause `implicitly has type 'any'` error) <-
6666
*/
67-
export const serverSchema = z.object({
67+
export const schema = z.object({
6868
NODE_ENV: z.enum(["development", "test", "production"]),
6969
});
7070

7171
/**
72-
* Specify your client-side environment variables schema here.
73-
* This way you can ensure the app isn't built with invalid env vars.
74-
* To expose them to the client, prefix them with `NEXT_PUBLIC_`.
75-
* @template {ReturnType<typeof z.object>} T
76-
* @param {T} serverSchema
77-
* @returns {z.infer<T>}
72+
* Environment variable declarations based on the schema help structure your environment variables programmatically.
73+
* @type {{ [k in keyof z.infer<typeof schema>]: z.infer<typeof schema>[k] | undefined }}
7874
*/
79-
export const clientSchema = z.object({
80-
// NEXT_PUBLIC_TEST: z.string(),
81-
});
75+
export const env = {
76+
NODE_ENV: process.env.NODE_ENV,
77+
};
8278

83-
export const env = validateEnvironmentVariables(serverSchema, clientSchema);
79+
validateEnvironmentVariables(schema, env);
8480
```
8581

8682
### Update ~~`next.config.js`~~ to `next.config.mjs`
@@ -100,7 +96,7 @@ const config = {
10096
export default config;
10197
```
10298

103-
### Create `environment.d.ts`
99+
### Create `env.d.ts`
104100

105101
```js
106102
import { env } from "./env.mjs";
@@ -161,141 +157,52 @@ Where your server and client schemas live for typesafe environment variables
161157
import { z } from "zod";
162158

163159
/**
164-
* Specify your server-side environment variables schema here.
165-
* This way you can ensure the app isn't built with invalid env vars.
166-
* @template {ReturnType<typeof z.object>} T
167-
* @param {T} serverSchema
168-
* @returns {z.infer<T>}
160+
* Specify your environment variables schema here.
161+
* This way, you can ensure the app isn't built with invalid environment variables.
162+
* By default, environment variables are only available in the Node.js environment, meaning they won't be exposed to the browser. In order to expose a variable to the browser you have to prefix the variable with NEXT_PUBLIC_.
163+
* -> Don't use any Zod .transform() methods in the schema (will cause `implicitly has type 'any'` error) <-
169164
*/
170-
export const serverSchema = z.object({
165+
export const schema = z.object({
171166
NODE_ENV: z.enum(["development", "test", "production"]),
172167
});
173168

174169
/**
175-
* Specify your client-side environment variables schema here.
176-
* This way you can ensure the app isn't built with invalid env vars.
177-
* To expose them to the client, prefix them with `NEXT_PUBLIC_`.
178-
* @template {ReturnType<typeof z.object>} T
179-
* @param {T} serverSchema
180-
* @returns {z.infer<T>}
170+
* Environment variable declarations based on the schema help structure your environment variables programmatically.
171+
* @type {{ [k in keyof z.infer<typeof schema>]: z.infer<typeof schema>[k] | undefined }}
181172
*/
182-
export const clientSchema = z.object({
183-
// NEXT_PUBLIC_TEST: z.string(),
184-
});
173+
export const env = {
174+
NODE_ENV: process.env.NODE_ENV,
175+
};
185176

186177
/**
187178
* --------------------------------
188179
* --------------------------------
189-
* DON'T TOUCH ANYTHING BELOW THIS LINE (UNLESS YOU KNOW WHAT YOU'RE DOING)
180+
* Next-ValidEnv Manual Implementation
190181
* --------------------------------
191182
* --------------------------------
192183
*/
193184

194-
// maps through zod schema keys and returns an object with the safeParse values from process.env[key]
195-
export const mapEnvironmentVariablesToObject = (
196-
/** @type {ReturnType<typeof z.object>} */ schema
197-
) => {
198-
/** @type {{ [key: string]: string | undefined; }} */
199-
let env = {};
200-
201-
Object.keys(schema.shape).forEach((key) => (env[key] = process.env[key]));
202-
203-
return schema.safeParse(env);
204-
};
205-
206185
export const formatZodErrors = (
207-
/** @type {import('zod').ZodFormattedError<Map<string,string>,string>} */ errors
186+
/** @type z.ZodFormattedError<Map<string, string>, string> */ errors
208187
) =>
209188
Object.entries(errors)
210189
.map(([name, value]) => {
211190
if (value && "_errors" in value)
212191
return `${name}: ${value._errors.join(", ")}\n`;
192+
193+
return;
213194
})
214195
.filter(Boolean);
215196

216-
export const formatErrors = (/** @type {(string | undefined)[]} */ errors) =>
217-
errors.map((name) => `${name}\n`);
218-
219-
/**
220-
* @function
221-
* @template {ReturnType<typeof z.object>} T
222-
* @template {ReturnType<typeof z.object>} K
223-
* @param {T} serverSchema
224-
* @param {K} clientSchema
225-
* @returns {z.infer<T> & z.infer<K>}
226-
*/
227-
export const validateEnvironmentVariables = (serverSchema, clientSchema) => {
228-
let serverEnv = mapEnvironmentVariablesToObject(serverSchema);
229-
let clientEnv = mapEnvironmentVariablesToObject(clientSchema);
230-
231-
// holds not set environment variable errors for both client and server
232-
/** @type {(string | undefined)[]} */ let invalidEnvErrors = [];
233-
234-
if (!serverEnv.success) {
235-
invalidEnvErrors = [
236-
...invalidEnvErrors,
237-
...formatZodErrors(serverEnv.error.format()),
238-
];
239-
}
240-
241-
if (!clientEnv.success) {
242-
invalidEnvErrors = [
243-
...invalidEnvErrors,
244-
...formatZodErrors(clientEnv.error.format()),
245-
];
246-
}
197+
const safeParsedEnv = schema.safeParse(env);
247198

248-
// if one or more environment variables are not set, throw an error
249-
if (!serverEnv.success || !clientEnv.success) {
250-
console.error("❌ Invalid environment variables:\n", ...invalidEnvErrors);
251-
throw new Error("Invalid environment variables");
252-
}
253-
254-
// holds server environment variables errors that are exposed to the client
255-
/** @type {(string | undefined)[]} */ let exposedServerEnvErrors = [];
256-
257-
for (let key of Object.keys(serverEnv.data)) {
258-
if (key.startsWith("NEXT_PUBLIC_")) {
259-
exposedServerEnvErrors = [...exposedServerEnvErrors, key];
260-
}
261-
}
262-
263-
// if one or more server environment variables are exposed to the client, throw an error
264-
if (exposedServerEnvErrors.length > 0) {
265-
console.error(
266-
"❌ You are exposing the following server-side environment variables to the client:\n",
267-
...formatErrors(exposedServerEnvErrors)
268-
);
269-
throw new Error(
270-
"You are exposing the following server-side environment variables to the client"
271-
);
272-
}
273-
274-
// holds client environment variables errors that are not exposed to the client
275-
/** @type {(string | undefined)[]} */ let notExposedClientEnvErrors = [];
276-
277-
for (let key of Object.keys(clientEnv.data)) {
278-
if (!key.startsWith("NEXT_PUBLIC_")) {
279-
notExposedClientEnvErrors = [...notExposedClientEnvErrors, key];
280-
}
281-
}
282-
283-
// if one or more client environment variables are not exposed to the client, throw an error
284-
if (notExposedClientEnvErrors.length > 0) {
285-
console.error(
286-
"❌ All client-side environment variables must begin with 'NEXT_PUBLIC_', you are not exposing the following:\n",
287-
...formatErrors(notExposedClientEnvErrors)
288-
);
289-
throw new Error(
290-
"All client-side environment variables must begin with 'NEXT_PUBLIC_', you are not exposing the following:"
291-
);
292-
}
293-
294-
// return both client and server environment variables
295-
return { ...serverEnv.data, ...clientEnv.data };
296-
};
297-
298-
export const env = validateEnvironmentVariables(serverSchema, clientSchema);
199+
if (!safeParsedEnv.success) {
200+
console.error(
201+
"❌ Invalid environment variables:\n",
202+
...formatZodErrors(safeParsedEnv.error.format())
203+
);
204+
throw new Error("Invalid environment variables");
205+
}
299206
```
300207

301208
### Update ~~`next.config.js`~~ to `next.config.mjs`
@@ -315,7 +222,7 @@ const config = {
315222
export default config;
316223
```
317224

318-
### Create `environment.d.ts`
225+
### Create `env.d.ts`
319226

320227
```js
321228
import { env } from "./env.mjs";

0 commit comments

Comments
 (0)