-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* use pacakge for env * fix script versioning * whoops * add a callout in docs * fixies
- Loading branch information
1 parent
6d3339f
commit 00018ff
Showing
11 changed files
with
1,080 additions
and
1,328 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"create-t3-app": patch | ||
--- | ||
|
||
move env validation logic out to a separate package |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,82 +1,30 @@ | ||
import { z } from "zod"; | ||
|
||
/** | ||
* Specify your server-side environment variables schema here. This way you can ensure the app isn't | ||
* built with invalid env vars. | ||
*/ | ||
const server = z.object({ | ||
NODE_ENV: z.enum(["development", "test", "production"]), | ||
import { createEnv } from "@t3-oss/env-nextjs"; | ||
|
||
export const env = createEnv({ | ||
/** | ||
* Specify your server-side environment variables schema here. This way you can ensure the app | ||
* isn't built with invalid env vars. | ||
*/ | ||
server: { | ||
NODE_ENV: z.enum(["development", "test", "production"]), | ||
}, | ||
|
||
/** | ||
* 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_`. | ||
*/ | ||
client: { | ||
// NEXT_PUBLIC_CLIENTVAR: z.string().min(1), | ||
}, | ||
|
||
/** | ||
* You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g. | ||
* middlewares) or client-side so we need to destruct manually. | ||
*/ | ||
runtimeEnv: { | ||
NODE_ENV: process.env.NODE_ENV, | ||
// NEXT_PUBLIC_CLIENTVAR: process.env.NEXT_PUBLIC_CLIENTVAR, | ||
}, | ||
}); | ||
|
||
/** | ||
* 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_`. | ||
*/ | ||
const client = z.object( | ||
/** @satisfies {Record<`NEXT_PUBLIC_${string}`, import('zod').ZodType>} */ ( | ||
{ | ||
// NEXT_PUBLIC_CLIENTVAR: z.string().min(1), | ||
} | ||
), | ||
); | ||
|
||
/** | ||
* You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g. | ||
* middlewares) or client-side so we need to destruct manually. | ||
* | ||
* @type {Record<keyof z.infer<typeof server> | keyof z.infer<typeof client>, string | undefined>} | ||
*/ | ||
const processEnv = { | ||
NODE_ENV: process.env.NODE_ENV, | ||
// NEXT_PUBLIC_CLIENTVAR: process.env.NEXT_PUBLIC_CLIENTVAR, | ||
}; | ||
|
||
// Don't touch the part below | ||
// -------------------------- | ||
|
||
const merged = server.merge(client); | ||
|
||
/** @typedef {z.input<typeof merged>} MergedInput */ | ||
/** @typedef {z.infer<typeof merged>} MergedOutput */ | ||
/** @typedef {z.SafeParseReturnType<MergedInput, MergedOutput>} MergedSafeParseReturn */ | ||
|
||
let env = /** @type {MergedOutput} */ (process.env); | ||
|
||
const skip = | ||
!!process.env.SKIP_ENV_VALIDATION && | ||
process.env.SKIP_ENV_VALIDATION !== "false" && | ||
process.env.SKIP_ENV_VALIDATION !== "0"; | ||
if (!skip) { | ||
const isServer = typeof window === "undefined"; | ||
|
||
const parsed = /** @type {MergedSafeParseReturn} */ ( | ||
isServer | ||
? merged.safeParse(processEnv) // on server we can validate all env vars | ||
: client.safeParse(processEnv) // on client we can only validate the ones that are exposed | ||
); | ||
|
||
if (parsed.success === false) { | ||
console.error( | ||
"❌ Invalid environment variables:", | ||
parsed.error.flatten().fieldErrors, | ||
); | ||
throw new Error("Invalid environment variables"); | ||
} | ||
|
||
env = new Proxy(parsed.data, { | ||
get(target, prop) { | ||
if (typeof prop !== "string") return undefined; | ||
// Throw a descriptive error if a server-side env var is accessed on the client | ||
// Otherwise it would just be returning `undefined` and be annoying to debug | ||
if (!isServer && !prop.startsWith("NEXT_PUBLIC_")) | ||
throw new Error( | ||
process.env.NODE_ENV === "production" | ||
? "❌ Attempted to access a server-side environment variable on the client" | ||
: `❌ Attempted to access server-side environment variable '${prop}' on the client`, | ||
); | ||
return target[/** @type {keyof typeof target} */ (prop)]; | ||
}, | ||
}); | ||
} | ||
|
||
export { env }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,102 +1,49 @@ | ||
import { z } from "zod"; | ||
|
||
/** | ||
* Specify your server-side environment variables schema here. This way you can ensure the app isn't | ||
* built with invalid env vars. | ||
*/ | ||
const server = z.object({ | ||
DATABASE_URL: z.string().url(), | ||
NODE_ENV: z.enum(["development", "test", "production"]), | ||
NEXTAUTH_SECRET: | ||
process.env.NODE_ENV === "production" | ||
? z.string().min(1) | ||
: z.string().min(1).optional(), | ||
NEXTAUTH_URL: z.preprocess( | ||
// This makes Vercel deployments not fail if you don't set NEXTAUTH_URL | ||
// Since NextAuth.js automatically uses the VERCEL_URL if present. | ||
(str) => process.env.VERCEL_URL ?? str, | ||
// VERCEL_URL doesn't include `https` so it cant be validated as a URL | ||
process.env.VERCEL ? z.string().min(1) : z.string().url(), | ||
), | ||
// Add `.min(1) on ID and SECRET if you want to make sure they're not empty | ||
DISCORD_CLIENT_ID: z.string(), | ||
DISCORD_CLIENT_SECRET: z.string(), | ||
import { createEnv } from "@t3-oss/env-nextjs"; | ||
|
||
export const env = createEnv({ | ||
/** | ||
* Specify your server-side environment variables schema here. This way you can ensure the app | ||
* isn't built with invalid env vars. | ||
*/ | ||
server: { | ||
DATABASE_URL: z.string().url(), | ||
NODE_ENV: z.enum(["development", "test", "production"]), | ||
NEXTAUTH_SECRET: | ||
process.env.NODE_ENV === "production" | ||
? z.string().min(1) | ||
: z.string().min(1).optional(), | ||
NEXTAUTH_URL: z.preprocess( | ||
// This makes Vercel deployments not fail if you don't set NEXTAUTH_URL | ||
// Since NextAuth.js automatically uses the VERCEL_URL if present. | ||
(str) => process.env.VERCEL_URL ?? str, | ||
// VERCEL_URL doesn't include `https` so it cant be validated as a URL | ||
process.env.VERCEL ? z.string().min(1) : z.string().url(), | ||
), | ||
// Add `.min(1) on ID and SECRET if you want to make sure they're not empty | ||
DISCORD_CLIENT_ID: z.string(), | ||
DISCORD_CLIENT_SECRET: z.string(), | ||
}, | ||
|
||
/** | ||
* 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_`. | ||
*/ | ||
client: { | ||
// NEXT_PUBLIC_CLIENTVAR: z.string().min(1), | ||
}, | ||
|
||
/** | ||
* You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g. | ||
* middlewares) or client-side so we need to destruct manually. | ||
*/ | ||
runtimeEnv: { | ||
DATABASE_URL: process.env.DATABASE_URL, | ||
NODE_ENV: process.env.NODE_ENV, | ||
NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET, | ||
NEXTAUTH_URL: process.env.NEXTAUTH_URL, | ||
DISCORD_CLIENT_ID: process.env.DISCORD_CLIENT_ID, | ||
DISCORD_CLIENT_SECRET: process.env.DISCORD_CLIENT_SECRET, | ||
}, | ||
}); | ||
|
||
/** | ||
* 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_`. | ||
*/ | ||
const client = z.object( | ||
/** @satisfies {Record<`NEXT_PUBLIC_${string}`, import('zod').ZodType>} */ ( | ||
{ | ||
// NEXT_PUBLIC_CLIENTVAR: z.string().min(1), | ||
} | ||
), | ||
); | ||
|
||
/** | ||
* You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g. | ||
* middlewares) or client-side so we need to destruct manually. | ||
* | ||
* @type {Record<keyof z.infer<typeof server> | keyof z.infer<typeof client>, string | undefined>} | ||
*/ | ||
const processEnv = { | ||
DATABASE_URL: process.env.DATABASE_URL, | ||
NODE_ENV: process.env.NODE_ENV, | ||
NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET, | ||
NEXTAUTH_URL: process.env.NEXTAUTH_URL, | ||
DISCORD_CLIENT_ID: process.env.DISCORD_CLIENT_ID, | ||
DISCORD_CLIENT_SECRET: process.env.DISCORD_CLIENT_SECRET, | ||
// NEXT_PUBLIC_CLIENTVAR: process.env.NEXT_PUBLIC_CLIENTVAR, | ||
}; | ||
|
||
// Don't touch the part below | ||
// -------------------------- | ||
|
||
const merged = server.merge(client); | ||
|
||
/** @typedef {z.input<typeof merged>} MergedInput */ | ||
/** @typedef {z.infer<typeof merged>} MergedOutput */ | ||
/** @typedef {z.SafeParseReturnType<MergedInput, MergedOutput>} MergedSafeParseReturn */ | ||
|
||
let env = /** @type {MergedOutput} */ (process.env); | ||
|
||
const skip = | ||
!!process.env.SKIP_ENV_VALIDATION && | ||
process.env.SKIP_ENV_VALIDATION !== "false" && | ||
process.env.SKIP_ENV_VALIDATION !== "0"; | ||
if (!skip) { | ||
const isServer = typeof window === "undefined"; | ||
|
||
const parsed = /** @type {MergedSafeParseReturn} */ ( | ||
isServer | ||
? merged.safeParse(processEnv) // on server we can validate all env vars | ||
: client.safeParse(processEnv) // on client we can only validate the ones that are exposed | ||
); | ||
|
||
if (parsed.success === false) { | ||
console.error( | ||
"❌ Invalid environment variables:", | ||
parsed.error.flatten().fieldErrors, | ||
); | ||
throw new Error("Invalid environment variables"); | ||
} | ||
|
||
env = new Proxy(parsed.data, { | ||
get(target, prop) { | ||
if (typeof prop !== "string") return undefined; | ||
// Throw a descriptive error if a server-side env var is accessed on the client | ||
// Otherwise it would just be returning `undefined` and be annoying to debug | ||
if (!isServer && !prop.startsWith("NEXT_PUBLIC_")) | ||
throw new Error( | ||
process.env.NODE_ENV === "production" | ||
? "❌ Attempted to access a server-side environment variable on the client" | ||
: `❌ Attempted to access server-side environment variable '${prop}' on the client`, | ||
); | ||
return target[/** @type {keyof typeof target} */ (prop)]; | ||
}, | ||
}); | ||
} | ||
|
||
export { env }; |
Oops, something went wrong.
00018ff
There was a problem hiding this comment.
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:
create-t3-app – ./
create-t3-app-nu.vercel.app
create-t3-app-t3-oss.vercel.app
create-t3-app-git-next-t3-oss.vercel.app
beta.create.t3.gg
ct3.app
create.t3.gg
www.ct3.app