Environment Variables

Managing environment variables in ShipKit

Environment Variables

ShipKit uses Zod for type-safe environment variable validation. This ensures runtime safety and provides excellent developer experience with TypeScript integration.

Quick Setup

  1. Copy the example environment file:
cp .env.example .env.local
  1. Generate required secrets:
# Generate AUTH_SECRET
openssl rand -base64 32

# Generate PAYLOAD_SECRET
openssl rand -base64 32

File Structure

  • .env.local - Local development variables (not committed)
  • .env.test - Test environment variables (committed)
  • .env.example - Example variables template (committed)
  • env.schema.ts - TypeScript schema (committed)

Required Variables

Core Authentication

# Base URL for authentication (required)
AUTH_URL="http://localhost:3000"

# JWT secret for session management (required)
# Generate with: openssl rand -base64 32
AUTH_SECRET=""

OAuth Providers

GitHub

# GitHub OAuth credentials (required)
# Get from: https://github.com/settings/developers
AUTH_GITHUB_ID=""
AUTH_GITHUB_SECRET=""

Discord

# Discord OAuth credentials (required)
# Get from: https://discord.com/developers/applications
AUTH_DISCORD_ID=""
AUTH_DISCORD_SECRET=""

Google

# Google OAuth credentials (required)
# Get from: https://console.cloud.google.com/apis/credentials
AUTH_GOOGLE_ID=""
AUTH_GOOGLE_SECRET=""

Database

# PostgreSQL connection string (required)
# Format: postgresql://USER:PASSWORD@HOST:PORT/DATABASE
DATABASE_URL="postgresql://postgres:password@localhost:5432/shipkit"

Email Service

# Resend API key (required)
# Get from: https://resend.com/api-keys
RESEND_API_KEY=""

Payments

# LemonSqueezy API credentials (required)
# Get from: https://app.lemonsqueezy.com/settings/api
LEMONSQUEEZY_API_KEY=""
LEMONSQUEEZY_STORE_ID=""
LEMONSQUEEZY_WEBHOOK_SECRET=""

Content Management

# Payload CMS secret (required)
# Generate with: openssl rand -base64 32
PAYLOAD_SECRET=""

Node Environment

# Node environment (required)
# Values: development, test, production
NODE_ENV="development"

Type Safety

Environment variables are validated using Zod schemas:

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

export const envSchema = z.object({
  // Core Auth
  AUTH_URL: z.string().url(),
  AUTH_SECRET: z.string().min(1),

  // OAuth Providers
  AUTH_GITHUB_ID: z.string().min(1),
  AUTH_GITHUB_SECRET: z.string().min(1),
  AUTH_DISCORD_ID: z.string().min(1),
  AUTH_DISCORD_SECRET: z.string().min(1),
  AUTH_GOOGLE_ID: z.string().min(1),
  AUTH_GOOGLE_SECRET: z.string().min(1),

  // Database
  DATABASE_URL: z.string().min(1),

  // Email
  RESEND_API_KEY: z.string().min(1),

  // Payments
  LEMONSQUEEZY_API_KEY: z.string().min(1),
  LEMONSQUEEZY_STORE_ID: z.string().min(1),
  LEMONSQUEEZY_WEBHOOK_SECRET: z.string().min(1),

  // CMS
  PAYLOAD_SECRET: z.string().min(1),

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

export type Env = z.infer<typeof envSchema>;

Environment Management

Local Development

  1. Initial setup:
cp .env.example .env.local
  1. Update variables:
  • Edit .env.local directly
  • Use pnpm db:sync to sync database configuration

Production (Vercel)

  1. Add variables:
vercel env add MY_NEW_VAR
  1. Remove variables:
vercel env rm MY_VAR
  1. Pull variables:
vercel env pull .env.production

Best Practices

  1. Security

    • Never commit .env.local or .env.production
    • Use strong, unique secrets for each environment
    • Rotate secrets regularly
    • Use different values for development and production
  2. Documentation

    • Keep .env.example updated with all variables
    • Document each variable's purpose and format
    • Include links to external service dashboards
  3. Type Safety

    • Always update env.schema.ts when adding variables
    • Use strict validation rules
    • Handle required variables explicitly
  4. Organization

    • Group related variables together
    • Use clear, descriptive names
    • Follow naming conventions consistently

Common Issues

  1. Database Connection

    • Check DATABASE_URL format
    • Verify database credentials
    • Confirm database server is running
  2. OAuth Configuration

    • Verify callback URLs match AUTH_URL
    • Check provider credentials
    • Confirm OAuth app settings
  3. Email Service

    • Validate RESEND_API_KEY format
    • Check API key permissions
    • Monitor email delivery logs
  4. Payment Integration

    • Verify LemonSqueezy credentials
    • Check webhook configuration
    • Test payment flows in sandbox mode

Security Considerations

  1. Secret Management

    • Use a password manager for secrets
    • Never share secrets in plain text
    • Rotate compromised secrets immediately
  2. Access Control

    • Limit access to production variables
    • Use separate accounts for staging/production
    • Audit variable access regularly
  3. Monitoring

    • Log environment changes
    • Monitor for unauthorized access
    • Set up alerts for critical changes

Validation Scripts

// scripts/validate-env.ts
import { envSchema } from "../src/env.schema";

function validateEnv() {
  try {
    envSchema.parse(process.env);
    console.log("✅ Environment variables are valid");
  } catch (error) {
    console.error("❌ Invalid environment variables:", error);
    process.exit(1);
  }
}

validateEnv();

Run validation:

pnpm tsx scripts/validate-env.ts