环境变量是我们开发中经常用到的配置方式,通过它们可以管理不同环境下的配置信息,比如数据库连接、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 部署,可以在项目设置中配置环境变量。
配置环境变量
- 进入项目设置
- 选择 "Environment Variables"
添加变量:
DATABASE_URLAPI_SECRETJWT_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/mydbDocker 环境变量
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 开发中的基础技能,合理使用环境变量可以让你的应用在不同环境下都能正常运行。记住区分服务器端和客户端变量,永远不要把敏感信息暴露到客户端,这是保证应用安全的基本原则。