Mentorship Platform.
An AI-powered mentorship matching and session management system. Built as a graduating project at Glasspaper Academy — the backend-heavy project that proved I can own a full system, not just the front end.
Problem
Mentor matching is typically done manually — spreadsheets, email threads, coordinator time. The goal was to automate initial matching using AI embeddings, then give both parties a structured space to manage sessions, notes, and goals.
Users: mentors (professionals offering time), mentees (students and career-changers), and admins (reviewing flagged content, managing cohorts). My role: sole developer, full ownership of architecture, backend, frontend, and deployment.
Architecture
Backend: ASP.NET Core API with JWT authentication, PostgreSQL via Supabase, pgvector for embedding storage, and Azure OpenAI for matching and session summarisation.
Frontend: Next.js App Router with TypeScript. Server Components for the dashboard shell, client components for real-time session updates. Supabase Realtime for live notification of new session bookings.
Deployment: API containerised with Docker, deployed to Azure Container Apps. Frontend on Vercel. GitHub Actions CI runs type-check, unit tests, and integration tests against a test database on every pull request.
Key decisions
ASP.NET Core for the API layer
The mentor–mentee matching logic required reliable typed models, middleware-level auth enforcement, and clear separation between domain logic and HTTP concerns. ASP.NET Core's controller pattern and middleware pipeline were the right tool — not an excuse to use C#, but a genuine fit for the complexity.
JWT auth with refresh token rotation
Sessions are stateless: a short-lived access token (15 min) plus a rotating refresh token stored in an HttpOnly cookie. On each refresh, the old token is invalidated and a new one issued. This limits the blast radius of a leaked access token without requiring a server-side session store.
PostgreSQL via Supabase
Supabase gives me a hosted Postgres instance, Row Level Security, and a generated TypeScript client for the frontend — without managing infrastructure. RLS policies enforce that mentors can only read their own sessions and mentees can only see their assigned mentor's profile.
AI matching via Azure OpenAI
Mentor–mentee compatibility is scored using embeddings: each profile is embedded on creation and stored in pgvector. At match time, the mentee's profile embedding is compared against all mentor embeddings using cosine similarity. Top-3 matches are returned with a reasoning string generated by the completion model.
Data modelling: separating identity from profile
Users (auth identity) are decoupled from Profiles (domain data). This lets me evolve the profile schema — add fields, versioning, soft-deletes — without touching the auth layer. A common mistake in early projects is to merge these, which creates migration pain later.
What I'd change
The embedding refresh strategy is naive — a full re-embed on every profile update. For a larger dataset this would be expensive. I'd move to an incremental update with a dirty flag and a background job.
The admin dashboard is the weakest part — I prioritised the matching engine and ran out of time for pagination, filtering, and bulk actions. Next iteration would start there.