Kaernest Backend - Complete Implementation Guide¶
๐ฆ What You Got¶
A complete, production-ready backend with: - โ Magic link authentication (passwordless) - โ 90-day session cookies - โ PostgreSQL character storage (JSONB) - โ SendGrid email integration - โ Full CRUD API for characters - โ Auto-cleanup of expired tokens/sessions - โ CORS configured for your frontend - โ Comprehensive error handling
Total: 12 files, ~1,200 lines of code
๐ฏ Zero to Running in 30 Minutes¶
Step 1: Extract Files (2 min)¶
cd ~/projects # or wherever you develop
tar -xzf kaernest-backend.tar.gz
cd kaernest-backend
Step 2: Install & Configure (5 min)¶
# Run quick setup
chmod +x setup.sh
./setup.sh
# Edit .env file
nano .env
Required .env values:
DATABASE_URL=postgresql://your_user:your_pass@localhost:5432/kaernest_builder
FRONTEND_URL=https://velloriam.kaernest.com
SENDGRID_API_KEY=SG.your_actual_key_here
FROM_EMAIL=noreply@kaernest.com
Step 3: Database Setup (3 min)¶
# Create database
createdb kaernest_builder
# Or via psql:
psql -U postgres -c "CREATE DATABASE kaernest_builder;"
# Run migrations
npm run db:setup
Step 4: SendGrid Setup (10 min)¶
- Go to https://sendgrid.com โ Sign up (free)
- Settings โ API Keys โ Create API Key
- Copy key to
.envasSENDGRID_API_KEY - Settings โ Sender Authentication โ Verify single sender
- Use
noreply@kaernest.com(or your domain) - Check email and verify
Step 5: Test & Start (10 min)¶
# Test configuration
node scripts/test-setup.js
# Start development server
npm run dev
# In another terminal, test login:
curl -X POST http://localhost:3001/auth/request-magic-link \
-H "Content-Type: application/json" \
-d '{"email":"your@email.com"}'
# Check your email for magic link!
๐ Production Deployment Options¶
Option A: Same Server as Frontend (Recommended)¶
You already host velloriam.kaernest.com, so add the backend there:
1. Upload backend¶
# On your local machine
rsync -avz kaernest-backend/ user@your-server.com:~/kaernest-backend/
2. Install on server¶
# SSH into server
ssh user@your-server.com
cd ~/kaernest-backend
npm install --production
cp .env.example .env
nano .env # Configure for production
npm run db:setup
3. Configure Nginx¶
# Add to your nginx config
location /api/ {
proxy_pass http://localhost:3001/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
sudo nginx -t
sudo systemctl reload nginx
4. Run with PM2¶
npm install -g pm2
# Start
pm2 start server.js --name kaernest-api
# Save config
pm2 save
# Auto-start on reboot
pm2 startup
# Follow the instructions it prints
# Check logs
pm2 logs kaernest-api
Your API is now at: https://velloriam.kaernest.com/api/
Option B: Railway (Free Tier - Easier)¶
- Push to GitHub (if not already)
- Go to https://railway.app
- "New Project" โ "Deploy from GitHub"
- Select your repo
- Add PostgreSQL service
- Set environment variables in Railway dashboard
- Done! Auto-deploys on git push
Cost: Free for low traffic, ~$5/month if you exceed free tier
Option C: DigitalOcean App Platform¶
Similar to Railway, $5/month includes database.
๐ Frontend Integration¶
Add to your frontend:¶
1. Install fetch polyfill (if needed):
# Not needed for modern browsers, but just in case:
npm install whatwg-fetch
2. Create API client:
Create src/api/client.ts:
const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost:3001';
async function apiRequest(endpoint: string, options = {}) {
const response = await fetch(`${API_BASE}${endpoint}`, {
...options,
credentials: 'include', // Send cookies
headers: {
'Content-Type': 'application/json',
...options.headers,
},
});
if (!response.ok) {
const error = await response.json().catch(() => ({ error: 'Request failed' }));
throw new Error(error.error || 'Request failed');
}
return response.json();
}
export const api = {
// Auth
requestMagicLink: (email: string) =>
apiRequest('/auth/request-magic-link', {
method: 'POST',
body: JSON.stringify({ email }),
}),
checkAuth: () => apiRequest('/auth/status'),
logout: () => apiRequest('/auth/logout', { method: 'POST' }),
// Characters
listCharacters: () => apiRequest('/characters'),
getCharacter: (id: string) => apiRequest(`/characters/${id}`),
createCharacter: (data: CharacterDraft) =>
apiRequest('/characters', {
method: 'POST',
body: JSON.stringify({ data }),
}),
updateCharacter: (id: string, data: CharacterDraft) =>
apiRequest(`/characters/${id}`, {
method: 'PUT',
body: JSON.stringify({ data }),
}),
deleteCharacter: (id: string) =>
apiRequest(`/characters/${id}`, { method: 'DELETE' }),
};
3. Add environment variable:
.env.local:
VITE_API_URL=http://localhost:3001
.env.production:
VITE_API_URL=https://velloriam.kaernest.com/api
4. Use in components:
See FRONTEND_INTEGRATION.md for full React component examples.
๐ Database Schema¶
users
- id (uuid, primary key)
- email (unique)
- created_at
- last_login
magic_tokens
- token (primary key)
- user_id โ users.id
- expires_at
- created_at
sessions
- session_id (primary key)
- user_id โ users.id
- expires_at
- created_at
characters
- id (uuid, primary key)
- user_id โ users.id
- name
- data (jsonb) โ Full CharacterDraft
- created_at
- updated_at
All foreign keys have ON DELETE CASCADE - deleting a user deletes their data.
๐ Security Checklist¶
- No passwords stored
- Crypto-random tokens (32 bytes)
- Tokens expire in 15 minutes
- Tokens are one-time use
- HttpOnly cookies (JS can't access)
- Secure cookies in production
- CORS restricted to your domain
- Rate limiting recommended for production
For production, consider adding:
- Rate limiting on /auth/request-magic-link (5 requests/15min per IP)
- Honeypot field in login form
- CAPTCHA if abuse occurs
๐งช Testing¶
# Test configuration
node scripts/test-setup.js
# Test database
psql kaernest_builder -c "SELECT COUNT(*) FROM users;"
# Test email
curl -X POST http://localhost:3001/auth/request-magic-link \
-H "Content-Type: application/json" \
-d '{"email":"your@email.com"}'
# Test health
curl http://localhost:3001/health
๐ Monitoring¶
# PM2 monitoring
pm2 monit
# Database stats
psql kaernest_builder -c "
SELECT
(SELECT COUNT(*) FROM users) as users,
(SELECT COUNT(*) FROM characters) as characters,
(SELECT COUNT(*) FROM sessions WHERE expires_at > NOW()) as active_sessions;
"
# Disk usage
du -sh /var/lib/postgresql/data
๐ Common Issues¶
"Cannot connect to database"¶
# Check PostgreSQL is running
sudo service postgresql status
# Check connection
psql "postgresql://user:pass@localhost/kaernest_builder"
"SendGrid error"¶
- Verify API key is correct
- Verify sender email is verified in SendGrid
- Check quota (free = 100/day)
"CORS error"¶
FRONTEND_URLmust match exactly (including https://)- Check browser console for actual origin
"Session not persisting"¶
- Check
secure: trueonly in production - Verify cookies enabled
- Check browser dev tools โ Application โ Cookies
๐ Notes¶
- Cost: $0 (SendGrid free tier, self-hosted DB)
- Scale: Handles 1000s of users easily
- Maintenance: Almost none (auto-cleanup runs hourly)
- Data: Characters stored as JSONB (easy to query/update)
๐ You're Done!¶
You now have: - โ Passwordless auth - โ Character cloud save - โ 90-day sessions - โ Full API - โ Production-ready
Questions? Check README.md or open an issue!