xiaohongshu
xiaohongshu
infra-env
Environment variable conventions and security practices for Next.js projects. This skill should be used when setting up environment configuration, managing secrets, or establishing security patterns for a new project.
Full skill instructions
Conventions and security practices for managing environment variables in Next.js projects.
.env.example templates.env* files with real values stay out of git.env.example shows structure without valuesNEXT_PUBLIC_* reaches the browser| File | Purpose | Git? |
|---|---|---|
.env | Default values (shared) | Optional |
.env.local | Local overrides (secrets) | Never |
.env.development | Dev-specific defaults | Optional |
.env.production | Prod defaults (no secrets) | Optional |
.env.example | Template for developers | Always |
Load order (later overrides earlier):
.env.env.local.env.development / .env.production (based on NODE_ENV).env.development.local / .env.production.localCreate .env.example as documentation:
# =============================================================================
# ENVIRONMENT CONFIGURATION
# =============================================================================
# Copy this file to .env.local and fill in the values
# NEVER commit .env.local to git
# =============================================================================
# -----------------------------------------------------------------------------
# Application
# -----------------------------------------------------------------------------
NEXT_PUBLIC_APP_URL=http://localhost:3000
NEXT_PUBLIC_APP_NAME=MyApp
# -----------------------------------------------------------------------------
# Database
# -----------------------------------------------------------------------------
# PostgreSQL connection string
# Format: postgres://USER:PASSWORD@HOST:PORT/DATABASE
DATABASE_URL=postgres://postgres:postgres@localhost:5432/myapp
# -----------------------------------------------------------------------------
# Authentication (Better Auth)
# -----------------------------------------------------------------------------
# Generate with: openssl rand -base64 32
BETTER_AUTH_SECRET=your-secret-key-min-32-characters-here
BETTER_AUTH_URL=http://localhost:3000
# OAuth Providers (get from provider console)
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
# -----------------------------------------------------------------------------
# Third-Party Services (Optional)
# -----------------------------------------------------------------------------
# Analytics
# NEXT_PUBLIC_POSTHOG_KEY=
# NEXT_PUBLIC_POSTHOG_HOST=https://app.posthog.com
# Email (Resend)
# RESEND_API_KEY=
# Storage (S3/R2/Minio)
# S3_BUCKET=
# S3_REGION=
# S3_ACCESS_KEY_ID=
# S3_SECRET_ACCESS_KEY=
# S3_ENDPOINT=
# -----------------------------------------------------------------------------
# Development Only
# -----------------------------------------------------------------------------
# Set to 'true' to enable debug features
# DEBUG=false
# Database
DATABASE_URL=...
# Auth secrets
BETTER_AUTH_SECRET=...
GOOGLE_CLIENT_SECRET=...
# API keys
RESEND_API_KEY=...
OPENAI_API_KEY=...
Must start with NEXT_PUBLIC_:
# URLs and public identifiers
NEXT_PUBLIC_APP_URL=http://localhost:3000
NEXT_PUBLIC_POSTHOG_KEY=phc_xxx
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_xxx
SCREAMING_SNAKE_CASEDATABASE_URL not DBGOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRETRESEND_API_KEY, STRIPE_SECRET_KEYCreate src/lib/env.ts:
import { z } from 'zod';
const envSchema = z.object({
// Database
DATABASE_URL: z.string().url(),
// Auth
BETTER_AUTH_SECRET: z.string().min(32),
BETTER_AUTH_URL: z.string().url(),
// OAuth (optional in development)
GOOGLE_CLIENT_ID: z.string().optional(),
GOOGLE_CLIENT_SECRET: z.string().optional(),
GITHUB_CLIENT_ID: z.string().optional(),
GITHUB_CLIENT_SECRET: z.string().optional(),
// Public
NEXT_PUBLIC_APP_URL: z.string().url(),
// Node environment
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
});
// Validate at module load
const parsed = envSchema.safeParse(process.env);
if (!parsed.success) {
console.error('❌ Invalid environment variables:');
console.error(parsed.error.flatten().fieldErrors);
throw new Error('Invalid environment variables');
}
export const env = parsed.data;
import { env } from '@/lib/env';
// Type-safe access
const dbUrl = env.DATABASE_URL;
const isProduction = env.NODE_ENV === 'production';
For client-side validation, create src/lib/env-client.ts:
import { z } from 'zod';
const clientEnvSchema = z.object({
NEXT_PUBLIC_APP_URL: z.string().url(),
NEXT_PUBLIC_POSTHOG_KEY: z.string().optional(),
});
export const clientEnv = clientEnvSchema.parse({
NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY,
});
# ❌ Secrets in .env.example with real values
API_KEY=sk_live_real_key_here
# ❌ Secrets with NEXT_PUBLIC_ prefix
NEXT_PUBLIC_DATABASE_URL=postgres://...
# ❌ Committing .env.local
# (should be in .gitignore)
# ✅ Placeholder values in .env.example
API_KEY=your-api-key-here
# ✅ Secrets stay server-only
DATABASE_URL=postgres://...
# ✅ Only public info gets NEXT_PUBLIC_
NEXT_PUBLIC_APP_URL=http://localhost:3000
Ensure these are in .gitignore:
# Environment files with secrets
.env.local
.env.*.local
.env.development.local
.env.production.local
# Keep .env.example in git
!.env.example
# Generate random secret (32 bytes, base64)
openssl rand -base64 32
# Generate random secret (hex)
openssl rand -hex 32
# Generate UUID
uuidgen
# .env.development
NODE_ENV=development
NEXT_PUBLIC_APP_URL=http://localhost:3000
# Set in deployment platform (Railway, Vercel, AWS)
# Never in files committed to git
NODE_ENV=production
DATABASE_URL=postgres://prod-user:prod-pass@prod-host:5432/prod-db
BETTER_AUTH_SECRET=production-secret-here
NEXT_PUBLIC_APP_URL=https://myapp.com
# .env.test
NODE_ENV=test
DATABASE_URL=postgres://postgres:postgres@localhost:5432/myapp_test
cp .env.example .env.localdocker compose up -dnpm run dev# Set via CLI
railway variables --set DATABASE_URL=postgres://...
railway variables --set BETTER_AUTH_SECRET=...
# Or use Railway dashboard
# Settings → Variables → Add Variable
# Set via CLI
vercel env add DATABASE_URL production
vercel env add BETTER_AUTH_SECRET production
# Or use Vercel dashboard
# Settings → Environment Variables
Use AWS Secrets Manager or Parameter Store:
# Store secret
aws secretsmanager create-secret \
--name myapp/production/database-url \
--secret-string "postgres://..."
# Reference in task definition
"secrets": [
{
"name": "DATABASE_URL",
"valueFrom": "arn:aws:secretsmanager:region:account:secret:myapp/production/database-url"
}
]
// Temporary debug (remove before commit!)
console.log('DATABASE_URL exists:', !!process.env.DATABASE_URL);
console.log('NODE_ENV:', process.env.NODE_ENV);
Variable undefined at runtime:
.env.local not .env.local.txt)Client can't access variable:
NEXT_PUBLIC_ prefixWrong value used:
.env.local overrides .env).next cache: rm -rf .nextWorks locally, fails in production:
.env.example with all variables.env.local to .gitignoresrc/lib/env.ts for validation| Variable Type | Prefix | Accessible In |
|---|---|---|
| Server secret | None | Server only |
| Public config | NEXT_PUBLIC_ | Server + Client |
| File | Committed? | Contains Secrets? |
|---|---|---|
.env.example | Yes | No (placeholders) |
.env | Optional | No |
.env.local | No | Yes |
xiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience