Back to Skills

env-config

sgcarstrends
Updated Today
29 views
9
1
9
View on GitHub
Testinggeneral

About

The env-config skill helps developers manage environment variables and secrets across development, staging, and production environments within an SST-based monorepo. Use it for adding new secrets, updating stage-specific configurations, or debugging environment-related issues. It provides capabilities to read, edit, and grep environment files to maintain consistent configuration management.

Documentation

Environment Configuration Skill

This skill helps you manage environment variables and secrets across the monorepo.

When to Use This Skill

  • Adding new environment variables
  • Configuring stage-specific variables
  • Setting up secrets for deployment
  • Debugging environment variable issues
  • Managing API keys and tokens
  • Configuring database connections
  • Setting up third-party integrations

Environment File Structure

sgcarstrends/
├── .env.example              # Example env file (committed)
├── .env.local                # Local development (not committed)
├── .env.test                 # Test environment (not committed)
├── apps/
│   ├── api/
│   │   ├── .env.example
│   │   └── .env.local
│   └── web/
│       ├── .env.example
│       └── .env.local
└── packages/
    └── database/
        ├── .env.example
        └── .env.local

Environment Variable Categories

Database

# PostgreSQL
DATABASE_URL=postgresql://user:password@host:5432/database

# Connection pooling (optional)
DATABASE_POOL_MIN=2
DATABASE_POOL_MAX=10

Redis

# Upstash Redis
UPSTASH_REDIS_REST_URL=https://your-instance.upstash.io
UPSTASH_REDIS_REST_TOKEN=your-token-here

AI Services

# Google Gemini
GOOGLE_GEMINI_API_KEY=your-api-key
GEMINI_MODEL=gemini-1.5-pro-latest

Workflows & Queues

# QStash
QSTASH_TOKEN=your-qstash-token
QSTASH_CURRENT_SIGNING_KEY=your-signing-key
QSTASH_NEXT_SIGNING_KEY=your-next-signing-key

Social Media

# Discord
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/...

# Telegram
TELEGRAM_BOT_TOKEN=your-bot-token
TELEGRAM_CHAT_ID=your-chat-id

# Twitter
TWITTER_BEARER_TOKEN=your-bearer-token

# LinkedIn
LINKEDIN_ACCESS_TOKEN=your-access-token
LINKEDIN_ORG_ID=your-org-id

Storage

# Vercel Blob
BLOB_READ_WRITE_TOKEN=vercel_blob_token

Next.js Public Variables

# Public variables (exposed to browser)
NEXT_PUBLIC_API_URL=https://api.sgcarstrends.com
NEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX
NEXT_PUBLIC_SITE_URL=https://sgcarstrends.com

Build & Runtime

# Node environment
NODE_ENV=development  # development | production | test

# Next.js
NEXT_TELEMETRY_DISABLED=1

Environment File Examples

Root .env.example

# Database
DATABASE_URL=postgresql://user:password@localhost:5432/sgcarstrends

# Redis
UPSTASH_REDIS_REST_URL=https://your-instance.upstash.io
UPSTASH_REDIS_REST_TOKEN=your-token-here

# AI Services
GOOGLE_GEMINI_API_KEY=your-api-key

# Workflows
QSTASH_TOKEN=your-qstash-token

# Social Media
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/...
TELEGRAM_BOT_TOKEN=your-bot-token
TELEGRAM_CHAT_ID=your-chat-id

# Storage
BLOB_READ_WRITE_TOKEN=vercel_blob_token

apps/web/.env.example

# API
NEXT_PUBLIC_API_URL=http://localhost:3000

# Database (for server components)
DATABASE_URL=postgresql://user:password@localhost:5432/sgcarstrends

# Redis
UPSTASH_REDIS_REST_URL=https://your-instance.upstash.io
UPSTASH_REDIS_REST_TOKEN=your-token-here

# Analytics
NEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX

# Site URL
NEXT_PUBLIC_SITE_URL=http://localhost:3001

apps/api/.env.example

# Database
DATABASE_URL=postgresql://user:password@localhost:5432/sgcarstrends

# Redis
UPSTASH_REDIS_REST_URL=https://your-instance.upstash.io
UPSTASH_REDIS_REST_TOKEN=your-token-here

# All other API-specific vars...

Stage-Specific Configuration

Development

