About the project
Inspiration
I wanted a practical way to practice interviews that feels closer to the real experience—structured questions, timed answers, and spoken prompts—rather than only typing in a chat box. I also wanted to explore how far I could push an AI-driven workflow in a full-stack product: authentication, persistent sessions, audio recording, and cloud deployment.
What I built
This project is an AI interview simulator that lets users:
- Create interview sessions from a job description and resume
- Start an interview and receive questions (text + audio)
- Record answers and submit them for analysis
- Review history of previous interview sessions
Tech stack highlights
- React (Vite) frontend for the interview UI, recording, and playback
- FastAPI backend for session management, authentication, and interview logic
- PostgreSQL (Cloud SQL) for persistent storage
- Alembic for database migrations
- Cloud Run for containerized backend deployment
- (Optional) GCS for media storage if audio needs to persist across instances
How I built it
I built the backend first to ensure the interview flow worked end-to-end:
- Designed the database schema (users, interview sessions, interactions, audio metadata)
- Implemented authentication (
/auth/token,/auth/me) and protected routes - Added interview endpoints for creating sessions, starting interviews, and submitting answers
- Implemented audio generation/streaming endpoints for spoken questions
- Containerized the app with Docker and deployed to Cloud Run + Cloud SQL
- Added Alembic migrations and created a Cloud Run Job to run
alembic upgrade head - Built the React UI: session list, interview page, audio playback, recording, and submission states
Challenges I faced (and how I solved them)
1) Cloud Run + database migrations (Alembic)
Cloud Run services are meant to be stateless and always-on, so running migrations inside the service startup can be risky. I solved this by creating a separate Cloud Run Job dedicated to migrations. The job connects to Cloud SQL using the Cloud SQL Unix socket and runs alembic upgrade head safely.
2) Cloud SQL connectivity
In local Docker, I used localhost and exposed ports. On Cloud Run, localhost doesn’t point to Cloud SQL, so migrations initially failed. The fix was to use a Cloud SQL socket-based DATABASE_URL, and ensure the Cloud SQL connection is attached to both the service and the migration job.
3) Cookies, CORS, and authentication in production
Login worked (/auth/token returned 200), but /auth/me returned 401 because cookies weren’t being sent cross-site. The fix required:
- correct CORS configuration (
allow_credentials=True, explicit allowed origins) - correct cookie flags for HTTPS (
Secure=True,SameSite=Nonein production)
4) Audio streaming & dev/prod differences
During local development, Vite’s proxy makes /api/... requests appear to come from the frontend origin, which can be confusing at first. I standardized API base URLs and normalized relative URLs returned by the backend to ensure audio playback works consistently across local and production environments.
What I learned
- How to deploy a containerized FastAPI service on Cloud Run and connect it to Cloud SQL reliably
- How to handle real-world auth issues: cookies vs bearer tokens, CORS, and browser security rules
- How to run production database migrations safely using a dedicated migration job
- How to build an end-to-end feature involving media: recording audio, uploading it, and streaming it back
- How small dev/prod differences (proxy, HTTPS, stateless instances) can cause subtle bugs—and how to debug them systematically
What’s next
- Move all media storage to GCS (or signed URLs) to make audio fully instance-safe
- Improve interview scoring feedback and summaries
- Add richer session analytics and progress tracking
- Better UI polish and error handling for recording/streaming edge cases
Built With
- elevenlabs
- fastapi
- google-genai
- googlecloudrun
- langgraph
- postgresql
- react
- vue
Log in or sign up for Devpost to join the conversation.