MedQ
A medical education platform for Tunisian medical students — AI-validated question banks, per-subject analytics and a collaborative study layer.
- Role
- Software Engineer
- Status
- shipped
Problem
Externes en medecine in Tunisia revise from question banks that are static, unevenly written and impossible to work through as a cohort. MedQ turns the bank into a platform: a model validates each question and writes the explanation, progress is tracked by subject, course and question type, and a commenting layer lets a year group work the same material together.
Architecture
Read this diagram as text
- Next.js 15 client mounts Component library
- Next.js 15 client connects to Application API
- Application API connects to NextAuth.js
- Application API validate + explain Azure OpenAI
- Azure OpenAI progress SSE progress channel
- SSE progress channel streams Next.js 15 client
- Admin dashboard bulk import Application API
- Application API connects to PostgreSQL
- Application API checkout Konnect
- Vercel hosts Next.js 15 client
- Selenium suite covers Next.js 15 client
Engineering decisions
- Server-Sent EventsThe validation and explanation passes run long enough that progress has to be reported while they work, not handed over once at the end.
- KonnectA Tunisian gateway for Tunisian students — the payment methods they already hold, rather than the ones a foreign processor prefers.
- SeleniumDriven through a Page Object Model, so an interface change is one edit in one place instead of a rewrite across the suite.
Also in the stack, reasoning not written up yet:
Next.js · TypeScript · React · Prisma · PostgreSQL · Azure OpenAI · NextAuth.js · OAuth / JWT · Tailwind CSS · shadcn/ui · Vercel
The hard part
Bulk import is where it broke. An admin uploads a year of questions from Excel, each one goes to the model for validation and an explanation, and the browser is expected to sit there for minutes. The first version streamed progress over SSE and lost the connection halfway: the job carried on server-side, the page had no idea, and re-uploading produced a second copy of every question. The import became a job with an id and a resumable channel — the stream carries the state of a job rather than the state of a request, so a dropped connection reattaches to the same job and a re-upload finds the rows already there.
Result
Live for medical students across Tunisia. The bank is validated and explained by the model rather than by hand, students track performance by subject, course and question type, and comment threads sit against the questions themselves. Admins import in bulk from Excel and pay through Konnect.
