7.1 KiB
7.1 KiB
API Key Management - Quick Reference
🎯 The Big Picture
Problem: You want to develop locally with convenience, but alpha testers should use their own API keys (so you don't pay for their usage).
Solution:
- Local Dev: API keys saved to
.envfiles (convenient) - Production: API keys saved to database per user (isolated, zero cost to you)
🚀 How It Works
1. Local Development (You)
# backend/.env
DEBUG=true
GEMINI_API_KEY=your_key_here
EXA_API_KEY=your_exa_key
COPILOTKIT_API_KEY=your_copilot_key
Behavior:
- ✅ Complete onboarding once
- ✅ Keys saved to
.envAND database - ✅ All services use keys from
.env - ✅ Convenient, keys persist
You pay for: Your own API usage
2. Production (Alpha Testers)
# Render environment variables
DEBUG=false
DEPLOY_ENV=render
DATABASE_URL=postgresql://...
Behavior:
- ✅ Each tester completes onboarding with their keys
- ✅ Keys saved to database (user-specific rows)
- ✅ Services fetch keys from database per user
- ✅ Complete user isolation
You pay for: $0-$7/month (infrastructure only)
Testers pay for: Their own API usage
📝 Code Examples
Using User API Keys in Services
from services.user_api_key_context import user_api_keys
import google.generativeai as genai
def generate_blog(user_id: str, topic: str):
# Get user-specific API keys
with user_api_keys(user_id) as keys:
gemini_key = keys.get('gemini')
# Configure Gemini with THIS user's key
genai.configure(api_key=gemini_key)
model = genai.GenerativeModel('gemini-pro')
# Generate content (charges THIS user's Gemini account)
response = model.generate_content(f"Write a blog about {topic}")
return response.text
What this does:
- Dev mode (
user_id=NoneorDEBUG=true): Uses.envfile - Prod mode (
DEPLOY_ENV=render): Fetches from database for thisuser_id
🔄 Migration Checklist
Step 1: Update Environment Variables
Local (backend/.env):
DEBUG=true
# Your development API keys (stay as-is)
GEMINI_API_KEY=...
EXA_API_KEY=...
Render Dashboard:
DEBUG=false
DEPLOY_ENV=render
DATABASE_URL=postgresql://...
# Remove GEMINI_API_KEY, EXA_API_KEY from here!
# Users will provide their own via onboarding
Step 2: Update Services to Use user_api_keys
Before:
import os
gemini_key = os.getenv('GEMINI_API_KEY') # ❌ Same for all users!
After:
from services.user_api_key_context import user_api_keys
with user_api_keys(user_id) as keys:
gemini_key = keys.get('gemini') # ✅ User-specific!
Step 3: Update FastAPI Endpoints
Add user_id parameter:
@router.post("/api/generate")
async def generate(
prompt: str,
current_user: dict = Depends(get_current_user) # Get authenticated user
):
user_id = current_user.get('user_id') # Extract user_id
# Pass user_id to service
result = await my_service.generate(user_id, prompt)
return result
Step 4: Test
Local:
- Complete onboarding
- Check
backend/.envhas your keys ✅ - Generate content - should work ✅
Production:
- Deploy to Render with
DEPLOY_ENV=render - User A: Complete onboarding with keys A
- User B: Complete onboarding with keys B
- User A generates content → Uses keys A ✅
- User B generates content → Uses keys B ✅
🔍 Troubleshooting
"No API key found" error
In development:
# Check backend/.env exists and has:
DEBUG=true
GEMINI_API_KEY=your_key_here
In production:
-- Check database has keys for this user:
SELECT s.user_id, k.provider, k.key
FROM api_keys k
JOIN onboarding_sessions s ON k.session_id = s.id
WHERE s.user_id = 'user_xxx';
Wrong user's keys being used
Cause: Service not using user_api_keys(user_id)
Fix:
# OLD (wrong):
gemini_key = os.getenv('GEMINI_API_KEY')
# NEW (correct):
with user_api_keys(user_id) as keys:
gemini_key = keys.get('gemini')
Keys not saving to .env in development
Cause: DEBUG not set to true
Fix:
# backend/.env
DEBUG=true # Must be explicitly true
📊 Cost Breakdown
Your Monthly Costs
| Item | Dev | Production |
|---|---|---|
| Infrastructure | $0 | $0-7/month |
| Database | Free | Free (Render) |
| API Usage (Gemini, Exa, etc.) | Your usage | $0 (users pay!) |
| Total | Your API usage | $0-7/month |
Alpha Tester Costs
| Item | Cost |
|---|---|
| ALwrity Subscription | Free (alpha) |
| Their Gemini API | Their usage |
| Their Exa API | Their usage |
| Total | Their API usage |
🎓 Key Concepts
Environment Detection
is_development = (
os.getenv('DEBUG', 'false').lower() == 'true' or
os.getenv('DEPLOY_ENV') is None
)
if is_development:
# Use .env file (convenience)
keys = load_from_env()
else:
# Use database (user isolation)
keys = load_from_database(user_id)
User Isolation
Database guarantees:
┌──────────────────┬─────────────┬──────────────────┐
│ user_id │ provider │ key │
├──────────────────┼─────────────┼──────────────────┤
│ user_tester_a │ gemini │ tester_a_key │ ← Isolated
│ user_tester_b │ gemini │ tester_b_key │ ← Isolated
└──────────────────┴─────────────┴──────────────────┘
Query for Tester A: WHERE user_id = 'user_tester_a'
Query for Tester B: WHERE user_id = 'user_tester_b'
No overlap, no conflicts!
🚀 Quick Start
For Local Development:
- Clone repo
- Set
DEBUG=trueinbackend/.env - Add your API keys to
backend/.env - Run backend:
python start_alwrity_backend.py --dev - Complete onboarding (keys auto-save to
.env) - Done! ✅
For Production Deployment:
- Deploy backend to Render
- Set environment variables:
DEBUG=falseDEPLOY_ENV=renderDATABASE_URL=postgresql://...
- Deploy frontend to Vercel
- Alpha testers complete onboarding with their keys
- Done! Each tester uses their own keys ✅
📚 Further Reading
✅ Summary
The magic:
- Same codebase works in both dev and prod
- Dev: Convenience of
.envfiles - Prod: Isolation via database
- Zero cost: Testers use their own API keys
- Automatic: Just set
DEBUGandDEPLOY_ENV
Bottom line:
Write code once, works everywhere. Development is convenient, production is isolated. You focus on building, testers pay for their usage. Win-win! 🎉