Comprehensive Build Guide & Interview Prep
This document serves as a complete walkthrough of how the Quizzo Teacher Assistant application was built. It is designed to help you understand the architecture, explain your technical decisions during an interview, and confidently discuss how all the pieces fit together.
1. High-Level Architecture Overview
Quizzo is a Full-Stack AI Application separated into two distinct repositories (or folders):
- Frontend (Client): Built with Next.js (React) and TypeScript. It handles the user interface, authentication state, and the chat window.
- Backend (Server): Built with Node.js, Express, and TypeScript. It handles user authentication, session management, and securely communicates with the Google Gemini AI.
Why did we separate the frontend and backend?
-
Security: We must never expose our
GEMINI_API_KEYto the browser. By keeping an Express backend, the frontend simply asks the backend for a chat response, and the backend securely talks to Google. - Scalability: The backend can handle complex AI logic, database connections, and long-running tasks without slowing down the user's browser.
2. Step-by-Step Implementation
Phase 1: The Backend Foundation (Node & Express)
We started by creating a REST API using Express and TypeScript.
-
In-Memory Store: To keep things lightweight for the initial build, we used an in-memory array (
memoryStore.ts) to store Users and Chat Sessions. (Interview Tip: Mention that in a real production app, this would be replaced by a database like PostgreSQL or MongoDB). -
Authentication:
- We built
/api/auth/registerand/api/auth/login. - We used
bcryptto securely hash user passwords before saving them. - We used
jsonwebtoken(JWT) to generate a secure token when a user logs in.
- We built
-
Auth Middleware: We created
auth.middleware.tsto protect our chat routes. This intercepts requests, checks if the user has a valid JWT in theirAuthorizationheader, and rejects unauthorized access.
Phase 2: Integrating Google Gemini AI
This is the "brain" of the application. We used the @google/generative-ai SDK.
-
Configuration (
gemini.ts): We initialized the AI client using our secretGEMINI_API_KEY. We also implemented a Fallback Model Strategy, meaning if our primary model (gemini-3.5-flash) goes down or hits a rate limit, the system automatically tries secondary models to ensure the app stays online. -
Service Layer (
gemini.service.ts): We created ahandleChatfunction that takes the user's message and their past conversation history, sends it to Gemini, and returns the response. -
Function Calling / Tools: This is the most advanced part of the AI integration. We didn't just build a chatbot; we gave the AI "Tools" it can execute.
-
generate_quiz: The AI can output a structured quiz based on a topic. -
grade_response: The AI can compare a student's answer against a rubric and grade it. -
summarize_performance: The AI can aggregate grades and provide a class summary. -
How it works: The AI detects when a user asks for a quiz. Instead of just replying with text, it tells our backend "Execute the
generate_quiztool". Our backend runs the tool, hands the data back to the AI, and the AI presents it to the user.
-
Phase 3: The Frontend Experience (Next.js)
We built the user interface using Next.js.
-
AuthContext: We used React's Context API to manage global state. When a user logs in,
AuthContextsaves their JWT token tolocalStorageso they stay logged in even if they refresh the page. -
API Service (
api.ts): We centralized all our backendfetchrequests here. It dynamically readsprocess.env.NEXT_PUBLIC_API_URLso it knows whether to talk tolocalhostor the live deployed backend. - The UI: We built a clean Login and Register page, highlighting our tagline: "The Smart Teaching Assistant". We then built a dynamic chat interface that updates instantly when the user sends a message, handling loading states while waiting for the AI to respond.
Phase 4: Testing & Quality Assurance
Writing tests proves to interviewers that you care about code quality.
-
Jest & SWC: We set up a test suite using
Jest. We specifically opted for@swc/jestbecause the defaultts-jestrunner had compatibility issues with our modern version of TypeScript. SWC is written in Rust and compiles tests incredibly fast. -
Unit Tests: We wrote tests for
auth.middleware.tsto ensure it correctly blocks bad tokens and allows good ones. We also tested our AI tool definitions (likegrade_response.test.ts) to ensure they return the expected schemas.
Phase 5: Deployment
We deployed the app using two different specialized platforms:
-
Netlify (Frontend): Next.js works flawlessly on Netlify. We provided Netlify with our
NEXT_PUBLIC_API_URLenvironment variable so it knows how to find our server. -
Render (Backend): We deployed the Express server to Render because it is designed for long-running Node.js processes. We added our
GEMINI_API_KEYandJWT_SECRETsecurely to Render's environment dashboard. -
CORS Fix: We encountered a CORS error where the backend blocked the frontend. We fixed this in
server.tsby explicitly allowing requests from our Netlify URL.
3. Common Interview Questions & How to Answer Them
Q: Why did you use Next.js instead of just standard React?
"Next.js provides an excellent developer experience out of the box. It gives us an intuitive file-based routing system, built-in optimization for fonts and images, and a clear path if we ever wanted to move some of our Express backend logic into Next.js Server Actions or API routes in the future."
Q: How did you secure the application?
"Security was a priority at multiple layers. First, passwords are never stored in plaintext; we salt and hash them using bcrypt. Second, we use stateless JWT authentication, passing tokens via headers. Finally, our Gemini API key is strictly kept on the server and is never exposed to the client bundle."
Q: How do the Gemini AI Tools/Functions work in your app?
"We implemented 'Function Calling'. We defined a schema of tools (like
grade_response) and passed them to the Gemini model. When a teacher asks a specific question, the AI determines which tool to use, pauses its generation, returns a JSON object with arguments to our backend, our backend executes the javascript logic for that tool, and hands the result back to the AI to format a final answer."
Q: What would you do differently if you were to scale this to 100,000 users?
"Currently, we use an in-memory store for users and chat history. To scale, the immediate first step would be implementing a persistent database like PostgreSQL for relational user data, and perhaps Redis for fast session/chat history retrieval. I would also add rate-limiting to the backend to prevent abuse of our Gemini API quota."
Q: What was the hardest bug you faced and how did you solve it?
"When deploying, we hit an
ERESOLVEnpm error on Render because of a strict peer dependency conflict with an older version ofts-jestand our moderntypescriptversion. I diagnosed it by reading the build logs, uninstalled the conflicting package since we had already migrated to the much faster@swc/jestrunner, and pushed the cleanpackage.jsonto trigger a successful build. We also navigated a CORS block by configuring our Express middleware to specifically whitelist our live Netlify domain."
Top comments (0)