Environment Variables
Environment Variables
Section titled “Environment Variables”Complete guide to configuring environment variables for production deployment of your podcast website.
Required Variables
Section titled “Required Variables”These variables are required for the site to work:
# Sanity CMSPUBLIC_SANITY_PROJECT_ID="abc123xyz"PUBLIC_SANITY_DATASET="production"PUBLIC_SANITY_API_VERSION="2024-01-01"
# Site ConfigurationPUBLIC_SITE_URL="https://yourpodcast.com"Optional Variables
Section titled “Optional Variables”Add these for additional features:
Newsletter (ConvertKit)
Section titled “Newsletter (ConvertKit)”CONVERTKIT_API_KEY="your-convertkit-api-secret"CONVERTKIT_FORM_ID="your-form-id"Where to get:
- ConvertKit account → Settings → Advanced → API Secret
- Forms → Select form → Form ID in URL
Enables:
- Newsletter signup forms
- Email subscription collection
- ConvertKit integration
Contribution Emails (Resend)
Section titled “Contribution Emails (Resend)”RESEND_API_KEY="re_..."Where to get:
- Resend account → API Keys → Create
- Verify domain for
NOTIFICATION_EMAIL
Enables:
- Contribution form submissions
- Email notifications
- Community engagement
Analytics (Google Analytics 4)
Section titled “Analytics (Google Analytics 4)”PUBLIC_GA_MEASUREMENT_ID="G-XXXXXXXXXX"Where to get:
- Google Analytics
- Create GA4 property
- Data Streams → Web → Measurement ID
Enables:
- Visitor tracking
- Page view analytics
- User behavior insights
Error Tracking (Sentry)
Section titled “Error Tracking (Sentry)”SENTRY_ENVIRONMENT="production"SENTRY_RELEASE="1.0.0"Where to get:
- Sentry account
- Create project
- Settings → Client Keys (DSN)
Enables:
- Error tracking
- Performance monitoring
- Issue alerting
RSS Import
Section titled “RSS Import”RSS_FEED_URL="https://yourpodcast.com/feed.xml"Used by:
npm run import:episodesscript- Importing existing episodes from RSS feed
Variable Types
Section titled “Variable Types”Public Variables
Section titled “Public Variables”Accessible in browser code (client and server):
PUBLIC_* # All variables starting with PUBLIC_Example:
// ✅ Works in browserconst projectId = import.meta.env.PUBLIC_SANITY_PROJECT_ID;
// ✅ Also available on serverimport { getEnv } from '@rejected-media/podcast-framework-core';const projectId = getEnv('PUBLIC_SANITY_PROJECT_ID', context);Private Variables
Section titled “Private Variables”Only accessible on server (API routes, server functions):
# No PUBLIC_ prefixRESEND_API_KEY="..."CONVERTKIT_API_KEY="..."SENTRY_DSN="..."Example:
// ✅ Works in API routesimport { getEnv } from '@rejected-media/podcast-framework-core';const apiKey = getEnv('RESEND_API_KEY', context);
// ❌ NOT accessible in browser// import.meta.env.RESEND_API_KEY → undefinedPlatform-Specific Setup
Section titled “Platform-Specific Setup”Cloudflare Pages
Section titled “Cloudflare Pages”- Dashboard → Workers & Pages → Your project
- Settings → Environment variables
- Click “Add variable”
- Enter name and value
- Select environment (Production, Preview, or both)
- Click “Save”
Access in code:
import { getEnv } from '@rejected-media/podcast-framework-core';
export const POST: APIRoute = async (context) => { const apiKey = getEnv('API_KEY', context); // ✅ Works};Netlify
Section titled “Netlify”- Site settings → Environment variables
- Click “Add a variable”
- Enter key and value
- Select scopes (Production, Deploy previews, Branch deploys)
- Click “Create variable”
Access in code:
import { getEnv } from '@rejected-media/podcast-framework-core';
export const handler = async (event, context) => { const apiKey = getEnv('API_KEY'); // ✅ Works};Vercel
Section titled “Vercel”- Project → Settings → Environment Variables
- Enter name and value
- Select environments (Production, Preview, Development)
- Click “Save”
Access in code:
import { getEnv } from '@rejected-media/podcast-framework-core';
export default async function handler(req, res) { const apiKey = getEnv('API_KEY'); // ✅ Works}Variable Management
Section titled “Variable Management”Development vs Production
Section titled “Development vs Production”Use different values for different environments:
.env (local development):
PUBLIC_SANITY_DATASET="development"PUBLIC_SITE_URL="http://localhost:4321"NOTIFICATION_EMAIL="dev@localhost"Production (Cloudflare dashboard):
PUBLIC_SANITY_DATASET="production"PUBLIC_SITE_URL="https://yourpodcast.com"Staging Environment
Section titled “Staging Environment”Create staging dataset and deploy:
# Staging branchgit checkout -b staging
# Staging variablesPUBLIC_SANITY_DATASET="staging"PUBLIC_SITE_URL="https://staging.yourpodcast.com"Environment Variable Template
Section titled “Environment Variable Template”Keep .env.template updated:
# .env.template (commit to git)
# Sanity CMS (Required)PUBLIC_SANITY_PROJECT_ID=PUBLIC_SANITY_DATASET=PUBLIC_SANITY_API_VERSION=
# Site Configuration (Required)PUBLIC_SITE_URL=
# Newsletter (Optional)CONVERTKIT_API_KEY=CONVERTKIT_FORM_ID=
# Contributions (Optional)RESEND_API_KEY=NOTIFICATION_EMAIL=SANITY_API_TOKEN=
# Analytics (Optional)PUBLIC_GA_MEASUREMENT_ID=
# Error Tracking (Optional)SENTRY_DSN=SENTRY_ENVIRONMENT=Team members copy to .env and fill in values.
Security Best Practices
Section titled “Security Best Practices”1. Never Commit Secrets
Section titled “1. Never Commit Secrets”Add to .gitignore:
.env.env.local.env.production✅ Commit .env.template (no values)
2. Rotate Credentials
Section titled “2. Rotate Credentials”Rotate API keys periodically:
Every 90 days:- Regenerate Sanity token- Regenerate Resend API key- Regenerate ConvertKit API key- Update in Cloudflare dashboard3. Use Least Privilege
Section titled “3. Use Least Privilege”Sanity token permissions:
✅ Editor (read + write contributions)❌ Admin (unnecessary)4. Separate Dev and Production
Section titled “4. Separate Dev and Production”Development dataset: "development"Production dataset: "production"
Development API keys: Test keysProduction API keys: Live keys5. Monitor Access
Section titled “5. Monitor Access”Check Sanity audit logs:
- Dashboard → Project → Activity
- Review API token usage
- Revoke compromised tokens
Validation
Section titled “Validation”Check Required Variables
Section titled “Check Required Variables”// In API routeimport { getRequiredEnv } from '@rejected-media/podcast-framework-core';
export const POST: APIRoute = async (context) => { try { const env = getRequiredEnv([ 'PUBLIC_SANITY_PROJECT_ID', 'RESEND_API_KEY' ], context);
// All variables guaranteed to exist const { PUBLIC_SANITY_PROJECT_ID, RESEND_API_KEY } = env;
} catch (error) { // Missing variables - returns error return new Response('Configuration error', { status: 500 }); }};Runtime Checks
Section titled “Runtime Checks”// Check if feature is configuredconst hasNewsletter = !!getEnv('CONVERTKIT_API_KEY', context);const hasContributions = !!getEnv('RESEND_API_KEY', context);const hasAnalytics = !!import.meta.env.PUBLIC_GA_MEASUREMENT_ID;
// Conditionally enable features{hasNewsletter && <NewsletterSignup />}{hasContributions && <ContributeLink />}Troubleshooting
Section titled “Troubleshooting””Missing required environment variables”
Section titled “”Missing required environment variables””Check 1: Variables are set in platform dashboard
Check 2: Variable names match exactly (case-sensitive)
Check 3: Rebuild after adding variables
- Cloudflare: Deployments → Retry deployment
Variables not accessible in API routes
Section titled “Variables not accessible in API routes”Check using hosting adapter:
// ✅ Platform-agnostic (works everywhere)import { getEnv } from '@rejected-media/podcast-framework-core';const apiKey = getEnv('API_KEY', context);
// ❌ Platform-specific (breaks on Cloudflare)const apiKey = process.env.API_KEY;Different values in preview vs production
Section titled “Different values in preview vs production”Check environment selection:
- Variables can be different for Production vs Preview
- Verify correct environment is selected when adding
Variable updates not reflecting
Section titled “Variable updates not reflecting”Trigger new build:
# Push empty commit to trigger rebuildgit commit --allow-empty -m "Trigger rebuild"git pushRelated
Section titled “Related”- Cloudflare Pages - Cloudflare deployment
- Netlify - Netlify deployment
- Configuration - Local configuration
Next Steps
Section titled “Next Steps”- Custom Domains - Set up custom domain
- Cloudflare Pages - Deploy to Cloudflare
- Hosting Adapter - Platform abstraction