Tools
Tools: From Localhost to Production: Deploying an AI Blog Generator (FastAPI + React)
2026-01-25
0 views
admin
Welcome Back! ## Let's dive in! ## Deployment Strategy ## Why Railway for Backend? ## Why Vercel for Frontend? ## Architecture Overview ## Part 1: Deploying Backend to Railway ## Step 1: Preparing the Backend ## Creating railway.json ## Updating Production Dependencies ## Step 2: Setting Up Railway ## Creating the Project ## Adding PostgreSQL ## Step 3: Environment Variables ## Step 4: First Deployment Attempt ## Challenge #1: Import Errors ## Challenge #2: Database Tables Not Created ## Step 5: Generating a Domain ## Part 2: Deploying Frontend to Vercel ## Step 1: Preparing the Frontend ## Environment Configuration ## Vercel Configuration ## Step 2: Deploying to Vercel ## Import from GitHub ## Build Configuration ## Adding Environment Variable ## Challenge #3: CORS Errors ## Testing in Production ## First Blog Generation ## Performance Testing ## Challenges & Solutions Summary ## 1. Module Import Errors ## 2. Database Tables Not Created ## 3. CORS Policy Blocking Requests ## 4. Environment Variables Not Loading ## 5. Hugging Face API 503 Errors ## Production Optimizations ## Backend Optimizations ## Frontend Optimizations ## Cost Breakdown ## Railway (Backend + Database) ## Vercel (Frontend) ## Hugging Face ## Total Monthly Cost ## CI/CD Pipeline: Auto-Deployment ## How It Works ## Deployment Logs ## Monitoring & Maintenance ## Railway Monitoring ## Vercel Analytics ## Database Monitoring ## Lessons Learned ## 1. Start with Simple Deployment ## 2. Environment Variables Are Critical ## 3. CORS Will Get You ## 4. Database Initialization Matters ## 5. Logs Are Your Best Friend ## 6. Test Production Early ## 7. Free Tiers Are Powerful ## Future Improvements ## Immediate (This Week) ## Short-term (This Month) ## Long-term (This Quarter) ## Final Thoughts ## Resources ## Try It Yourself! ## π Thank You! ## Connect With Me ## What's Next? Part 2 of building an AI blog generator with FastAPI, React, Hugging Face, Railway, and Vercel. This post is for developers whoβve built full-stack apps locally and want to confidently ship them to production without overengineering. In Part 1, I built an AI-powered blog generator locally.
In this post, Iβll show how I deployed the full system to production using Railway and Vercelβcovering backend deployment, database setup, frontend integration, and the real issues I ran into along the way. In this post, I'll walk you through: Before jumping into deployment, I needed to make some key decisions. I chose Railway over alternatives like Heroku, AWS, or DigitalOcean because: Vercel was the obvious choice for the React frontend: Here's what we're building: Simple, scalable, and cost-effective. Before deployment, I needed to ensure the backend was production-ready. Railway uses this file to understand how to deploy our app: Made sure requirements.txt included production essentials: The process was surprisingly smooth: Railway immediately started building! This was the easiest database setup I've ever done: Railway automatically: No configuration files, no manual connection strings - just magic. Railway makes environment management clean and secure. I added these variables via the Railway dashboard: Pro tip: The DATABASE_URL was automatically set when I added PostgreSQL. No manual configuration needed! I pushed my code and watched the build logs: Railway detected the push and started building... My local environment had different import paths than production. I realized I needed to ensure all imports were relative to the backend directory. Updated app/main.py: Lesson learned: Always test imports from the project root! API worked, but generating blogs failed with: FastAPI wasn't creating tables on startup in production. Updated the lifespan context manager in main.py: Now tables are created automatically on every deployment! Railway provides a free subdomain: Got: https://ai-blog-generator-production-bd0c.up.railway.app Backend deployed successfully! Created .env.production: This tells the frontend where to find the backend in production. Created vercel.json for optimal deployment: The rewrites section ensures React Router works properly in production. Vercel auto-detected everything: Added the most important variable: Clicked "Deploy" and waited... First deployment: Success! Got URL: https://frontend-ochre-rho-71.vercel.app/ Opened my Vercel URL and... nothing worked. The browser console showed: The backend wasn't configured to accept requests from the Vercel domain. Added CORS_ORIGINS to Railway: Railway auto-redeployed the backend with new CORS settings. Hard refreshed the frontend (Ctrl+Shift+R) and...
Connected! The status indicator turned green! Held my breath and tried generating a blog: Clicked "Generate Blog Post"... I generated several blogs to test: Let me share all the issues I encountered and how I solved them: Solution:
Ensured all imports were absolute from the project root: Solution:
Added lifespan context manager to create tables on startup: Solution:
Added frontend URL to CORS_ORIGINS in Railway: Error:
Frontend couldn't connect to backend (showing localhost URL) Solution:
Ensured VITE_API_URL was set in Vercel's environment variables for all environments (Production, Preview, Development). Solution:
This is expected! Hugging Face models go to sleep after inactivity. My retry logic handles it: After the first request warms up the model, subsequent requests are fast. 1. Database Connection Pooling 2. Request Timeout Handling 3. Logging for Production Added proper logging to track issues: 1. Environment-based API URL Added proper error handling: Clear feedback during long operations: Let's talk money - one of the best parts! $0 - $5/month depending on Railway usage! For a production-ready, full-stack AI application, that's incredible value. The best part? Both platforms auto-deploy when I push to GitHub! Deployment time: 2-3 minutes for both! Railway and Vercel both provide detailed logs: Railway dashboard shows: Enabled Vercel Analytics (free!) to track: Checked PostgreSQL usage: Everything running smoothly! Here are the key takeaways from this deployment journey: Don't overcomplicate. Railway and Vercel handle 90% of DevOps for you. Spent hours debugging before realizing a typo in VITE_API_URL. Triple-check these! Always configure CORS properly. Test with actual frontend URL, not localhost. In production, tables won't magically appear. Handle database setup in application lifecycle. Both Railway and Vercel have excellent logging. Use them to debug issues. Deploy early, deploy often. Catching issues in production is better than finding them in interviews! You can build and deploy production apps for $0-5/month. No excuses! Now that we're in production, here's what I'm planning: Deploying this project taught me more than months of tutorials ever could. What I'd do differently: Most importantly: I now have a live, working application in my portfolio. Something I can show in interviews, share with friends, and actually use! Tools and platforms used: Documentation that helped: The complete code is open source! Feel free to: This project reinforced that deployment is where architectural decisions are validatedβand where many βworkingβ projects quietly fail Thanks for following along on this journey! Part 1 covered the development process.
Part 2 (this post) covered deployment. What did you learn? What would you do differently? Drop a comment below! If you found this helpful: I'd love to hear your thoughts and answer questions! I'm already working on v2.0 with exciting features: Stay tuned! Follow me to get notified when the next update drops. Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse CODE_BLOCK:
βββββββββββββββ HTTPS βββββββββββββββ
β ββββββββββββββββββΊβ β
β Vercel β β Railway β
β (Frontend) β β (Backend) β
β β β β
βββββββββββββββ ββββββββ¬βββββββ β β ββββββββΌβββββββ β PostgreSQL β β Database β βββββββββββββββ Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
βββββββββββββββ HTTPS βββββββββββββββ
β ββββββββββββββββββΊβ β
β Vercel β β Railway β
β (Frontend) β β (Backend) β
β β β β
βββββββββββββββ ββββββββ¬βββββββ β β ββββββββΌβββββββ β PostgreSQL β β Database β βββββββββββββββ CODE_BLOCK:
βββββββββββββββ HTTPS βββββββββββββββ
β ββββββββββββββββββΊβ β
β Vercel β β Railway β
β (Frontend) β β (Backend) β
β β β β
βββββββββββββββ ββββββββ¬βββββββ β β ββββββββΌβββββββ β PostgreSQL β β Database β βββββββββββββββ CODE_BLOCK:
{ "$schema": "https://railway.app/railway.schema.json", "build": { "builder": "NIXPACKS" }, "deploy": { "startCommand": "uvicorn app.main:app --host 0.0.0.0 --port $PORT", "restartPolicyType": "ON_FAILURE", "restartPolicyMaxRetries": 10 }
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
{ "$schema": "https://railway.app/railway.schema.json", "build": { "builder": "NIXPACKS" }, "deploy": { "startCommand": "uvicorn app.main:app --host 0.0.0.0 --port $PORT", "restartPolicyType": "ON_FAILURE", "restartPolicyMaxRetries": 10 }
} CODE_BLOCK:
{ "$schema": "https://railway.app/railway.schema.json", "build": { "builder": "NIXPACKS" }, "deploy": { "startCommand": "uvicorn app.main:app --host 0.0.0.0 --port $PORT", "restartPolicyType": "ON_FAILURE", "restartPolicyMaxRetries": 10 }
} COMMAND_BLOCK:
fastapi==0.104.1
uvicorn[standard]==0.24.0
sqlalchemy==2.0.23
psycopg2-binary==2.9.9 # PostgreSQL driver
pydantic==2.5.0
pydantic-settings==2.1.0
python-dotenv==1.0.0
requests==2.31.0
gunicorn==21.2.0 # Production server Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
fastapi==0.104.1
uvicorn[standard]==0.24.0
sqlalchemy==2.0.23
psycopg2-binary==2.9.9 # PostgreSQL driver
pydantic==2.5.0
pydantic-settings==2.1.0
python-dotenv==1.0.0
requests==2.31.0
gunicorn==21.2.0 # Production server COMMAND_BLOCK:
fastapi==0.104.1
uvicorn[standard]==0.24.0
sqlalchemy==2.0.23
psycopg2-binary==2.9.9 # PostgreSQL driver
pydantic==2.5.0
pydantic-settings==2.1.0
python-dotenv==1.0.0
requests==2.31.0
gunicorn==21.2.0 # Production server CODE_BLOCK:
Click "New" β "Database" β "PostgreSQL" Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
Click "New" β "Database" β "PostgreSQL" CODE_BLOCK:
Click "New" β "Database" β "PostgreSQL" CODE_BLOCK:
HUGGINGFACE_API_KEY=hf_xxxxxxxxxxxxx
HUGGINGFACE_MODEL=meta-llama/Llama-3.3-70B-Instruct
ENVIRONMENT=production
APP_NAME=AI Blog Generator API
VERSION=1.0.0 Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
HUGGINGFACE_API_KEY=hf_xxxxxxxxxxxxx
HUGGINGFACE_MODEL=meta-llama/Llama-3.3-70B-Instruct
ENVIRONMENT=production
APP_NAME=AI Blog Generator API
VERSION=1.0.0 CODE_BLOCK:
HUGGINGFACE_API_KEY=hf_xxxxxxxxxxxxx
HUGGINGFACE_MODEL=meta-llama/Llama-3.3-70B-Instruct
ENVIRONMENT=production
APP_NAME=AI Blog Generator API
VERSION=1.0.0 COMMAND_BLOCK:
git add .
git commit -m "Prepare for Railway deployment"
git push origin main Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
git add .
git commit -m "Prepare for Railway deployment"
git push origin main COMMAND_BLOCK:
git add .
git commit -m "Prepare for Railway deployment"
git push origin main CODE_BLOCK:
ModuleNotFoundError: No module named 'app.database' Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
ModuleNotFoundError: No module named 'app.database' CODE_BLOCK:
ModuleNotFoundError: No module named 'app.database' COMMAND_BLOCK:
# Before (worked locally)
from database import engine, Base # After (works everywhere)
from app.database import engine, Base Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# Before (worked locally)
from database import engine, Base # After (works everywhere)
from app.database import engine, Base COMMAND_BLOCK:
# Before (worked locally)
from database import engine, Base # After (works everywhere)
from app.database import engine, Base CODE_BLOCK:
relation "blog_posts" does not exist Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
relation "blog_posts" does not exist CODE_BLOCK:
relation "blog_posts" does not exist COMMAND_BLOCK:
from contextlib import asynccontextmanager @asynccontextmanager
async def lifespan(app: FastAPI): # Startup: Create tables print("Creating database tables...") Base.metadata.create_all(bind=engine) print("Tables created successfully!") yield # Shutdown print("Application shutting down") app = FastAPI(lifespan=lifespan) Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
from contextlib import asynccontextmanager @asynccontextmanager
async def lifespan(app: FastAPI): # Startup: Create tables print("Creating database tables...") Base.metadata.create_all(bind=engine) print("Tables created successfully!") yield # Shutdown print("Application shutting down") app = FastAPI(lifespan=lifespan) COMMAND_BLOCK:
from contextlib import asynccontextmanager @asynccontextmanager
async def lifespan(app: FastAPI): # Startup: Create tables print("Creating database tables...") Base.metadata.create_all(bind=engine) print("Tables created successfully!") yield # Shutdown print("Application shutting down") app = FastAPI(lifespan=lifespan) CODE_BLOCK:
Settings β Networking β Generate Domain Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
Settings β Networking β Generate Domain CODE_BLOCK:
Settings β Networking β Generate Domain COMMAND_BLOCK:
curl curl https://ai-blog-generator-production-bd0c.up.railway.app/health # Response:
{"status":"healthy","environment":"production"} Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
curl curl https://ai-blog-generator-production-bd0c.up.railway.app/health # Response:
{"status":"healthy","environment":"production"} COMMAND_BLOCK:
curl curl https://ai-blog-generator-production-bd0c.up.railway.app/health # Response:
{"status":"healthy","environment":"production"} CODE_BLOCK:
VITE_API_URL=https:/https://ai-blog-generator-production-bd0c.up.railway.app Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
VITE_API_URL=https:/https://ai-blog-generator-production-bd0c.up.railway.app CODE_BLOCK:
VITE_API_URL=https:/https://ai-blog-generator-production-bd0c.up.railway.app CODE_BLOCK:
{ "buildCommand": "npm run build", "outputDirectory": "dist", "framework": "vite", "rewrites": [ { "source": "/(.*)", "destination": "/index.html" } ]
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
{ "buildCommand": "npm run build", "outputDirectory": "dist", "framework": "vite", "rewrites": [ { "source": "/(.*)", "destination": "/index.html" } ]
} CODE_BLOCK:
{ "buildCommand": "npm run build", "outputDirectory": "dist", "framework": "vite", "rewrites": [ { "source": "/(.*)", "destination": "/index.html" } ]
} CODE_BLOCK:
Framework: Vite
Build Command: npm run build
Output Directory: dist
Install Command: npm install Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
Framework: Vite
Build Command: npm run build
Output Directory: dist
Install Command: npm install CODE_BLOCK:
Framework: Vite
Build Command: npm run build
Output Directory: dist
Install Command: npm install CODE_BLOCK:
VITE_API_URL = https://ai-blog-generator-production-bd0c.up.railway.app Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
VITE_API_URL = https://ai-blog-generator-production-bd0c.up.railway.app CODE_BLOCK:
VITE_API_URL = https://ai-blog-generator-production-bd0c.up.railway.app CODE_BLOCK:
Access to fetch at 'https://ai-blog-generator-production-bd0c.up.railway.app/api/v1/generate' from origin 'https://frontend-ochre-rho-71.vercel.app' has been blocked by CORS policy Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
Access to fetch at 'https://ai-blog-generator-production-bd0c.up.railway.app/api/v1/generate' from origin 'https://frontend-ochre-rho-71.vercel.app' has been blocked by CORS policy CODE_BLOCK:
Access to fetch at 'https://ai-blog-generator-production-bd0c.up.railway.app/api/v1/generate' from origin 'https://frontend-ochre-rho-71.vercel.app' has been blocked by CORS policy CODE_BLOCK:
CORS_ORIGINS=https://frontend-ochre-rho-71.vercel.app Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
CORS_ORIGINS=https://frontend-ochre-rho-71.vercel.app CODE_BLOCK:
CORS_ORIGINS=https://frontend-ochre-rho-71.vercel.app CODE_BLOCK:
Topic: The Future of Cloud Computing
Tone: Professional
Length: Medium
Keywords: cloud, scalability, innovation Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
Topic: The Future of Cloud Computing
Tone: Professional
Length: Medium
Keywords: cloud, scalability, innovation CODE_BLOCK:
Topic: The Future of Cloud Computing
Tone: Professional
Length: Medium
Keywords: cloud, scalability, innovation CODE_BLOCK:
Title: The Future of Cloud Computing: Embracing Innovation and Scalability Word Count: 1,247 words
SEO Score: 82 [Full formatted blog content displayed] Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
Title: The Future of Cloud Computing: Embracing Innovation and Scalability Word Count: 1,247 words
SEO Score: 82 [Full formatted blog content displayed] CODE_BLOCK:
Title: The Future of Cloud Computing: Embracing Innovation and Scalability Word Count: 1,247 words
SEO Score: 82 [Full formatted blog content displayed] CODE_BLOCK:
ModuleNotFoundError: No module named 'app.database' Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
ModuleNotFoundError: No module named 'app.database' CODE_BLOCK:
ModuleNotFoundError: No module named 'app.database' CODE_BLOCK:
from app.database import engine
from app.models.blog import BlogPost
from app.services.ai_service import hf_service Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
from app.database import engine
from app.models.blog import BlogPost
from app.services.ai_service import hf_service CODE_BLOCK:
from app.database import engine
from app.models.blog import BlogPost
from app.services.ai_service import hf_service CODE_BLOCK:
psycopg2.errors.UndefinedTable: relation "blog_posts" does not exist Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
psycopg2.errors.UndefinedTable: relation "blog_posts" does not exist CODE_BLOCK:
psycopg2.errors.UndefinedTable: relation "blog_posts" does not exist CODE_BLOCK:
@asynccontextmanager
async def lifespan(app: FastAPI): Base.metadata.create_all(bind=engine) yield app = FastAPI(lifespan=lifespan) Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
@asynccontextmanager
async def lifespan(app: FastAPI): Base.metadata.create_all(bind=engine) yield app = FastAPI(lifespan=lifespan) CODE_BLOCK:
@asynccontextmanager
async def lifespan(app: FastAPI): Base.metadata.create_all(bind=engine) yield app = FastAPI(lifespan=lifespan) CODE_BLOCK:
Access to fetch has been blocked by CORS policy Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
Access to fetch has been blocked by CORS policy CODE_BLOCK:
Access to fetch has been blocked by CORS policy CODE_BLOCK:
CORS_ORIGINS=https://ai-blog-generator.vercel.app Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
CORS_ORIGINS=https://ai-blog-generator.vercel.app CODE_BLOCK:
CORS_ORIGINS=https://ai-blog-generator.vercel.app CODE_BLOCK:
Model is currently loading Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
Model is currently loading CODE_BLOCK:
Model is currently loading COMMAND_BLOCK:
if response.status_code == 503: print("Model loading... waiting 30 seconds") time.sleep(30) continue # Retry Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
if response.status_code == 503: print("Model loading... waiting 30 seconds") time.sleep(30) continue # Retry COMMAND_BLOCK:
if response.status_code == 503: print("Model loading... waiting 30 seconds") time.sleep(30) continue # Retry COMMAND_BLOCK:
engine = create_engine( settings.DATABASE_URL, pool_pre_ping=True, # Verify connections pool_size=10, # Connection pool max_overflow=20 # Max additional connections
) Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
engine = create_engine( settings.DATABASE_URL, pool_pre_ping=True, # Verify connections pool_size=10, # Connection pool max_overflow=20 # Max additional connections
) COMMAND_BLOCK:
engine = create_engine( settings.DATABASE_URL, pool_pre_ping=True, # Verify connections pool_size=10, # Connection pool max_overflow=20 # Max additional connections
) COMMAND_BLOCK:
response = requests.post( self.api_url, headers=self.headers, json=payload, timeout=120 # 2 minute timeout
) Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
response = requests.post( self.api_url, headers=self.headers, json=payload, timeout=120 # 2 minute timeout
) COMMAND_BLOCK:
response = requests.post( self.api_url, headers=self.headers, json=payload, timeout=120 # 2 minute timeout
) COMMAND_BLOCK:
import logging logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__) # In routes
logger.info(f"Generating blog for topic: {request.topic}")
logger.error(f"Generation failed: {str(e)}") Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
import logging logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__) # In routes
logger.info(f"Generating blog for topic: {request.topic}")
logger.error(f"Generation failed: {str(e)}") COMMAND_BLOCK:
import logging logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__) # In routes
logger.info(f"Generating blog for topic: {request.topic}")
logger.error(f"Generation failed: {str(e)}") CODE_BLOCK:
const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000'; Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000'; CODE_BLOCK:
const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000'; CODE_BLOCK:
try { const result = await blogAPI.generateBlog(formData); setCurrentBlog(result); setSuccess(true);
} catch (err) { const errorMessage = err.response?.data?.detail || 'Failed to generate blog'; setError(errorMessage);
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
try { const result = await blogAPI.generateBlog(formData); setCurrentBlog(result); setSuccess(true);
} catch (err) { const errorMessage = err.response?.data?.detail || 'Failed to generate blog'; setError(errorMessage);
} CODE_BLOCK:
try { const result = await blogAPI.generateBlog(formData); setCurrentBlog(result); setSuccess(true);
} catch (err) { const errorMessage = err.response?.data?.detail || 'Failed to generate blog'; setError(errorMessage);
} CODE_BLOCK:
{isGenerating ? ( <> Generating Content... </>
) : ( <> Generate Blog Post </>
)} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
{isGenerating ? ( <> Generating Content... </>
) : ( <> Generate Blog Post </>
)} CODE_BLOCK:
{isGenerating ? ( <> Generating Content... </>
) : ( <> Generate Blog Post </>
)} COMMAND_BLOCK:
# Make changes
git add .
git commit -m "Add new feature"
git push origin main # Automatically:
# 1. Railway detects push
# 2. Builds backend
# 3. Runs tests
# 4. Deploys new version
# 5. Zero downtime! # Similarly:
# 1. Vercel detects push
# 2. Builds frontend
# 3. Deploys to CDN
# 4. Instantly live! Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# Make changes
git add .
git commit -m "Add new feature"
git push origin main # Automatically:
# 1. Railway detects push
# 2. Builds backend
# 3. Runs tests
# 4. Deploys new version
# 5. Zero downtime! # Similarly:
# 1. Vercel detects push
# 2. Builds frontend
# 3. Deploys to CDN
# 4. Instantly live! COMMAND_BLOCK:
# Make changes
git add .
git commit -m "Add new feature"
git push origin main # Automatically:
# 1. Railway detects push
# 2. Builds backend
# 3. Runs tests
# 4. Deploys new version
# 5. Zero downtime! # Similarly:
# 1. Vercel detects push
# 2. Builds frontend
# 3. Deploys to CDN
# 4. Instantly live! CODE_BLOCK:
Building...
ββ Installing dependencies
ββ Building application ββ Starting server β Deployment successful
β Health check passed Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
Building...
ββ Installing dependencies
ββ Building application ββ Starting server β Deployment successful
β Health check passed CODE_BLOCK:
Building...
ββ Installing dependencies
ββ Building application ββ Starting server β Deployment successful
β Health check passed CODE_BLOCK:
Building...
ββ Installing dependencies
ββ Running build
ββ Optimizing assets
ββ Uploading to CDN β Build completed
β Deployment live Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
Building...
ββ Installing dependencies
ββ Running build
ββ Optimizing assets
ββ Uploading to CDN β Build completed
β Deployment live CODE_BLOCK:
Building...
ββ Installing dependencies
ββ Running build
ββ Optimizing assets
ββ Uploading to CDN β Build completed
β Deployment live - Deploying the FastAPI backend to Railway
- Setting up PostgreSQL in production
- Deploying the React frontend to Vercel
- Connecting everything together
- The challenges I faced and how I solved them - PostgreSQL Integration - One-click database setup
- GitHub Integration - Automatic deployments on push
- Developer Experience - Clean UI, clear logs, easy configuration
- Pricing - $5/month hobby plan is perfect for side projects
- Zero Config - Railway auto-detects Python and handles everything - Built for React/Vite - Optimized for frontend frameworks
- Global CDN - Lightning-fast content delivery
- Automatic HTTPS - SSL certificates out of the box
- Preview Deployments - Every PR gets its own URL
- Free Tier - Perfect for personal projects - NIXPACKS auto-detects Python and installs dependencies
- --host 0.0.0.0 allows external connections
- $PORT uses Railway's dynamic port assignment
- Restart policy handles crashes gracefully - Went to railway.app
- Clicked "Start a New Project"
- Selected "Deploy from GitHub repo"
- Chose my ai-blog-generator repository
- Important: Selected backend as the root directory - Provisioned a PostgreSQL instance
- Created the DATABASE_URL environment variable
- Connected it to my backend service - Logged into vercel.com
- Clicked "New Project"
- Selected my repository
- Critical: Set root directory to frontend - β
AI generation
- β
Database storage
- β
SEO scoring
- β
Copy to clipboard
- β
Download as Markdown
- β
Blog history - API health check: ~50ms
- Blog generation: 25-35 seconds (AI processing)
- Blog retrieval: ~100ms
- Blog list: ~150ms - Tables created automatically β
- Blogs saving correctly β
- Queries performing well β
- Includes backend hosting
- PostgreSQL database (1GB storage)
- Automatic backups
- 99.9% uptime SLA - $5 credit/month for free accounts
- Perfect for side projects and portfolios - Unlimited personal projects
- 100GB bandwidth/month
- Automatic HTTPS
- Custom domains - Inference API included
- Rate limits apply (but generous)
- Models sleep after inactivity
- Perfect for demos and low-traffic apps - CPU Usage: ~5-10% average
- Memory: ~150MB
- Response Times: 50-100ms (excluding AI)
- Uptime: 99.9% - User locations
- Performance metrics
- Core Web Vitals - Storage: ~5MB (100 blog posts)
- Connections: 2-3 active
- Queries: Fast (<100ms) - [ ] Add rate limiting to prevent abuse
- [ ] Set up error monitoring (Sentry)
- [ ] Add database backup automation
- [ ] Create health check monitoring - [ ] Add user authentication
- [ ] Implement blog favorites
- [ ] Add export to PDF
- [ ] Create API documentation site - [ ] Multi-language support
- [ ] Image generation for blogs
- [ ] Content scheduling
- [ ] Analytics dashboard
- [ ] WordPress integration - Railway's automatic PostgreSQL setup
- Vercel's instant deployments
- GitHub integration for CI/CD
- Simple, clean architecture - Set up monitoring from day one
- Add comprehensive error logging earlier
- Test with production environment variables locally
- Document deployment steps as I go - Hugging Face - Railway Docs
- Vercel Docs
- FastAPI Deployment - Star the repository
- Fork and customize
- Report issues
- Submit pull requests - β€οΈ Like this post
- π¬ Leave a comment
- π Share with others
- π Follow me for more content - π User authentication
- πΌοΈ AI-generated blog images
- π
Content scheduling
- π Analytics dashboard
how-totutorialguidedev.toaimlservernetworknetworkingrouterpostgresqlpythonssldatabasegit