From 17854b4ad4dd540e33e54a9cb764230fe6d4217b Mon Sep 17 00:00:00 2001 From: Atridad Lahiji Date: Thu, 31 Aug 2023 12:46:46 -0600 Subject: [PATCH] Switched to t3-env to manage environment vars --- package.json | 1 + pnpm-lock.yaml | 31 +++++++++++-- src/env.mjs | 124 +++++++++++-------------------------------------- 3 files changed, 54 insertions(+), 102 deletions(-) diff --git a/package.json b/package.json index 2c36a1b..719e808 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@neondatabase/serverless": "^0.6.0", "@paralleldrive/cuid2": "^2.2.2", "@react-email/components": "^0.0.7", + "@t3-oss/env-nextjs": "^0.6.1", "@tanstack/react-query": "^4.33.0", "@trpc/client": "10.38.1", "@trpc/next": "10.38.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bd5587b..4305d72 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ dependencies: '@react-email/components': specifier: ^0.0.7 version: 0.0.7 + '@t3-oss/env-nextjs': + specifier: ^0.6.1 + version: 0.6.1(typescript@5.2.2)(zod@3.22.2) '@tanstack/react-query': specifier: ^4.33.0 version: 4.33.0(react-dom@18.2.0)(react@18.2.0) @@ -1293,6 +1296,27 @@ packages: defer-to-connect: 2.0.1 dev: false + /@t3-oss/env-core@0.6.1(typescript@5.2.2)(zod@3.22.2): + resolution: {integrity: sha512-KQD7qEDJtkWIWWmTVjNvk0wnHpkvAQ6CRbUxbWMFNG/fiosBQDQvtRpBNu6USxBscJCoC4z6y7P9MN52/mLOzw==} + peerDependencies: + typescript: '>=4.7.2' + zod: ^3.0.0 + dependencies: + typescript: 5.2.2 + zod: 3.22.2 + dev: false + + /@t3-oss/env-nextjs@0.6.1(typescript@5.2.2)(zod@3.22.2): + resolution: {integrity: sha512-z1dIC++Vxj9kmzX5nSPfcrCSkszy3dTEPC4Ssx7Ap5AqR3c2Qa7S0xf8axn6coy7D/vCXDAAnHYnCMDhtcY3SQ==} + peerDependencies: + typescript: '>=4.7.2' + zod: ^3.0.0 + dependencies: + '@t3-oss/env-core': 0.6.1(typescript@5.2.2)(zod@3.22.2) + typescript: 5.2.2 + zod: 3.22.2 + dev: false + /@tanstack/query-core@4.33.0: resolution: {integrity: sha512-qYu73ptvnzRh6se2nyBIDHGBQvPY1XXl3yR769B7B6mIDD7s+EZhdlWHQ67JI6UOTFRaI7wupnTnwJ3gE0Mr/g==} dev: false @@ -1919,7 +1943,7 @@ packages: dependencies: browserslist: 4.21.10 caniuse-lite: 1.0.30001525 - fraction.js: 4.3.2 + fraction.js: 4.3.4 normalize-range: 0.1.2 picocolors: 1.0.0 postcss: 8.4.29 @@ -3328,8 +3352,8 @@ packages: mime-types: 2.1.35 dev: false - /fraction.js@4.3.2: - resolution: {integrity: sha512-9VLF466MqX1OUP7/d9r7/Vsvu6Hpp+taXBLmiR5x6mEYfT0BDkGVBt5TyA1aDu1WzIE1sF8F66evOFaz7iAEGQ==} + /fraction.js@4.3.4: + resolution: {integrity: sha512-pwiTgt0Q7t+GHZA4yaLjObx4vXmmdcS0iSJ19o8d/goUGgItX9UZWKWNnLHehxviD8wU2IWRsnR8cD5+yOJP2Q==} dev: false /fs-constants@1.0.0: @@ -5863,7 +5887,6 @@ packages: resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} engines: {node: '>=14.17'} hasBin: true - dev: true /unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} diff --git a/src/env.mjs b/src/env.mjs index d2ed54f..71087d9 100644 --- a/src/env.mjs +++ b/src/env.mjs @@ -1,101 +1,29 @@ +import { createEnv } from "@t3-oss/env-nextjs"; 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(), - UPSTASH_REDIS_REST_URL: z.string().url(), - UPSTASH_REDIS_REST_TOKEN: z.string(), - UPSTASH_REDIS_EXPIRY_SECONDS: z.string(), - UPSTASH_RATELIMIT_REQUESTS: z.string(), - UPSTASH_RATELIMIT_SECONDS: z.string(), - NODE_ENV: z.enum(["development", "test", "production"]), - ABLY_PRIVATE_KEY: z.string(), - APP_ENV: z.string(), - RESEND_API_KEY: z.string(), - UNKEY_ROOT_KEY: z.string(), - CLERK_SECRET_KEY: z.string(), +export const env = createEnv({ + server: { + DATABASE_URL: z.string().url(), + UPSTASH_REDIS_REST_URL: z.string().url(), + UPSTASH_REDIS_REST_TOKEN: z.string(), + UPSTASH_REDIS_EXPIRY_SECONDS: z.string(), + UPSTASH_RATELIMIT_REQUESTS: z.string(), + UPSTASH_RATELIMIT_SECONDS: z.string(), + NODE_ENV: z.enum(["development", "test", "production"]), + ABLY_PRIVATE_KEY: z.string(), + APP_ENV: z.string(), + UNKEY_ROOT_KEY: z.string(), + CLERK_SECRET_KEY: z.string(), + }, + client: { + NEXT_PUBLIC_ABLY_PUBLIC_KEY: z.string(), + NEXT_PUBLIC_APP_ENV: z.string(), + NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: z.string(), + }, + experimental__runtimeEnv: { + NEXT_PUBLIC_ABLY_PUBLIC_KEY: process.env.NEXT_PUBLIC_ABLY_PUBLIC_KEY, + NEXT_PUBLIC_APP_ENV: process.env.NEXT_PUBLIC_APP_ENV, + NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: + process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY, + }, }); - -/** - * 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({ - NEXT_PUBLIC_ABLY_PUBLIC_KEY: z.string(), - NEXT_PUBLIC_APP_ENV: z.string(), - NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: z.string(), -}); - -/** - * 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, string | undefined>} - */ -const processEnv = { - DATABASE_URL: process.env.DATABASE_URL, - UPSTASH_REDIS_REST_URL: process.env.UPSTASH_REDIS_REST_URL, - UPSTASH_REDIS_REST_TOKEN: process.env.UPSTASH_REDIS_REST_TOKEN, - UPSTASH_REDIS_EXPIRY_SECONDS: process.env.UPSTASH_REDIS_EXPIRY_SECONDS, - UPSTASH_RATELIMIT_REQUESTS: process.env.UPSTASH_RATELIMIT_REQUESTS, - UPSTASH_RATELIMIT_SECONDS: process.env.UPSTASH_RATELIMIT_SECONDS, - NODE_ENV: process.env.NODE_ENV, - ABLY_PRIVATE_KEY: process.env.ABLY_PRIVATE_KEY, - NEXT_PUBLIC_ABLY_PUBLIC_KEY: process.env.NEXT_PUBLIC_ABLY_PUBLIC_KEY, - APP_ENV: process.env.APP_ENV, - NEXT_PUBLIC_APP_ENV: process.env.NEXT_PUBLIC_APP_ENV, - RESEND_API_KEY: process.env.RESEND_API_KEY, - UNKEY_ROOT_KEY: process.env.UNKEY_ROOT_KEY, - NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: - process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY, - CLERK_SECRET_KEY: process.env.CLERK_SECRET_KEY, -}; - -// Don't touch the part below -// -------------------------- - -const merged = server.merge(client); - -/** @typedef {z.input} MergedInput */ -/** @typedef {z.infer} MergedOutput */ -/** @typedef {z.SafeParseReturnType} MergedSafeParseReturn */ - -let env = /** @type {MergedOutput} */ (process.env); - -if (!!process.env.SKIP_ENV_VALIDATION == false) { - 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 };