Skip to content
Return to Projects
Case Study

Payday Chat

A real-time, members-only network for online business founders.

The Problem

Founders needed a private, high-signal place to network, build teams, and scale revenue — not another noisy public feed. Payday Chat is invite-only and built around real-time collaboration.

My Role

Built solo with AI-assisted development — Flutter client, NestJS API, data model, real-time layer, and deployment.

Highlights

  • Real-time messaging with presence and live events over WebSockets
  • One NestJS API powering both a Flutter mobile app and a web client
  • PostgreSQL domain model with Redis for caching and pub/sub fan-out
  • NGINX gateway, Stripe payments, and OAuth authentication

Stack

FlutterDartNestJSPostgreSQLRedisNGINXStripe

Constraints

  • Invite-only product — auth and access control had to be correct from day one.
  • One backend, two clients (Flutter + web) — schema and contracts had to be shared, not duplicated.
  • Realtime is a feature, not a nice-to-have — presence and live events must survive reconnects.

System Architecture

Clients
Flutter Mobile App
Web App
Gateway
NGINX
API
NestJS REST
WebSocket Gateway
Data
PostgreSQL
Redis (cache + pub/sub)
External
Stripe
OAuth

Key Trade-offs

The decisions worth defending — what I chose, what I turned down, and why.

Realtime transport

Chose

WebSockets with Redis pub/sub fan-out

Rejected

Long-polling or third-party realtime SaaS

Predictable latency, no per-message vendor cost, and Redis already in the stack for caching — one fewer moving part.

Mobile client framework

Chose

Flutter (single codebase for iOS + Android)

Rejected

Native Swift + Kotlin clients

Solo build — two native clients would have doubled the surface area and slowed iteration on the API.

API style

Chose

REST + dedicated WebSocket gateway

Rejected

GraphQL subscriptions

Simpler operational story, easier to cache at the NGINX layer, and the realtime channel stays an explicit, observable component.

What I'd Do Differently

An honest retrospective — the stuff I'd change with more time, more users, or a second pass.

  1. 1Introduce contract tests between the NestJS API and the Flutter client earlier — a few breakages were caught only at runtime.
  2. 2Move long-lived sockets to a dedicated process so API deploys don't drop client connections.
  3. 3Add structured event versioning from day one instead of retrofitting it once the schema started moving.

Technical Deep-Dive

Architecture, specifications, and implementation details.

Early User Testing Deployment Guide

This guide provides a simplified, step-by-step process to deploy the Payday Chat application for a small group of early testers.


##Prerequisites

  1. A Domain Name: You need a domain (e.g., your-app.com). You can get one from providers like Namecheap, GoDaddy, or Google Domains.
  2. A Linux Server (VPS): A basic virtual private server is required to host the backend and web app.
    • Recommended: A server with at least 2 vCPU, 4 GB RAM, and 40 GB SSD.
    • Providers: Hetzner, DigitalOcean, Vultr, or AWS EC2.

##Step 1: Point Your Domain to the Server

In your domain provider's DNS settings, create an A record that points your domain to your server's public IP address.

  • Host: @ (or your domain name)
  • Value: YOUR_SERVER_IP
  • TTL: 3600 (or 1 hour)

Example: your-app.com -> 123.45.67.89


##Step 2: Set Up the Server

SSH into your new server and run the following commands to install Docker and prepare the environment.

# SSH into your server as root
ssh root@YOUR_SERVER_IP

# Update system packages
apt update && apt upgrade -y

# Install Docker and Docker Compose
curl -fsSL https://get.docker.com | sh
apt install docker-compose-plugin -y

# Clone your project repository
git clone <your-git-repo-url> /app
cd /app

##Step 3: Configure and Launch the Backend

The backend is configured using an environment file.

  1. Create the Production Environment File: Create a file named .env.production in the server/ directory:

    nano server/.env.production
    
  2. Add Production Configuration: Copy and paste the following template into the file. You must replace the placeholder values with your actual domain, database credentials, and other secrets.

    # App
    NODE_ENV=production
    APP_URL=https://your-app.com
    PORT=3000
    
    # Security
    JWT_SECRET=replace_with_a_long_random_string
    JWT_REFRESH_SECRET=replace_with_another_long_random_string
    ALLOWED_ORIGINS=https://your-app.com
    
    # Database (use strong, random passwords)
    DB_HOST=postgres
    DB_PORT=5432
    DB_USERNAME=payday
    DB_PASSWORD=replace_with_strong_db_password
    DB_NAME=payday
    DB_LOGGING=false
    
    # Redis
    REDIS_HOST=redis
    REDIS_PORT=6379
    
    # Email (using Resend as an example)
    MAIL_HOST=smtp.resend.com
    MAIL_PORT=465
    MAIL_USER=resend
    MAIL_PASSWORD=replace_with_your_resend_api_key
    MAIL_FROM="Payday App <noreply@your-app.com>"
    
    # Stripe (optional, can be added later)
    STRIPE_SECRET_KEY=
    STRIPE_WEBHOOK_SECRET=
    
    # Other
    SWAGGER_ENABLED=false
    
  3. Launch the Production Stack: Run the following command from the root of the project (/app) to build and start all services in production mode.

    docker compose -f docker-compose.yml -f docker-compose.prod.yml --env-file ./server/.env.production up -d --build
    

    Your backend and web application are now live at http://YOUR_DOMAIN/. (If you configure HTTPS on your server, use https://YOUR_DOMAIN/.)


##Step 4: Build and Share the Android App

  1. Build the Release APK (recommended: no code edits): In your local terminal, navigate to the mobile directory and run the build command.

    cd mobile
    flutter build apk --release --dart-define=GATEWAY_URL=http://YOUR_DOMAIN
    

    If your server uses HTTPS:

    flutter build apk --release --dart-define=GATEWAY_URL=http://YOUR_DOMAIN
    
  2. Upload the APK to your server (recommended): Copy the generated APK into the releases/ folder on the server.

    Example:

    scp mobile/build/app/outputs/apk/release/app-release.apk \
      your_user@YOUR_SERVER_IP:/app/releases/app-release.apk
    
  3. Public download link (the one you paste in your bio-site): Once deployed, the APK will be available at: http://YOUR_DOMAIN/download/app-release.apk (or https://YOUR_DOMAIN/download/app-release.apk if your server has HTTPS).

  4. Share with testers:

    • Website: http://YOUR_DOMAIN/ (or https://YOUR_DOMAIN/ with HTTPS)
    • Android download: http://YOUR_DOMAIN/download/app-release.apk
    • Important: Testers must enable "Install from unknown sources" to install the APK.

##Summary for Your Testers

Give testers these two links:

  • Website: http://YOUR_DOMAIN/ (or https://YOUR_DOMAIN/ with HTTPS)
  • Android APK: http://YOUR_DOMAIN/download/app-release.apk

They can create accounts on either platform and the data will be synchronized.

~ End of Document ~