Skip to content
Back to skills

Environment Setup

ASecurity

Eliminates the number one blocker for non-technical users — environment variable configuration. Explains what .env files are, auto-generates .env.example, provides step-by-step guides for getting API keys, and validates setup at startup.

  • 7 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added May 27, 2026
developmenttypescriptgobashsqlnextjsdebugginggitapidatabasesecurity

Works with

  • cli
  • api

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add Vimalk0703/shipworthy --skill environment-setup --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Environment Setup?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Environment Setup
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/vimalk0703-environment-setup/badge)](https://www.skillsdirectory.com/skills/vimalk0703-environment-setup)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: environment-setup
description: Eliminates the number one blocker for non-technical users — environment variable configuration. Explains what .env files are, auto-generates .env.example, provides step-by-step guides for getting API keys, and validates setup at startup.
invoke_when: Use when setting up a project for the first time, adding a new service integration, encountering "missing environment variable" errors, or configuring API keys, secrets, or .env files.
---

# Environment Setup

## What Are .env Files? (Plain Language)

Your app needs secret information to work — database passwords, API keys, service credentials. You can't put these directly in your code because:

1. **Security:** If your code is on GitHub, anyone can see your secrets
2. **Flexibility:** Different environments (your laptop vs the live site) need different values
3. **Team safety:** Each developer has their own keys; you don't share secrets in code

A `.env` file is a simple text file that sits on your computer (and ONLY your computer) containing these secrets. It looks like this:

```
# Database connection
DATABASE_URL=postgresql://user:password@localhost:5432/mydb

# Stripe payment processing
STRIPE_SECRET_KEY=sk_test_abc123
STRIPE_PUBLISHABLE_KEY=pk_test_xyz789
```

Your app reads these values when it starts up. The `.env` file is listed in `.gitignore` so it never gets uploaded to GitHub.

---

## The .env.example File

Every project MUST have a `.env.example` file. This is a template that shows what variables are needed WITHOUT revealing actual secret values.

### Auto-Generate Template

When setting up a project or adding a new service, always create or update `.env.example`:

```bash
# =============================================================
# Environment Variables
# Copy this file to .env and fill in your values:
#   cp .env.example .env
# =============================================================

# ----- DATABASE -----
# Your Supabase database connection string
# Get it: Supabase Dashboard > Project Settings > Database > Connection string
DATABASE_URL=postgresql://postgres:[YOUR-PASSWORD]@db.[YOUR-PROJECT-REF].supabase.co:5432/postgres

# Supabase anonymous (public) key — safe to expose in the browser
# Get it: Supabase Dashboard > Project Settings > API > anon/public key
NEXT_PUBLIC_SUPABASE_URL=https://[YOUR-PROJECT-REF].supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key-here

# Supabase service role key — NEVER expose this in the browser
# Get it: Supabase Dashboard > Project Settings > API > service_role key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key-here

# ----- AUTHENTICATION -----
# NextAuth secret — random string for encrypting session tokens
# Generate one: openssl rand -base64 32
NEXTAUTH_SECRET=generate-a-random-string-here
NEXTAUTH_URL=http://localhost:3000

# Google OAuth (optional — for "Sign in with Google")
# Get it: https://console.cloud.google.com/apis/credentials
# Create OAuth 2.0 Client ID, set redirect URI to http://localhost:3000/api/auth/callback/google
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret

# ----- PAYMENTS -----
# Stripe API keys
# Get it: https://dashboard.stripe.com/test/apikeys
# Use TEST keys during development (start with sk_test_ and pk_test_)
STRIPE_SECRET_KEY=sk_test_your-key-here
STRIPE_PUBLISHABLE_KEY=pk_test_your-key-here

# Stripe webhook secret for verifying webhook signatures
# Get it: https://dashboard.stripe.com/test/webhooks — click your endpoint, reveal signing secret
STRIPE_WEBHOOK_SECRET=whsec_your-webhook-secret-here

# ----- EMAIL -----
# Resend API key for sending emails
# Get it: https://resend.com/api-keys — create a new key
RESEND_API_KEY=re_your-api-key-here

# ----- APP CONFIG -----
# Your app's public URL (no trailing slash)
NEXT_PUBLIC_APP_URL=http://localhost:3000
```

**Rules for .env.example:**
- Every variable that the app needs MUST be listed
- Include a comment explaining what it is and where to get the value
- Use placeholder values, never real secrets
- Group related variables with section headers
- Mark which variables are safe for the browser (NEXT_PUBLIC_) vs secret

---

## Where to Get Common API Keys

### Supabase (Database + Auth)
1. Go to [supabase.com](https://supabase.com) and sign in (or create an account — it's free)
2. Click "New Project" and give it a name
3. Set a database password (save this somewhere — you'll need it)
4. Wait about 2 minutes for the project to be created
5. Go to **Project Settings** (gear icon in the left sidebar)
6. Click **API** in the settings menu
7. You'll see:
   - **Project URL** — this is your `NEXT_PUBLIC_SUPABASE_URL`
   - **anon/public key** — this is your `NEXT_PUBLIC_SUPABASE_ANON_KEY` (safe for browsers)
   - **service_role key** — this is your `SUPABASE_SERVICE_ROLE_KEY` (keep secret!)
8. Click **Database** in the settings menu for your `DATABASE_URL`

### Stripe (Payments)
1. Go to [dashboard.stripe.com](https://dashboard.stripe.com) and sign in (or create an account)
2. Make sure you're in **Test mode** (toggle in the top-right says "Test mode")
3. Go to **Developers > API keys**
4. You'll see:
   - **Publishable key** (starts with `pk_test_`) — this is your `STRIPE_PUBLISHABLE_KEY`
   - **Secret key** (starts with `sk_test_`) — click "Reveal" — this is your `STRIPE_SECRET_KEY`
5. For webhooks: Go to **Developers > Webhooks > Add endpoint**
   - URL: `https://your-domain.com/api/webhooks/stripe` (for local dev, use Stripe CLI)
   - Select events you want to listen for
   - After creating, click the endpoint and find "Signing secret" — this is your `STRIPE_WEBHOOK_SECRET`

### Resend (Email)
1. Go to [resend.com](https://resend.com) and sign in (or create an account)
2. Go to **API Keys** in the sidebar
3. Click **Create API Key**
4. Give it a name (like "my-app-dev") and choose permissions (Full access for development)
5. Copy the key (starts with `re_`) — this is your `RESEND_API_KEY`
6. Important: you can only see the key once. If you lose it, create a new one.

### Google OAuth (Sign in with Google)
1. Go to [console.cloud.google.com](https://console.cloud.google.com)
2. Create a new project (or select existing)
3. Go to **APIs & Services > Credentials**
4. Click **Create Credentials > OAuth 2.0 Client ID**
5. Application type: **Web application**
6. Add authorized redirect URI: `http://localhost:3000/api/auth/callback/google`
7. Click **Create**
8. You'll see your **Client ID** and **Client Secret** — these are your `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET`
9. For production, add your real domain to the redirect URIs

### NextAuth Secret
This isn't an API key — it's a random string your app uses to encrypt session data. Generate one:
```bash
openssl rand -base64 32
```
Copy the output and use it as your `NEXTAUTH_SECRET`. Any random string works, but make it long and unpredictable.

---

## NEXT_PUBLIC_ Prefix Explained

In Next.js, environment variables are SECRET by default — they only exist on the server.

To make a variable available in the browser (client-side), prefix it with `NEXT_PUBLIC_`.

### What's SAFE to Expose (Use NEXT_PUBLIC_)
- Supabase anon key (`NEXT_PUBLIC_SUPABASE_ANON_KEY`) — designed to be public, Row Level Security protects your data
- Supabase project URL (`NEXT_PUBLIC_SUPABASE_URL`) — just a URL, not a secret
- Stripe publishable key (`STRIPE_PUBLISHABLE_KEY`) — designed to be public
- Your app's URL (`NEXT_PUBLIC_APP_URL`) — obviously public
- Analytics IDs (Google Analytics, Plausible) — public tracking codes
- Feature flags that are non-sensitive

### What MUST Stay Secret (NO NEXT_PUBLIC_ Prefix)
- Database URLs (`DATABASE_URL`) — contains password
- Supabase service role key (`SUPABASE_SERVICE_ROLE_KEY`) — bypasses Row Level Security
- Stripe secret key (`STRIPE_SECRET_KEY`) — can charge cards
- Stripe webhook secret (`STRIPE_WEBHOOK_SECRET`) — verifies webhook authenticity
- Resend API key (`RESEND_API_KEY`) — can send emails as you
- NextAuth secret (`NEXTAUTH_SECRET`) — encrypts sessions
- Any password, token, or secret key

**The Rule:** If someone with this value could do something harmful (charge money, read private data, send emails, access your database), it MUST NOT have `NEXT_PUBLIC_` prefix.

---

## Development vs Production Variables

You'll have at least two sets of values:

### Development (Your Computer)
```
DATABASE_URL=postgresql://postgres:localpassword@localhost:5432/mydb
STRIPE_SECRET_KEY=sk_test_...    # Test key — no real charges
NEXT_PUBLIC_APP_URL=http://localhost:3000
```

### Production (Live Site)
```
DATABASE_URL=postgresql://postgres:prodpassword@db.abc123.supabase.co:5432/postgres
STRIPE_SECRET_KEY=sk_live_...    # Live key — real charges!
NEXT_PUBLIC_APP_URL=https://myapp.com
```

**How to manage this:**
- `.env` or `.env.local` — your local development values (never committed to git)
- Production values — set in your hosting provider's dashboard (Vercel > Settings > Environment Variables)
- Never copy production secrets to your local `.env` unless you have a specific reason

---

## Startup Validation

Every project should crash immediately with a clear message if required environment variables are missing. This saves hours of debugging mysterious errors.

### Implementation

Add this to your project's startup (e.g., `lib/env.ts` or `config/env.ts`):

```typescript
// lib/env.ts — Validate environment variables at startup

function requireEnv(name: string): string {
  const value = process.env[name];
  if (!value) {
    throw new Error(
      `Missing required environment variable: ${name}\n` +
      `Check your .env file. See .env.example for setup instructions.`
    );
  }
  return value;
}

function optionalEnv(name: string, defaultValue: string = ''): string {
  return process.env[name] || defaultValue;
}

// Required — app won't start without these
export const env = {
  DATABASE_URL: requireEnv('DATABASE_URL'),
  NEXTAUTH_SECRET: requireEnv('NEXTAUTH_SECRET'),

  // Required for payments (only if payments are enabled)
  // STRIPE_SECRET_KEY: requireEnv('STRIPE_SECRET_KEY'),

  // Optional with defaults
  APP_URL: optionalEnv('NEXT_PUBLIC_APP_URL', 'http://localhost:3000'),
};
```

Then import `env` instead of using `process.env` directly throughout your app. If any required variable is missing, the app crashes on startup with a message like:

```
Error: Missing required environment variable: DATABASE_URL
Check your .env file. See .env.example for setup instructions.
```

This is infinitely better than discovering 10 minutes into debugging that your database URL is undefined.

---

## .gitignore Check

Every time you set up or modify environment configuration, verify that `.gitignore` includes:

```
# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
```

**If `.env` is NOT in `.gitignore`, add it immediately.** This is a security-critical step. If `.env` was ever committed to git history, consider all secrets in it compromised — rotate every key.

### How to Check
```bash
# Check if .env is in .gitignore
grep -q "^\.env$" .gitignore && echo "OK: .env is in .gitignore" || echo "WARNING: .env is NOT in .gitignore — add it now!"

# Check if .env was ever committed
git log --all --oneline -- .env
# If this shows any results, those secrets may be compromised
```

---

## Quick Start for New Projects

When a user starts a new project, run through this checklist:

1. Create `.env.example` with all required variables and plain-language comments
2. Create `.env` by copying `.env.example`
3. Verify `.env` is in `.gitignore`
4. Help the user get each API key (link to the service, step-by-step)
5. Add startup validation (`lib/env.ts`)
6. Test that the app starts successfully with all variables set
7. Test that the app crashes clearly when a required variable is missing

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…