Quizwright writes multiple-choice questions for a class. You choose a topic, a difficulty, how many questions you want, and the student level. Each question comes back with four options, one correct answer, and a short explanation.
The browser only talks to POST /api/generate. Gemini is called from that server route with the official @google/genai SDK. The API key is read from GEMINI_API_KEY and is never sent to the client.
- Node.js 20.9 or newer
- npm
- A Gemini API key from Google AI Studio
npm install
copy .env.example .env.local
notepad .env.local
npm run devnpm install
Copy-Item .env.example .env.local
notepad .env.local
npm run devnpm install
cp .env.example .env.local
npm run devPut your key on the GEMINI_API_KEY= line in .env.local, save the file, then start the dev server. Open http://localhost:3000.
.env.local is gitignored. Do not commit it, and do not rename the variable to anything starting with NEXT_PUBLIC_.
| Name | Required | Purpose |
|---|---|---|
GEMINI_API_KEY |
Yes | Server-only Gemini API key. |
GEMINI_MODEL |
No | Model id. Defaults to gemini-3.5-flash-lite. |
Restart npm run dev after changing .env.local. Next.js only reads env files at startup.
- Import this repository in Vercel. The framework preset is Next.js.
- Open the project Settings → Environment Variables.
- Add
GEMINI_API_KEYand paste the key from Google AI Studio. Enable it for Production and Preview (and Development if you usevercel dev). - Optionally add
GEMINI_MODEL. Leave it unset to usegemini-3.5-flash-lite. - Do not add
NEXT_PUBLIC_GEMINI_API_KEY. ANEXT_PUBLIC_prefix would expose the key in the browser bundle. - Redeploy. Vercel does not upload
.env.local; the dashboard variables are what the deployed server reads.
The key is only used when someone generates questions, so the project can build before the variable is set. Requests fail with a clear error until GEMINI_API_KEY is present.
| Command | What it does |
|---|---|
npm run dev |
Start the local dev server. |
npm run build |
Production build. |
npm run start |
Serve the production build. |
npm run lint |
Run ESLint. |
npm test |
Run unit tests once. |
- The route validates the body with Zod: topic, difficulty (
Beginner,Intermediate, orAdvanced), question count from 1 to 20, and student level. - Gemini is asked for JSON with
responseMimeType: "application/json"and a response schema for that count, topic, and difficulty. - The route parses the JSON and checks it again with Zod: the right number of questions, four non-empty distinct options,
correctAnswerequal to one of those options, a non-empty explanation, and difficulty and topic copied from the request. - If that check fails, the route retries once with the validation errors. If the second response still fails, the client gets a clear error and no question cards.
- The page renders a set only after the same schema accepts it.
The model adapter lives in lib/ai/gemini.ts. Question checking lives in lib/questions/ and does not import the SDK, so another provider can replace the adapter without changing the validation rules.
Built by Mahir Faysal, a web developer in Bangladesh.
- Project page: Quizwright (AI Question Generator) on mfaysal.com
- More projects: mfaysal.com/projects · Blog: mfaysal.com/blog