CASE STUDY 07 · SaaS
NexusLMS
A production-grade LMS with an AI tutor wired into the content model.

Learning Management System with a RAG-backed AI tutor, smart assessments with 8 question types, a block-based course builder, and enterprise multi-tenancy — 110+ tables, 38 API modules.
110+
database tables
38
API modules
8
assessment question types
RLS
database-enforced multi-tenancy
THE PROBLEM
LMS products split into two camps: legacy enterprise systems (deep but hostile to use) and modern course-sellers (pleasant but shallow — no real assessment engine, no tenancy, no compliance surface).
Bolting a chatbot onto either camp produces a toy. An AI tutor is only useful if it's grounded in the actual course content and aware of where the learner is.
WHAT I BUILT
A full LMS: block-based course builder (text, video, embeds, assessments composed like documents), an assessment engine with 8 question types and configurable grading, learner progress tracking, and enterprise multi-tenancy with row-level security.
The AI tutor is RAG over the tenant's own course corpus: it answers with citations into the material, scoped to what the learner is enrolled in — a tutor that has actually read the course, not a general chatbot in a sidebar.
ARCHITECTURE
Next.js 15 (learner + admin apps)
│
▼
NestJS API ── 38 modules
├─ course builder (block model)
├─ assessment engine (8 question types)
├─ AI tutor ── RAG over tenant course corpus
│ (embeddings + citation-grounded answers)
├─ custom JWT auth (multi-tenant claims)
└─ Drizzle ORM ──► PostgreSQL
110+ tables · RLS per tenant- Multi-tenancy runs on Postgres row-level security — the database enforces isolation even if application code slips, which is the only tenancy story worth telling enterprises.
- The tutor's RAG index is per-tenant and per-course: embeddings are scoped so answers can cite the learner's actual material and never leak across tenants.
- 38 NestJS modules keep a 110-table domain navigable — each module owns its slice of schema, DTOs, and permissions.
STACK — AND WHY
Next.js 15
Learner and admin frontends with server components for content-heavy pages.
NestJS
Module system that keeps a very large API surface organized and testable.
PostgreSQL + RLS
Database-enforced tenant isolation; 110+ tables of relational course/assessment/progress structure.
Drizzle ORM
Typed schema shared across 38 modules — refactoring a 110-table domain without types would be suicide.
THE HARD PARTS
RAG that cites, scoped to tenancy
The tutor must answer from the tenant's courses only, with citations a learner can click. That means per-tenant embedding namespaces, retrieval filtered by enrollment, and answer generation constrained to retrieved chunks — hallucinated confidence is worse than 'I don't know' in an education product.
An assessment engine that survives real teachers
Eight question types × partial credit × retakes × time limits × question banks with randomization — the combinatorics of assessment configuration is where LMS complexity actually lives. The engine models attempts as immutable event streams so regrades and disputes are replayable.
110+ tables without drowning
The schema is the product. Keeping it navigable required strict module ownership (no cross-module table writes), typed queries end-to-end, and migrations as first-class reviewed artifacts.
WHAT IT TAUGHT ME
- Row-level security moves tenancy from 'trust my code' to 'trust Postgres' — worth the query-planning care it costs.
- In education, the AI feature's ceiling is your content model: RAG is only as good as the block structure it retrieves over.
- Immutable attempt events made every 'my grade is wrong' conversation answerable with a replay instead of an apology.