Next.js第十九课 - 进阶 - 环境变量

_

环境变量是我们开发中经常用到的配置方式,通过它们可以管理不同环境下的配置信息,比如数据库连接、API 密钥等。这一章我会详细讲解 Next.js 中环境变量的使用方法和最佳实践。

环境变量概述

Next.js 内置了对环境变量的支持,可以从 .env.* 文件自动加载环境变量到 process.env

环境变量类型

Next.js 中有两种类型的环境变量:

类型前缀服务器端客户端示例
系统变量可访问不可访问DATABASE_URL
公共变量NEXT_PUBLIC_可访问可访问NEXT_PUBLIC_API_URL

这个区分很重要,因为涉及到安全问题。敏感信息(比如数据库密码、API 密钥)不应该暴露到客户端。

环境文件结构

文件优先级

Next.js 会按照以下优先级加载环境变量文件(从高到低):

.env.production.local       # 生产环境(本地,被 git 忽略)
.env.local                 # 所有环境(本地,被 git 忽略)
.env.production            # 生产环境(提交到 git)
.env.development           # 开发环境(提交到 git)
.env.test                 # 测试环境(提交到 git)
.env                       # 所有环境(提交到 git)

记住这个优先级很重要,因为当多个文件中存在同名变量时,优先级高的会覆盖优先级低的。

基本配置

# .env.local - 不提交到 git
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
API_SECRET=your_secret_key
JWT_SECRET=your_jwt_secret

# .env - 可以提交到 git
NEXT_PUBLIC_APP_NAME=My App
NEXT_PUBLIC_APP_URL=http://localhost:3000

我把包含敏感信息的变量放在 .env.local 中,把不敏感的配置放在 .env 中。

环境特定配置

不同环境可以使用不同的配置文件:

# .env.development
DATABASE_URL=postgresql://localhost:5432/mydb_dev
NEXT_PUBLIC_API_URL=http://localhost:4000

# .env.production
DATABASE_URL=postgresql://prod-server:5432/mydb_prod
NEXT_PUBLIC_API_URL=https://api.example.com

# .env.test
DATABASE_URL=postgresql://localhost:5432/mydb_test
NEXT_PUBLIC_API_URL=http://localhost:4000

使用环境变量

服务器端变量

在服务器组件中,你可以访问所有环境变量:

// app/page.tsx
export default async function Page() {
  // 可访问
  const dbUrl = process.env.DATABASE_URL
  const apiSecret = process.env.API_SECRET

  const data = await fetch(dbUrl, {
    headers: {
      Authorization: `Bearer ${apiSecret}`,
    },
  })

  return <div>{/* ... */}</div>
}

客户端变量

在客户端组件中,只能访问带有 NEXT_PUBLIC_ 前缀的变量:

// app/page.tsx
export default function Page() {
  // 可访问
  const apiUrl = process.env.NEXT_PUBLIC_API_URL

  // 不可访问(会是 undefined)
  const dbUrl = process.env.DATABASE_URL

  return <div>API URL: {apiUrl}</div>
}

客户端组件中使用

// components/ApiClient.tsx
'use client'

export default function ApiClient() {
  // 只有 NEXT_PUBLIC_ 变量可用
  const apiUrl = process.env.NEXT_PUBLIC_API_URL

  async function fetchData() {
    const response = await fetch(apiUrl)
    const data = await response.json()
    return data
  }

  return <div>{/* ... */}</div>
}

安全最佳实践

1. 永远不要暴露敏感变量

这是一个严重的安全错误,千万要避免:

// 不好:在客户端使用敏感变量
'use client'

export default function Component() {
  const apiKey = process.env.API_KEY // 不工作且不安全
  return <div>{apiKey}</div>
}

// 好:创建 API 路由
// app/api/data/route.ts
export async function GET() {
  const apiKey = process.env.API_KEY // 安全
  const response = await fetch(`https://api.com/data?key=${apiKey}`)
  const data = await response.json()
  return NextResponse.json(data)
}

