This boilerplate comes with .env validation out of the box to prevent accidental use of development values like localhost:3000 in production environments.

How it works

The app/lib/env.ts file contains validation logic that uses @t3-oss and zod to validate environment variables at runtime.

To see this in action, try removing any environment variable from your .env.local file. The application will throw an error indicating the missing variable.

import { createEnv } from "@t3-oss/env-nextjs";
import { z } from "zod";

export const env = createEnv({
    server: {
        DEFAULT_SUPABASE_SERVICE_ROLE_KEY: z.string().min(1),
        STRIPE_SECRET_KEY: z
            .string()
            .min(1)
            .refine(
                (str) =>
                    process.env.NODE_ENV === "production" ? !str.startsWith("sk_test_") : true,
                { message: "STRIPE_SECRET_KEY must not start with 'sk_test'" }
            ),
        STRIPE_WEBHOOK_SECRET: z
            .string()
            .min(1)
            .refine((str) => str.startsWith("whsec_"), {
                message: "STRIPE_WEBHOOK_SECRET must start with 'whsec_'",
            }),
        RESEND_API_KEY: z
            .string()
            .min(1)
            .refine((str) => str.startsWith("re_"), {
                message: "RESEND_API_KEY must start with 're_'",
            }),
    },

    client: {
        NEXT_PUBLIC_SITE_URL: z
            .string()
            .url()
            .refine(
                (str) =>
                    process.env.NODE_ENV === "production" ? !str.includes("localhost") : true,
                { message: "NEXT_PUBLIC_SITE_URL must not include 'localhost'" }
            ),
        NEXT_PUBLIC_SUPABASE_URL: z
            .string()
            .url()
            .refine((str) => str.includes("supabase"), {
                message: "NEXT_PUBLIC_SUPABASE_URL must include 'supabase'",
            }),
        NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string().min(1),
        NEXT_PUBLIC_BUILD_VERSION: z.string().min(1),
    },

    experimental__runtimeEnv: {
        NEXT_PUBLIC_SITE_URL: process.env.NEXT_PUBLIC_SITE_URL,
        NEXT_PUBLIC_SUPABASE_URL: process.env.NEXT_PUBLIC_SUPABASE_URL,
        NEXT_PUBLIC_SUPABASE_ANON_KEY: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
        NEXT_PUBLIC_BUILD_VERSION: process.env.NEXT_PUBLIC_BUILD_VERSION,
    },
});

Furthermore, the next.config.mjs file includes validation code that checks these environment variables during the build process. This ensures that all required variables are present and correctly formatted before the application can be built.

import { fileURLToPath } from "node:url";
import createJiti from "jiti";
const jiti = createJiti(fileURLToPath(import.meta.url));

jiti("./src/utils/env.ts");