# .env.local (development)
NODE_ENV=development
DATABASE_URL=postgresql://localhost:5432/sgcarstrends_dev
NEXT_PUBLIC_API_URL=http://localhost:3000
NEXT_PUBLIC_SITE_URL=http://localhost:3001

Staging

# Staging (set in SST or CI/CD)
NODE_ENV=production
DATABASE_URL=postgresql://...staging...
NEXT_PUBLIC_API_URL=https://api.staging.sgcarstrends.com
NEXT_PUBLIC_SITE_URL=https://staging.sgcarstrends.com

Production

# Production (set in SST or CI/CD)
NODE_ENV=production
DATABASE_URL=postgresql://...production...
NEXT_PUBLIC_API_URL=https://api.sgcarstrends.com
NEXT_PUBLIC_SITE_URL=https://sgcarstrends.com

SST Configuration

Using Environment Variables in SST

// infra/api.ts
import { StackContext, Function } from "sst/constructs";

export function API({ stack, app }: StackContext) {
  const api = new Function(stack, "api", {
    handler: "apps/api/src/index.handler",
    environment: {
      NODE_ENV: app.stage === "production" ? "production" : "development",
      DATABASE_URL: process.env.DATABASE_URL!,
      UPSTASH_REDIS_REST_URL: process.env.UPSTASH_REDIS_REST_URL!,
      UPSTASH_REDIS_REST_TOKEN: process.env.UPSTASH_REDIS_REST_TOKEN!,
      GOOGLE_GEMINI_API_KEY: process.env.GOOGLE_GEMINI_API_KEY!,
      QSTASH_TOKEN: process.env.QSTASH_TOKEN!,
      DISCORD_WEBHOOK_URL: process.env.DISCORD_WEBHOOK_URL!,
      TELEGRAM_BOT_TOKEN: process.env.TELEGRAM_BOT_TOKEN!,
      TELEGRAM_CHAT_ID: process.env.TELEGRAM_CHAT_ID!,
    },
  });

  return { api };
}

SST Secrets

For sensitive values, use SST secrets:

# Set secret for specific stage
npx sst secrets set DATABASE_URL "postgresql://..." --stage production
npx sst secrets set UPSTASH_REDIS_REST_TOKEN "token" --stage production

# List secrets
npx sst secrets list --stage production

# Remove secret
npx sst secrets remove DATABASE_URL --stage production

Access in code:

import { Config } from "sst/node/config";

export const handler = async () => {
  const databaseUrl = Config.DATABASE_URL;
  // Use secret...
};

Define in SST config:

// infra/api.ts
import { Config } from "sst/constructs";

export function API({ stack }: StackContext) {
  const DATABASE_URL = new Config.Secret(stack, "DATABASE_URL");

  const api = new Function(stack, "api", {
    handler: "apps/api/src/index.handler",
    bind: [DATABASE_URL],
  });
}

TypeScript Type Safety

Declare Environment Variables

// env.d.ts (root or app-specific)
declare global {
  namespace NodeJS {
    interface ProcessEnv {
      // Database
      DATABASE_URL: string;

      // Redis
      UPSTASH_REDIS_REST_URL: string;
      UPSTASH_REDIS_REST_TOKEN: string;

      // AI
      GOOGLE_GEMINI_API_KEY: string;
      GEMINI_MODEL?: string;

      // Workflows
      QSTASH_TOKEN: string;

      // Social Media
      DISCORD_WEBHOOK_URL: string;
      TELEGRAM_BOT_TOKEN: string;
      TELEGRAM_CHAT_ID: string;

      // Storage
      BLOB_READ_WRITE_TOKEN: string;

      // Next.js Public
      NEXT_PUBLIC_API_URL: string;
      NEXT_PUBLIC_SITE_URL: string;
      NEXT_PUBLIC_GA_MEASUREMENT_ID?: string;

      // Build
      NODE_ENV: "development" | "production" | "test";
    }
  }
}

export {};

Validate Environment Variables

// lib/env.ts
import { z } from "zod";

const envSchema = z.object({
  // Database
  DATABASE_URL: z.string().url(),

  // Redis
  UPSTASH_REDIS_REST_URL: z.string().url(),
  UPSTASH_REDIS_REST_TOKEN: z.string().min(1),

  // AI
  GOOGLE_GEMINI_API_KEY: z.string().min(1),

  // Workflows
  QSTASH_TOKEN: z.string().min(1),

  // Social Media
  DISCORD_WEBHOOK_URL: z.string().url().optional(),
  TELEGRAM_BOT_TOKEN: z.string().optional(),
  TELEGRAM_CHAT_ID: z.string().optional(),

  // Next.js
  NEXT_PUBLIC_API_URL: z.string().url(),
  NEXT_PUBLIC_SITE_URL: z.string().url(),

  // Build
  NODE_ENV: z.enum(["development", "production", "test"]),
});