// 客户端调用
'use client'
export default function Component() {
  useEffect(() => {
    fetch('/api/data') // 安全
  }, [])
}

2. 验证必需的变量

为了避免运行时错误,我建议在应用启动时验证必需的环境变量:

// lib/env.ts
function getEnvVar(key: string): string {
  const value = process.env[key]
  if (!value) {
    throw new Error(`Missing environment variable: ${key}`)
  }
  return value
}

function getPublicEnvVar(key: string): string {
  const value = process.env[key]
  if (!value) {
    throw new Error(`Missing public environment variable: ${key}`)
  }
  return value
}

// 服务器端变量
export const env = {
  databaseUrl: getEnvVar('DATABASE_URL'),
  apiSecret: getEnvVar('API_SECRET'),
  jwtSecret: getEnvVar('JWT_SECRET'),
}

// 客户端变量
export const publicEnv = {
  apiUrl: getPublicEnvVar('NEXT_PUBLIC_API_URL'),
  appName: getPublicEnvVar('NEXT_PUBLIC_APP_NAME'),
}

3. 使用类型安全

给环境变量添加类型定义,可以在开发时获得更好的提示:

// types/env.d.ts
interface ServerEnv {
  DATABASE_URL: string
  API_SECRET: string
  JWT_SECRET: string
}

interface PublicEnv {
  NEXT_PUBLIC_API_URL: string
  NEXT_PUBLIC_APP_NAME: string
}

declare global {
  namespace NodeJS {
    interface ProcessEnv extends ServerEnv, PublicEnv {}
  }
}

export {}

环境变量配置

next.config.js 中使用

你可以在 next.config.js 中注入环境变量:

// next.config.js
module.exports = {
  env: {
    // 构建时注入
    BUILD_TIME: new Date().toISOString(),
    GIT_COMMIT: process.env.VERCEL_GIT_COMMIT_SHA || 'development',
  },
  // 公共运行时配置
  publicRuntimeConfig: {
    // 只在客户端可用
    staticFolder: '/static',
  },
  // 服务器运行时配置
  serverRuntimeConfig: {
    // 只在服务器端可用
    mySecret: process.env.MY_SECRET,
  },
}

运行时访问

// 使用 runtime config
import getConfig from 'next/config'

const { serverRuntimeConfig, publicRuntimeConfig } = getConfig()

// 服务器端
console.log(serverRuntimeConfig.mySecret)

// 客户端
console.log(publicRuntimeConfig.staticFolder)

多环境配置

开发环境

# .env.development
NODE_ENV=development
NEXT_PUBLIC_APP_URL=http://localhost:3000
NEXT_PUBLIC_API_URL=http://localhost:4000
DATABASE_URL=postgresql://localhost:5432/mydb_dev

生产环境

# .env.production
NODE_ENV=production
NEXT_PUBLIC_APP_URL=https://myapp.com
NEXT_PUBLIC_API_URL=https://api.myapp.com
DATABASE_URL=postgresql://prod-db:5432/mydb

测试环境

# .env.test
NODE_ENV=test
NEXT_PUBLIC_APP_URL=http://localhost:3000
NEXT_PUBLIC_API_URL=http://localhost:4000
DATABASE_URL=postgresql://localhost:5432/mydb_test

构建时验证

在构建时验证必需的环境变量可以避免部署时出现意外:

// next.config.js
const requiredEnvVars = [
  'DATABASE_URL',
  'API_SECRET',
  'JWT_SECRET',
]

module.exports = {
  env: {
    // 验证必需的环境变量
    ...(requiredEnvVars.reduce((acc, key) => {
      if (!process.env[key]) {
        throw new Error(`Missing environment variable: ${key}`)
      }
      acc[key] = process.env[key]
      return acc
    }, {}) as Record<string, string>),
  },
}

实用模式

API 配置对象

