Static Vite + React + TypeScript exam app for GitHub/Vercel hosting. The frontend talks to a Google Apps Script Web App with fetch() and never contains correct answers.
src/api.ts: all Google Apps Script requestssrc/types.ts: frontend API and exam typessrc/components/: welcome, exam, result, and leaderboard UIapps-script/Code.gs: Apps Script backend.env.example: required frontend environment variable
Create a Google Sheet for the exam data. The Apps Script code uses the active spreadsheet, so the script should be bound to this sheet.
Required sheet tabs:
StudentsQuestions
The script will create Sessions automatically when students start exams.
Open the Google Sheet and import exam_questions_324.csv into the Questions sheet.
Supported headers:
id,section,questionText,optionA,optionB,optionC,optionD,optionE,correctAnswer
optionE is optional. correctAnswer must stay in Google Sheets and Apps Script only. Do not export it into frontend JSON.
Create a sheet named Students with this structure:
Column A: studentId / 學號
Column B: studentName / 姓名
Column C onward: exam scores / attempts
Use the first row as headers. Add student data starting from row 2.
- In the Google Sheet, go to
Extensions->Apps Script. - Replace the default code with
apps-script/Code.gs. - Save the project.
- Click
Deploy->New deployment. - Select type
Web app. - Set
Execute astoMe. - Set access to the audience you need. For a public exam link, use
Anyone. - Deploy and copy the Web App URL ending in
/exec.
When changing Apps Script code later, create a new deployment version or update the existing deployment.
Before connecting the frontend, open this URL in an incognito browser:
https://script.google.com/macros/s/YOUR_DEPLOYMENT_ID/exec?action=validateSetup
It must return JSON. If it shows a Google sign-in page, the Web App access is not public enough for the exam site.
Create .env in the project root:
GOOGLE_SCRIPT_URL=https://script.google.com/macros/s/YOUR_DEPLOYMENT_ID/execFor local development:
npm install
npm run dev- Push this
examfolder to GitHub. - Import the repository in Vercel.
- Set the framework preset to
Vite. - Add this environment variable in Vercel:
GOOGLE_SCRIPT_URL=https://script.google.com/macros/s/YOUR_DEPLOYMENT_ID/exec
- Deploy.
Build command:
npm run buildOutput directory:
dist
- Correct answers must remain only in the
Questionssheet andapps-script/Code.gs. - The frontend receives questions without answer keys.
- The frontend submits
{ questionId, selectedKey }only. - Apps Script selects random questions, stores sessions, evaluates answers, writes scores, and returns leaderboard data.
- Do not commit
.env. - Do not publish
exam_questions_324.csvwithcorrectAnswerin the public frontend repository unless the repository is private.
startExam(studentId)checks theStudentssheet, selects 50 unique random questions, shuffles options, stores a session, and returns questions without answers.submitExam(sessionId, studentId, answers)verifies the session owner, grades server-side, saves the score to the next empty score column from C onward, and returns the leaderboard.LockServiceis used while writing scores to reduce race conditions.