export const env = envSchema.parse(process.env);

// Usage
import { env } from "./lib/env";
const db = new Database(env.DATABASE_URL);

Loading Environment Variables

Next.js

Next.js automatically loads .env.local:

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  env: {
    CUSTOM_KEY: process.env.CUSTOM_KEY,
  },
};

export default nextConfig;

Drizzle Studio

// packages/database/drizzle.config.ts
import { defineConfig } from "drizzle-kit";
import * as dotenv from "dotenv";

// Load environment variables
dotenv.config({ path: "../../.env.local" });

export default defineConfig({
  schema: "./src/db/schema",
  out: "./migrations",
  dialect: "postgresql",
  dbCredentials: {
    url: process.env.DATABASE_URL!,
  },
});

Vitest

// vitest.config.ts
import { defineConfig } from "vitest/config";
import * as dotenv from "dotenv";

// Load test environment
dotenv.config({ path: ".env.test" });

export default defineConfig({
  test: {
    env: {
      DATABASE_URL: process.env.DATABASE_URL!,
    },
  },
});

GitHub Actions

Secrets Configuration

Set secrets in GitHub:

  1. Go to repository Settings
  2. Secrets and variables → Actions
  3. Add repository secrets

Use in Workflows

# .github/workflows/deploy-prod.yml
name: Deploy Production

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    env:
      DATABASE_URL: ${{ secrets.DATABASE_URL }}
      UPSTASH_REDIS_REST_URL: ${{ secrets.UPSTASH_REDIS_REST_URL }}
      UPSTASH_REDIS_REST_TOKEN: ${{ secrets.UPSTASH_REDIS_REST_TOKEN }}
      GOOGLE_GEMINI_API_KEY: ${{ secrets.GOOGLE_GEMINI_API_KEY }}

    steps:
      - uses: actions/checkout@v4

      - name: Deploy
        run: pnpm deploy:prod

Debugging Environment Issues

Check Variables Are Set

// Check at runtime
console.log("DATABASE_URL:", process.env.DATABASE_URL ? "✓ Set" : "✗ Not set");
console.log("REDIS_URL:", process.env.UPSTASH_REDIS_REST_URL ? "✓ Set" : "✗ Not set");

// Fail early if missing
if (!process.env.DATABASE_URL) {
  throw new Error("DATABASE_URL is required");
}

Use dotenv-cli

# Install
pnpm add -D dotenv-cli

# Run command with env file
pnpm dotenv -e .env.local -- pnpm dev

# Run tests with test env
pnpm dotenv -e .env.test -- pnpm test

Debug Script

// scripts/check-env.ts
import * as dotenv from "dotenv";

dotenv.config({ path: ".env.local" });

const requiredVars = [
  "DATABASE_URL",
  "UPSTASH_REDIS_REST_URL",
  "UPSTASH_REDIS_REST_TOKEN",
];

console.log("Checking environment variables...\n");

let missing = 0;

for (const varName of requiredVars) {
  const value = process.env[varName];

  if (value) {
    console.log(`✓ ${varName}`);
  } else {
    console.log(`✗ ${varName} - MISSING`);
    missing++;
  }
}

if (missing > 0) {
  console.error(`\n❌ ${missing} required variables are missing!`);
  process.exit(1);
} else {
  console.log("\n✅ All required variables are set!");
}

Add to package.json:

{
  "scripts": {
    "check-env": "tsx scripts/check-env.ts"
  }
}

Common Patterns

Conditional Loading

// Load different configs based on environment
const config = {
  development: {
    apiUrl: "http://localhost:3000",
    debug: true,
  },
  production: {
    apiUrl: process.env.NEXT_PUBLIC_API_URL,
    debug: false,
  },
  test: {
    apiUrl: "http://localhost:3000",
    debug: false,
  },
}[process.env.NODE_ENV || "development"];

Default Values

const config = {
  apiUrl: process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000",
  cacheTimeout: parseInt(process.env.CACHE_TIMEOUT || "3600", 10),
  enableFeature: process.env.ENABLE_FEATURE === "true",
};

