Daily Milk Delivery Calculator - Multi-tenant SaaS on Railway
abhishekmitra-ait
FREEOP

21 days ago

What I Built: A full-stack Flask application that tracks daily milk deliveries with automated billing calculations. Started as a simple tracker, evolved into a multi-tenant SaaS with user authentication, OAuth integration, and personalized settings.

Live: https://web-production-01a84.up.railway.app/

The Journey:

v1.0 - MVP (SQLite + Basic CRUD)

  • Simple Flask app with local SQLite

  • Deployed using Railway's GitHub integration

  • Challenge: Database persistence across deployments

  • Solution: Discovered Railway Volumes for persistent storage

v2.0 - Production Database Migration (SQLite → PostgreSQL)

  • Hit SQLite's concurrency limits with multiple users

  • Migrated to Railway's PostgreSQL in one click

  • Challenge: Auto-increment sequences broke after data migration

  • Solution: Created custom migration script to reset PostgreSQL sequences

v3.0 - Multi-User with Authentication

  • Added OAuth (Google + GitHub) using Authlib

  • Implemented email verification with custom token system

  • Challenge: OAuth callbacks failing in production (http vs https)

  • Solution: Dynamic redirect URI fixing:

v4.0 - User-Specific Data Isolation

  • Added foreign keys linking records to users

  • Challenge: Existing data had no user association

  • Solution: Smart migration script that assigns orphaned records to primary user

v5.0 - Personalized Settings

  • Per-user currency (10+ currencies) and milk pricing

  • Auto-calculation with user-defined rates

  • Optional bulk recalculation of historical records

  • Challenge: Column additions without downtime

  • Solution: Auto-migration on startup:

Railway Features I Love:

  1. Zero-Config PostgreSQL: One-click database provisioning with automatic DATABASE_URL injection

  2. Environment Variables: Seamless secret management across dev/prod

  3. Volume Persistence: Used for backup storage before PostgreSQL migration

  4. Automatic HTTPS: No SSL certificate headaches

  5. GitHub Integration: Push to deploy - Railway handles the rest

Technical Highlights:

Smart Database Detection:

Automatic Schema Migrations: Instead of complex migration tools, implemented auto-detection and migration on startup - perfect for Railway's ephemeral deployments.

Session-Based Multi-Currency: Each user sees their own currency symbol throughout the app - achieved with user settings stored in PostgreSQL.

Architecture:

  • Backend: Flask + SQLAlchemy ORM

  • Database: Railway PostgreSQL with connection pooling

  • Auth: OAuth 2.0 (Google/GitHub) + custom email verification

  • Frontend: Jinja2 templates with responsive CSS

  • Deployment: Gunicorn WSGI server on Railway

What I Learned:

  1. Railway's DATABASE_URL uses postgres:// but SQLAlchemy expects postgresql:// - quick fix avoided hours of debugging

  2. PostgreSQL sequences need manual reset after bulk imports

  3. Railway's environment variable injection makes 12-factor apps trivial

  4. Connection pooling is essential for Railway's PostgreSQL (pool_pre_ping, pool_recycle)

  5. Auto-migrations on startup work better than separate migration scripts for small apps

Challenges Overcome:

  • 404 errors post-deployment: Fixed with proper external=True in urlfor()

  • OAuth state mismatch: Railway's HTTPS enforcement required callback URL fixes

  • Data migration: Custom scripts handled SQLite boolean (0/1) to PostgreSQL boolean conversion

What's Next:

  • CSV export functionality

  • REST API for mobile apps

  • Scheduled email reports using Railway Cron Jobs

Railway made deployment so smooth that I spent more time on features than DevOps. The PostgreSQL integration alone saved days of setup work. This project went from "weekend hack" to "production SaaS" thanks to Railway's simplicity!

Attachments

0 Replies

Loading...