AI-interview-coach: AI Interview Coach: When the LLM Becomes a Typed Backend
How this repo turns a resume or job description into structured guidance, a day-by-day prep plan, and a polished PDF resume without relying on brittle prompt parsing.
- AI Interview Coach treats the model like a typed service, not a chat partner, and that design choice makes the whole app more reliable.
- The strongest output is not advice but an artifact: a roadmap, a tailored resume, and a PDF that can leave the browser and survive real use.
- The repo’s value comes from structure, with Zod validation, parsed inputs, and downstream UI pieces all built around the same schema.
- It is a stronger product than a generic AI wrapper because it turns messy candidate material into repeatable workflows instead of vague reassurance.
The LLM is not chatting. It is filling a schema.
That is the real story here. The app takes a resume, a self-description, or a job description and forces the model to return a structured interview report, not a rambling answer. In practice, that means the model behaves more like a backend primitive than a conversational UI.
const interviewReportSchema = z.object({
matchScore: z.number().min(0).max(100),
skillGaps: z.array(z.string()),
technicalQuestions: z.array(z.string()),
behavioralQuestions: z.array(z.string()),
preparationPlan: z.array(z.object({
day: z.number(),
task: z.string(),
})),
});
const jsonSchema = zodToJsonSchema(interviewReportSchema);
const report = await generateContent({
model: 'gemini-3-flash-preview',
schema: jsonSchema,
});
That schema is the product. Once the response is forced into known fields, the app can score fit, surface gaps, generate questions, and render a preparation plan without guessing what the model meant. The output becomes database-ready instead of prompt-ready.
The input can be messy. The output stays clean.
The backend is built to absorb ugly real-world input. A user can upload a PDF resume or paste a self-description, and the server handles the file boundary with multer, extracts text with pdf-parse, and then normalizes that text into a prompt. The whole point is to make the input path forgiving without letting the output drift.
| Layer | Generic chatbot flow | AI Interview Coach |
|---|---|---|
| Input | Freeform prompt text | PDF resume, self-description, or JD |
| Model output | Paragraphs and guesswork | Schema-validated JSON |
| Downstream use | Manual copying | UI, roadmap, and PDF generation |
| Failure mode | Hard to parse | Easy to validate |
| Product shape | Conversation | Pipeline |
That difference matters because the messy part is upstream, not downstream. The app does the painful work at the edge, then preserves a clean shape for everything after it.
The best output is not advice. It is an artifact.
The most useful move in the repo is the one-step conversion from structured interview insight to a tailored resume. The AI generates HTML, and Puppeteer turns that HTML into a PDF. That matters because the user does not just get suggestions. They get something they can download, send, and actually use.
const html = await generateTailoredResumeHTML(profile, jobDescription, report);
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setContent(html, { waitUntil: 'networkidle0' });
await page.pdf({ format: 'A4', printBackground: true });
The same logic powers the prep roadmap. It is not a blob of advice buried in a chat transcript. It is a day-by-day object that the frontend can render into a real workflow, which makes the product feel much more deliberate than a typical AI wrapper.
The frontend is built around the shape of the data.
The React side is organized around domains rather than generic screens. Authentication lives apart from interview logic. A custom hook owns the interview flow. Protected routes keep the experience coherent once the token is in place. That structure mirrors the backend contract: clear state in, clear state out.
| Frontend concern | How it shows up | Why it matters |
|---|---|---|
| Auth | Protected routes and token state | Keeps interview data behind a session boundary |
| Interview flow | Custom <code>useInterview</code> hook | Separates fetch logic from UI rendering |
| State | Feature-based context | Makes report, loading, and roadmap states explicit |
| Rendering | Roadmap and report sections | Matches the schema instead of fighting it |
That is the difference between a demo and a product. The UI is not improvising around model output. It is built to expect the output shape from the start.
Security and validation are doing real work here.
This repo is better than many AI demos because it does not ignore the boring parts. JWT auth is in place, logout blacklisting exists, and file uploads are handled through a controlled middleware path. Those are not flashy features, but they are the difference between a toy and software that can survive users.
| Concern | AI demo pattern | This repo |
|---|---|---|
| Session control | One token, no revocation | JWT plus blacklist on logout |
| File handling | Trust the upload | Multer boundary plus parsing step |
| Model output | Parse later if needed | Validate immediately with Zod |
| Data flow | Loose and conversational | Constrained and repeatable |
The validation story is especially strong. If the model misses the schema, the app does not have to guess. It can reject, retry, or surface a precise failure. That is how you make generated output trustworthy enough to build on.
What this repo gets right that generic AI interview tools miss.
Generic interview tools often optimize for conversation. This one optimizes for completion. It is closer to a workflow engine than a coach, and that is why it feels practical. It can be self-hosted, it keeps the candidate’s data closer to the application, and it produces reusable artifacts instead of just reassurance.
| Question | Generic interview tool | AI Interview Coach |
|---|---|---|
| What do I get? | A chat session | A structured report and a PDF |
| How predictable is it? | Low | High |
| Can I reuse the output? | Usually not | Yes, as UI data and documents |
| Is the model the product? | Often yes | No, the output shape is the product |
| Does it feel shippable? | Sometimes | Yes |
That is the central lesson. The best GenAI apps are often the least chatty ones. They narrow the model’s job, validate the result, and then turn that result into something people can actually carry forward.