Skip to content

dileeparanawake/littlesteps-ai

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

171 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

littlesteps-ai

GitHub Repo

AI guidance for new parents - a full-stack, auth-gated LLM chat, built with Next.js 15, PostgreSQL/Drizzle, React Query, Vitest, Docker, OpenAI. Shipped in tagged minimum viable slices (MVS). What’s an MVS? (Blog).

Keywords: Next.js, React, TypeScript, PostgreSQL, Drizzle ORM, Docker, React Query, Zod, BetterAuth.js, Google OAuth, OpenAI API, Vitest, Full-stack development, API design, Authentication, Authorization, Database design, Testing, Containerisation, REST APIs


Demo

LittleSteps AI Demo MVS3 Complete

Skills demonstrated

  • Built a full-stack, auth-gated chat app using Next.js, React, TypeScript, and React Query.
  • Modelled and persisted data in PostgreSQL with Drizzle ORM and SQL migrations, containerised via Docker Compose.
  • Implemented Google OAuth 2.0 authentication using BetterAuth with session-protected endpoints.
  • Validated and sanitised inputs using Zod; followed RESTful API conventions for predictable error handling.
  • Integrated OpenAI's API for prompt–response handling on the server.
  • Wrote DB-backed tests with Vitest + Docker Postgres to verify core actions and data integrity.
  • Deployed to Fly.io with Neon managed PostgreSQL, HTTPS, secrets management, and production-ready configuration.
  • Implemented access controls and usage limits around AI usage, using an adapter pattern for the AI provider and GitHub Actions for automated deployment.
  • Implemented GDPR compliance with a privacy policy page, automated inactive-user cleanup via GitHub Actions OIDC, and cascade deletes for data retention.

Features by Minimum Viable Slice

Each slice represents a tagged, working release — from basic prompt handling to full auth-gated history.

MVS5 — GDPR Compliance & Data Retention

Goal: Make LittleSteps GDPR-compliant with transparent privacy practices and automated data retention.

Key features

  • Privacy policy page (/privacy) with footer and sign-in modal links.
  • Automated inactive-user deletion — monthly GitHub Actions workflow deletes accounts inactive for 90+ days.
  • OIDC-secured admin endpoint (/api/admin/cleanup) authenticated via GitHub Actions OIDC tokens.
  • Verification table cleanup decoupled from user deletion for independent lifecycle management.
  • Admin users excluded from auto-deletion; cascade deletes handle all related data.
MVS4 — Production Deployment & Access Controls

Goal: Deploy LittleSteps to Fly.io as a production-ready application with safety guardrails and access controls.

Key features

  • Deployed to Fly.io with Neon managed PostgreSQL, HTTPS, and secrets management.
  • RBAC on routes for initial release (admin-only access to chat APIs).
  • Usage limits enforced for non-admin users.
  • AI provider adapter with prompt caching (threadId-based).
  • UI improvements: safety banner, disclaimer, Open Graph image, mobile-responsive layout.
  • Automated CD via GitHub Actions for production deployment.
MVS3 — Prompt History & Persistence

Goal: Persist chat threads and messages so logged-in users can revisit their prompt history.

Key features

  • PostgreSQL + Drizzle schema for thread / message (UUIDs, ordered sequence).
  • Auth-gated APIs (/api/chat, /api/threads) enforcing ownership.
  • Rename & delete threads (≤60 chars, Zod validation, cascade delete).
  • React Query caching / invalidation keeps sidebar and thread lists synced.
  • DB-backed tests (Vitest + Docker Postgres) verify CRUD and ordering.
MVS2 — User Authentication (Google OAuth)

Goal: Secure app access with Google OAuth via BetterAuth.js.

Key features

  • Google sign-in modal using BetterAuth.js (HttpOnly sessions).
  • Auth-gated routes and session-aware UI state.
  • Early DB prototype used SQLite (migrated to Postgres in MVS3).
MVS1 — Prompt Interface (No Auth)

Goal: Provide a simple OpenAI-powered prompt/response interface.

Key features

  • Basic chat UI with secure API route to OpenAI.
  • Docker Compose setup for containerised local development.
  • Established initial minimum viable slice and project structure.

Tech Stack

  • UI / App: Next.js (App Router), React, TypeScript
  • Data layer: React Query (@tanstack/react-query)
  • Database / ORM: PostgreSQL (Docker, Neon) + Drizzle ORM (migrations)
  • Auth: BetterAuth.js (Google OAuth, HttpOnly sessions)
  • Validation: Zod
  • LLM: OpenAI API (SDK)
  • Testing: Vitest (DB-backed tests)
  • Dev / Infra: Node 20 (Volta), Docker & Docker Compose, Fly.io (production), GitHub Actions (CD)

Note: SQLite was used in MVS2 only; MVS3 migrated to PostgreSQL.

