env-config

Environment Configuration Skill

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "env-config" with this command: npx skills add sgcarstrends/sgcarstrends/sgcarstrends-sgcarstrends-env-config

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

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

Error Reporting

Discord (workflow error logging only)

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

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

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

Error Reporting (Discord webhook)

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

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
  TELEGRAM_BOT_TOKEN: string;
  TELEGRAM_CHAT_ID: string;

  // Error Reporting
  DISCORD_WEBHOOK_URL: 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 TELEGRAM_BOT_TOKEN: z.string().optional(), TELEGRAM_CHAT_ID: z.string().optional(),

// Error Reporting DISCORD_WEBHOOK_URL: z.string().url().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:

  • Go to repository Settings

  • Secrets and variables → Actions

  • 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

  1. Use Environment-Specific Files

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

  1. 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

  1. 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

  1. 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:

  • Check file name is exactly .env.local

  • Restart development server

  • Verify file is in correct directory

  • Check for syntax errors in .env file

Next.js Public Variables

Issue: NEXT_PUBLIC_ variables not available in browser Solutions:

  • Ensure variable starts with NEXT_PUBLIC_

  • Restart Next.js dev server

  • Check browser console for actual value

SST Secrets Not Working

Issue: SST secrets return undefined Solutions:

  • Verify secret is set for correct stage

  • Check bind configuration in SST

  • Use Config import from sst/node/config

References

Best Practices

  • Use .env.example: Provide template for required variables

  • Type Safety: Define types for environment variables

  • Validation: Validate on application startup

  • SST Secrets: Use for sensitive production values

  • Never Commit: Add .env.local to .gitignore

  • Document: Comment what each variable is for

  • Stage-Specific: Use different values per environment

  • Fail Fast: Validate required variables at startup

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

framer-motion-animations

No summary provided by upstream source.

Repository SourceNeeds Review
General

shadcn-components

No summary provided by upstream source.

Repository SourceNeeds Review
General

api-testing

No summary provided by upstream source.

Repository SourceNeeds Review
General

design-language-system

No summary provided by upstream source.

Repository SourceNeeds Review