Required vs Optional

// Required (throw if missing)
const databaseUrl = process.env.DATABASE_URL!;

// Optional (with default)
const cacheTimeout = process.env.CACHE_TIMEOUT || "3600";

// Optional (may be undefined)
const optionalKey: string | undefined = process.env.OPTIONAL_KEY;

Security Best Practices

1. Never Commit Secrets

# .gitignore
.env
.env.local
.env.*.local
.env.development.local
.env.test.local
.env.production.local

2. Use Environment-Specific Files

.env.example        ✅ Committed (no sensitive data)
.env.local          ❌ Not committed (has secrets)
.env.development    ❌ Not committed
.env.production     ❌ Not committed

3. Rotate Secrets Regularly

# Update secret for all stages
npx sst secrets set API_KEY "new-key" --stage dev
npx sst secrets set API_KEY "new-key" --stage staging
npx sst secrets set API_KEY "new-key" --stage production

4. Least Privilege

Only grant necessary permissions:

  • Database: Use read-only user for read operations
  • API keys: Use restricted scopes
  • AWS: Use IAM roles with minimal permissions

5. Audit Access

# Check who has access to secrets
npx sst secrets list --stage production

# Review CloudWatch logs for unauthorized access

Testing with Environment Variables

// __tests__/api.test.ts
import { describe, it, expect, beforeAll } from "vitest";

describe("API Tests", () => {
  beforeAll(() => {
    // Set test environment variables
    process.env.DATABASE_URL = "postgresql://test:test@localhost:5432/test";
    process.env.UPSTASH_REDIS_REST_URL = "https://test.upstash.io";
  });

  it("uses correct environment", () => {
    expect(process.env.DATABASE_URL).toContain("test");
  });
});

Troubleshooting

Variables Not Loading

Issue: Environment variables are undefined Solutions:

  1. Check file name is exactly .env.local
  2. Restart development server
  3. Verify file is in correct directory
  4. Check for syntax errors in .env file

Next.js Public Variables

Issue: NEXT_PUBLIC_ variables not available in browser Solutions:

  1. Ensure variable starts with NEXT_PUBLIC_
  2. Restart Next.js dev server
  3. Check browser console for actual value

SST Secrets Not Working

Issue: SST secrets return undefined Solutions:

  1. Verify secret is set for correct stage
  2. Check bind configuration in SST
  3. Use Config import from sst/node/config

References

Best Practices

  1. Use .env.example: Provide template for required variables
  2. Type Safety: Define types for environment variables
  3. Validation: Validate on application startup
  4. SST Secrets: Use for sensitive production values
  5. Never Commit: Add .env.local to .gitignore
  6. Document: Comment what each variable is for
  7. Stage-Specific: Use different values per environment
  8. Fail Fast: Validate required variables at startup

Quick Install

/plugin add https://github.com/sgcarstrends/sgcarstrends/tree/main/env-config

Copy and paste this command in Claude Code to install this skill

GitHub 仓库

sgcarstrends/sgcarstrends
Path: .claude/skills/env-config
apiaws-lambdabackendhonojob-schedulerneon-postgres

Related Skills

subagent-driven-development

Development

This skill executes implementation plans by dispatching a fresh subagent for each independent task, with code review between tasks. It enables fast iteration while maintaining quality gates through this review process. Use it when working on mostly independent tasks within the same session to ensure continuous progress with built-in quality checks.

View skill

algorithmic-art

Meta

This Claude Skill creates original algorithmic art using p5.js with seeded randomness and interactive parameters. It generates .md files for algorithmic philosophies, plus .html and .js files for interactive generative art implementations. Use it when developers need to create flow fields, particle systems, or other computational art while avoiding copyright issues.

View skill

executing-plans

Design

Use the executing-plans skill when you have a complete implementation plan to execute in controlled batches with review checkpoints. It loads and critically reviews the plan, then executes tasks in small batches (default 3 tasks) while reporting progress between each batch for architect review. This ensures systematic implementation with built-in quality control checkpoints.

View skill

cost-optimization

Other

This Claude Skill helps developers optimize cloud costs through resource rightsizing, tagging strategies, and spending analysis. It provides a framework for reducing cloud expenses and implementing cost governance across AWS, Azure, and GCP. Use it when you need to analyze infrastructure costs, right-size resources, or meet budget constraints.

View skill