Data Model

A small, explicit schema that prioritises ownership, deterministic ordering, and clean deletes.

Data model (expand)
User (id TEXT PK, email UNIQUE, name, createdAt, updatedAt)
 ├─ Session (id TEXT PK, token UNIQUE, expiresAt, userId FK → User.id ON DELETE CASCADE)
 ├─ Account (id TEXT PK, accountId, providerId, userId FK → User.id ON DELETE CASCADE)
 └─ Thread (id UUID PK, userId FK → User.id ON DELETE CASCADE, title VARCHAR(60), createdAt, updatedAt)
     └─ Message (id UUID PK, threadId FK → Thread.id ON DELETE CASCADE,
                 sequence INT, role ENUM[system|user|assistant], content TEXT,
                 createdAt, promptTokens?, completionTokens?, totalTokens?,
                 UNIQUE(threadId, sequence))

Key decisions (what & why)

  • UUIDs for Thread/Message → non-guessable, safe in URLs, simpler client routing.
  • Deterministic orderingUNIQUE(thread_id, sequence) ensures stable message order without relying on timestamps.
  • Ownership enforcementthread.user_id FK + API session checks (BetterAuth) guarantee users can only access their own threads.
  • Clean deletesON DELETE CASCADE on FKs automatically removes dependent messages.
  • Tight title constraintVARCHAR(60) prevents UI overflow and keeps naming consistent.
  • Role enum (system | user | assistant) → validates message type at the DB layer.
  • Token fields (promptTokens, completionTokens, totalTokens) → reserved for future analytics and rate-limiting.

Migration & testing workflow

  • Schema changes are managed with Drizzle migrations.
  • Tests are DB-backed (Vitest + Docker Postgres): create thread → append messages → fetch ordered → assert content & order.

API quick reference

API reference (expand)

Authentication required for all endpoints (BetterAuth session). Usage limits are currently applied to authenticated non-admin users only.

POST /api/chat?threadId=UUID (optional)

  • body: { "prompt": string }
  • 200: { "threadID": UUID }
  • 400 invalid body | 401 unauthenticated | 500 error
  • Behavior: appends user prompt, calls OpenAI, appends assistant reply; creates a thread if none provided.

GET /api/chat?threadId=UUID

  • 200: Message[] ordered by sequence
  • 400 missing threadId | 401 unauthenticated | 403 not owner

GET /api/threads

  • 200: Thread[] for the session user

PATCH /api/threads

  • body: { "threadId": UUID, "title": string<=60 }
  • 200: Thread | 400 invalid | 401 unauth | 403 forbidden

DELETE /api/threads

  • body: { "threadId": UUID }
  • 200: { "success": true } | 401 unauth | 403 forbidden | 404 not found

Project structure

Project structure (expand)
┌─ src/app/ (Next.js App Router)
│  ├─ page.tsx (landing)
│  ├─ chat/[threadId]/page.tsx (thread view)
│  └─ api/
│     ├─ auth/[...all]/route.ts (BetterAuth handlers)
│     ├─ chat/route.ts (POST: send msg, GET: fetch msgs)
│     └─ threads/route.ts (GET: list, PATCH: rename, DELETE)
│
├─ src/components/
│  ├─ chat/
│  │  ├─ ChatSidebar/ (thread list, rename, delete)
│  │  └─ ChatThread/ (message list, input, submit)
│  ├─ sign-in/ (Google OAuth modal)
│  └─ layout/Header.tsx (nav, session UI)
│
└─ src/lib/
   ├─ auth.ts (BetterAuth config)
   ├─ chat/ (DB operations: create, read, update, delete)
   └─ db/ (Drizzle schema, migrations)

Data flow: User → React components → API routes → Drizzle ORM → PostgreSQL

Architecture at a glance

  • App: Next.js App Router; server routes for APIs, client components for UI.
  • Auth/Data: BetterAuth.js + Drizzle ORM → Postgres (UUID, sequence ordering).
  • Client state: React Query cache; invalidation on rename/delete/post.
  • Deployment: Dockerised Next.js app on Fly.io, connected to Neon managed PostgreSQL, deployed via GitHub Actions.

Tests

pnpm test

DB-backed (Vitest + Docker Postgres): create/read/update/delete flows for threads/messages.

Local Development

See full guide: public/docs/local-dev-guide.md

Quick start:

docker compose up -d
pnpm migrate

Releases

  • 2026-02-24 — MVS5: GDPR compliance & data retention — Tag
  • 2025-12-26 — MVS4: Production deployment & access controls — Tag
  • 2025-10-21 — MVS3: Prompt history & persistence — Tag
  • 2025-07-27 — MVS2: User Authentication — Tag
  • 2025-07-20 — MVS1: Prompt Interface — Tag

License

This project is licensed under the MIT License.