JSTAcademy
0 XP
Dashboard
Crash Courses
Environment Variables & Config
10 min
PhD+200 XP
Crash Courses · PhD

Environment Variables & Config

Manage environment variables, Next.js config, and TypeScript path aliases.
10 min read+200 XP on completionCert: Next.js Crash Course
Tap any word in the text below to start reading from there.

Environment Variables & Config

Environment Files — Priority Order

Next.js loads environment files in this order (highest to lowest priority):

.env.local         ← highest — never committed (your local secrets)
.env.development   ← dev builds only (npm run dev)
.env.production    ← production builds only (npm run build)
.env               ← all environments (lowest priority, commit safe for non-secrets)

Create .env.local for local secrets. Add it to .gitignore (Next.js does this automatically).

Server vs Client Variables

# Server-only — NEVER shipped to the browser (no NEXT_PUBLIC_ prefix)
DATABASE_URL=postgresql://...
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
AZURE_SPEECH_KEY=abc123def456
STRIPE_SECRET_KEY=sk_live_...

# Browser-exposed — safe to expose, never put secrets here
NEXT_PUBLIC_SUPABASE_URL=https://xyz.supabase.co
NEXT_PUBLIC_APP_URL=https://academy.jsupremeconglomerate.online
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...

The NEXT_PUBLIC_ vars are inlined at build time into the client bundle. If you accidentally put a secret in a NEXT_PUBLIC_ variable, it is visible to anyone who inspects the browser's network tab or JavaScript source.

TypeScript: Validate Env at Startup

// src/lib/env.ts — validates all required env vars when the server starts
import { z } from 'zod'

const envSchema = z.object({
  DATABASE_URL: z.string().url(),
  SUPABASE_SERVICE_ROLE_KEY: z.string().min(10),
  AZURE_SPEECH_KEY: z.string().min(1),
  NEXT_PUBLIC_SUPABASE_URL: z.string().url(),
  NEXT_PUBLIC_APP_URL: z.string().url(),
})

export const env = envSchema.parse(process.env)
// Throws at startup if any required var is missing or malformed
// Prevents silent runtime failures mid-request

Import env from @/lib/env everywhere instead of process.env directly. You get autocomplete, type safety, and a server crash on startup (not a mid-request 500) if something is missing.

next.config.ts

import type { NextConfig } from 'next'

const config: NextConfig = {
  // Allow images from these external domains
  images: {
    remotePatterns: [
      { protocol: 'https', hostname: '*.supabase.co' },
      { protocol: 'https', hostname: 'images.unsplash.com' },
    ],
  },
  // Add security headers to all API routes
  async headers() {
    return [{
      source: '/api/:path*',
      headers: [
        { key: 'X-Content-Type-Options', value: 'nosniff' },
        { key: 'X-Frame-Options', value: 'DENY' },
        { key: 'X-XSS-Protection', value: '1; mode=block' },
      ],
    }]
  },
  // Permanent redirects
  async redirects() {
    return [{
      source: '/old-path',
      destination: '/new-path',
      permanent: true,  // 308 — tell search engines to update their index
    }]
  },
}

export default config

tsconfig.json Path Aliases

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

With this configured:

  • import { COURSES } from '@/lib/courses' works from any file
  • No more ../../../lib/courses relative path gymnastics
  • Move files freely without updating import paths

Next.js's create-next-app sets this up automatically. If you are adding it manually, also set baseUrl and ensure the path alias matches your folder structure.

0%