AI-generated practice tests with a topic-level breakdown of your results.
Pick a subject (or type a specific topic), sit a timed multiple-choice paper, and get back an analysis that says which topics cost you marks — not just a score.
Built as a single Next.js app: the UI, the API and the database access all live here.
| Layer | Choice |
|---|---|
| Framework | Next.js 16 (App Router, React 19, Turbopack) |
| Language | TypeScript (strict) |
| Styling | Tailwind CSS v4, CSS-variable design tokens, light + dark themes via next-themes |
| Database | MongoDB + Mongoose 9 |
| Auth | JWT in an httpOnly cookie, signed with jose; passwords hashed with bcryptjs |
| AI | Google Gemini (gemini-3.6-flash) with a JSON response schema |
| Audio | ElevenLabs text-to-speech for analysis recommendations |
| Charts | Recharts for the accuracy trend; plain HTML bars for topic breakdowns |
| Validation | Zod on every API route |
- Node.js 20.9 or newer
- A MongoDB database — Atlas free tier or a local
mongod - A Google AI Studio API key — https://aistudio.google.com/apikey
- An ElevenLabs API key for optional audio recommendations — https://elevenlabs.io/
npm installcp .env.example .env.localThen fill in .env.local — see Environment variables below.
npm run devOpen http://localhost:3000, create an account, and generate your first test.
All of these go in .env.local (git-ignored). .env.example is the template.
| Variable | Required | What it is |
|---|---|---|
MONGODB_URI |
Yes | MongoDB connection string. Atlas: mongodb+srv://<user>:<pass>@<cluster>.mongodb.net/topiciq?retryWrites=true&w=majority. Local: mongodb://127.0.0.1:27017/topiciq |
JWT_SECRET |
Yes | Secret that signs the session cookie. At least 32 characters. Changing it signs everyone out. |
GEMINI_API_KEY |
Yes | Google AI Studio key used to generate questions. |
GEMINI_MODEL |
No | Overrides the model. Defaults to gemini-3.6-flash. |
ELEVENLABS_API_KEY |
No | Enables voice playback for topic recommendations on analysis pages. |
ELEVENLABS_MODEL_ID |
No | Overrides the ElevenLabs TTS model. Defaults to eleven_multilingual_v2. |
Generate a JWT_SECRET with:
node -e "console.log(require('crypto').randomBytes(48).toString('base64url'))"No NEXT_PUBLIC_* variables are needed — the browser never talks to Gemini, ElevenLabs or
MongoDB directly, only to this app's own API routes. If the ElevenLabs key is not configured,
the analysis page displays the recommendation text without audio controls.
/start— you choose level, subject, optional topic, difficulty, length and duration.POST /api/tests— the server asks Gemini for questions using a strict JSON response schema, shuffles each question's options, and saves the paper as aTestdocument with statusin_progress./test/[testId]— the runner loads the questions without their correct answers. It tracks per-question time, supports flagging and keyboard entry, and saves progress tolocalStorageso a refresh does not lose the attempt.POST /api/tests/[testId]/submit— the server grades the attempt against the stored questions, computes accuracy and marks the testcompleted. Submitting twice is a no-op./analysis/[testId]— accuracy, pace, per-topic performance and a full worked review of every question, filterable by correct / incorrect / skipped. When configured, ElevenLabs can read the topic recommendations with play/pause, replay, voice and playback-speed controls./dashboard— accuracy over time, topic mastery across all papers, strongest and weakest topics, and your test history.
Correct answers only ever reach the browser once a test is submitted, and grading happens on the server — the score cannot be edited from the client.
src/
app/
layout.tsx root layout, fonts, theme provider
page.tsx landing page
auth/ sign in / sign up
(app)/ signed-in shell (header, footer, auth guard)
dashboard/
start/
analysis/[testId]/
test/[testId]/ distraction-free test runner
api/
auth/{signup,login,logout}/
tests/ POST: generate + create a paper
tests/[testId]/submit/ POST: grade and complete
components/ UI, charts, runner, review
lib/
mongodb.ts cached connection
models/ Mongoose schemas
session.ts / auth.ts JWT cookie + server guards
gemini.ts question generation
queries.ts server-side reads for pages
proxy.ts route protection (Next 16 proxy convention)
npm run dev # development server
npm run build # production build
npm run start # serve the production build
npm run lint # eslintWorks on any Node host. On Vercel, add MONGODB_URI, JWT_SECRET and GEMINI_API_KEY as
project environment variables, and allow your deployment's IPs in MongoDB Atlas network
access (0.0.0.0/0 for a quick start).
Question generation for 30 questions can take longer than a free-tier function timeout.
The route sets maxDuration = 120; make sure your plan allows it, or keep tests to 10–20
questions.