把环境变量组织成一个配置对象,使用起来更方便:

// lib/config.ts
export const config = {
  app: {
    name: process.env.NEXT_PUBLIC_APP_NAME || 'My App',
    url: process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000',
  },
  api: {
    url: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4000',
    timeout: Number(process.env.API_TIMEOUT) || 5000,
  },
  database: {
    url: process.env.DATABASE_URL!,
    poolSize: Number(process.env.DB_POOL_SIZE) || 10,
  },
  auth: {
    jwtSecret: process.env.JWT_SECRET!,
    jwtExpiresIn: process.env.JWT_EXPIRES_IN || '7d',
  },
} as const

环境检查工具

// lib/env.ts
export const isDevelopment = process.env.NODE_ENV === 'development'
export const isProduction = process.env.NODE_ENV === 'production'
export const isTest = process.env.NODE_ENV === 'test'

export function assertDev(): void {
  if (!isDevelopment) {
    throw new Error('This function only works in development')
  }
}

export function assertProd(): void {
  if (!isProduction) {
    throw new Error('This function only works in production')
  }
}

特性开关

使用环境变量来控制功能的开启和关闭:

// lib/features.ts
export const features = {
  newDashboard: process.env.FEATURE_NEW_DASHBOARD === 'true',
  betaAPI: process.env.FEATURE_BETA_API === 'true',
  darkMode: process.env.FEATURE_DARK_MODE !== 'false', // 默认启用
}

// 使用
if (features.newDashboard) {
  // 显示新仪表盘
}

Vercel 环境变量

如果你使用 Vercel 部署,可以在项目设置中配置环境变量。

配置环境变量

  1. 进入项目设置
  2. 选择 "Environment Variables"
  3. 添加变量:

    • DATABASE_URL
    • API_SECRET
    • JWT_SECRET

预览环境

# 为预览部署设置不同的值
NEXT_PUBLIC_APP_URL=https://preview-myapp.vercel.app
DATABASE_URL=postgresql://preview-db:5432/mydb

生产环境

# 为生产部署设置值
NEXT_PUBLIC_APP_URL=https://myapp.com
DATABASE_URL=postgresql://prod-db:5432/mydb

Docker 环境变量

Docker Compose

version: '3.8'

services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://db:5432/mydb
      - API_SECRET=${API_SECRET}
    env_file:
      - .env.production
    depends_on:
      - db

  db:
    image: postgres:15
    environment:
      - POSTGRES_DB=mydb
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=${DB_PASSWORD}

Dockerfile

FROM node:20-alpine

# 设置构建时参数
ARG NODE_ENV=production
ENV NODE_ENV=${NODE_ENV}

# 设置环境变量
ENV NEXT_PUBLIC_API_URL=https://api.example.com

WORKDIR /app
COPY . .
RUN npm run build

CMD ["npm", "start"]

环境变量清单

必需变量

这些变量是应用运行必需的:

# 数据库
DATABASE_URL=postgresql://...

# 认证
JWT_SECRET=your_jwt_secret
NEXTAUTH_SECRET=your_nextauth_secret
NEXTAUTH_URL=https://yourdomain.com

# API
API_SECRET=your_api_secret

可选变量

这些变量可以根据需要添加:

# 应用配置
NEXT_PUBLIC_APP_NAME=My App
NEXT_PUBLIC_APP_URL=http://localhost:3000

# 功能开关
FEATURE_NEW_DASHBOARD=true
FEATURE_BETA_API=false

# 第三方服务
GOOGLE_CLIENT_ID=your_client_id
GOOGLE_CLIENT_SECRET=your_client_secret
STRIPE_SECRET_KEY=your_stripe_key

总结

环境变量管理是 Next.js 开发中的基础技能,合理使用环境变量可以让你的应用在不同环境下都能正常运行。记住区分服务器端和客户端变量,永远不要把敏感信息暴露到客户端,这是保证应用安全的基本原则。

上一页 下一页