TutorLoom
The learning platform for companies, colleges and training institutes. Free and open source, with an Enterprise edition.
Mock technical interviews with instant AI feedback. Pick a role, answer real questions, and get scored on clarity, structure, and content.
EducationJavaScript · TypeScriptGitHub @Khizar-IslamOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗Practice for a job interview with an AI interviewer. Pick a role and experience level, answer five generated questions, and get live-streamed feedback with scores on each answer.
Live demo: https://interview-prep-platform-xy36.vercel.app
The API runs on a free host, so the first request after a quiet period can take up to a minute. Sign-in is Google only.
GET /api/api-token) and sends it as Authorization: Bearer <token>. The API verifies it (HS256, issuer and audience pinned, expiry) and takes the user id only from the token.InterviewSession (mapped to the sessions table) so it doesn't clash with NextAuth's Session.| Layer | Tools |
|---|---|
| Frontend | Next.js (App Router), TypeScript, Tailwind CSS, Framer Motion |
| Auth | NextAuth with Google OAuth and the Prisma adapter |
| Backend | Node.js, Express |
| Database | PostgreSQL (Neon), Prisma |
| AI | Google Gemini |
| Hosting | Vercel (web), Render (API) |
backend/ Express API: sessions, answers, Gemini service, rate limiters
frontend/ Next.js app: home, new interview, interview page, dashboard
All routes except /health need Authorization: Bearer <token>.
| Method | Path | What it does |
|---|---|---|
| GET | /health | Health check |
| GET | /api/sessions | Your sessions, newest first (replaces /api/sessions/user/:userId) |
| GET | /api/sessions/:id | One session with questions and answers |
| POST | /api/sessions | Create a session: { role, experienceLevel } |
| PATCH | /api/sessions/:id/complete | Finish a session: { overallScore } |
| DELETE | /api/sessions/:id | Delete a session |
| POST | /api/answers | Submit an answer, feedback streams back: { questionId, answerText } |
backend/, create .env with DATABASE_URL, GEMINI_API_KEY, API_AUTH_SECRET, FRONTEND_ORIGIN and PORT. Then run npm install, npx prisma migrate dev and npm run dev.frontend/, create .env.local with DATABASE_URL, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, AUTH_SECRET, NEXT_PUBLIC_API_URL and API_AUTH_SECRET. Then run npm install and npm run dev.Both folders have a .env.example listing these variables.
| Variable | Where | What it is for |
|---|---|---|
DATABASE_URL | both | Postgres connection string |
GEMINI_API_KEY | backend | Gemini API key |
GEMINI_MODEL | backend | Optional. Gemini model for questions and feedback, default gemini-3.6-flash. Empty or blank uses the default |
GEMINI_FALLBACK_MODEL | backend | Optional. Tried when the main model is overloaded (503), out of quota (429) or times out, default gemini-3.5-flash-lite. Empty or blank uses the default |
API_AUTH_SECRET | both | Signs (web) and verifies (API) the sign-in tokens |
FRONTEND_ORIGIN | backend | Web app URLs allowed by CORS, comma-separated, no trailing slash |
PORT | backend | API port, default 5000 |
TRUST_PROXY_HOPS | backend | Number of proxies in front of the API in production, 1 to 5, default 1. On Render it must be 3 (measured: visitor, Cloudflare, Render proxy); a wrong value makes every visitor share one IP counter |
GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET | frontend | Google sign-in |
AUTH_SECRET | frontend | NextAuth secret (NEXTAUTH_SECRET also works) |
NEXT_PUBLIC_API_URL | frontend | API URL |
API_AUTH_SECRET must be the same on both sides, at least 32 characters, different from the NextAuth secret, and never prefixed with NEXT_PUBLIC_. The API won't start without it or FRONTEND_ORIGIN. To generate one: node -e "console.log(require('crypto').randomBytes(36).toString('base64url'))"
To run the auth tests, create a separate database branch (Neon: Branches, Create branch from your default branch), set it as TEST_DATABASE_URL in backend/.env, and run npm run test:auth in backend/. They check that missing or bad tokens get 401 and that one user can't read, change, delete or answer in another user's session. The script refuses to run if TEST_DATABASE_URL is the same as DATABASE_URL or on the same host.
npm run test:config, npm run test:ratelimit, npm run test:model and npm run test:retry in backend/ need no database and never call Gemini.
Deploy the API (Render) first, then the web app (Vercel), with the same API_AUTH_SECRET on both and the Vercel URL in FRONTEND_ORIGIN. The new API closes the security hole on its own, and the new web app only works with it because it sends a token instead of a user id. Until the web app is redeployed, the old one gets 401 errors.
Khizar Islam Rathore, Software Engineering student at the University of Karachi. GitHub: github.com/Khizar-Islam | LinkedIn: linkedin.com/in/khizar-islam-rathore