Full-stack interviews
Personal course · PERN / MERN + FastAPI + SSO · India, 4–7.5 LPA band

Full-Stack Offer Plan

A plan to get selected for full-stack roles like the one you pasted: React, Node and Express, PostgreSQL or MongoDB, Python with FastAPI, SSO, with Docker and LLMs as a plus. It starts with how hiring works, then teaches each skill in the job description with the questions interviewers actually ask, then has you build a project that proves all of it.

The whole plan in one sentence. Map every line of the job description to something you can show (a project feature, a commit, an answer you can give in two minutes), then rehearse each interview round until you can do it without notes.

0 · How hiring works for this role

The hiring loopA typical hiring loop for this roleApply + ATSresume keywordsRecruiter callCTC, notice, fitOnline testMCQ + 1–2 DSATech round 1JS, React, NodeTech round 2project, DB, designManager / HRbehaviour, offerUsually 2–4 weeks end to end. The two technical rounds decide most offers.

This job is a mid-level product-company or startup role: 2–3 years of experience, ₹4–7.5 lakh per year. Companies in this band hire fast and test practically. They want someone who can ship a feature end to end in week one. Fancy algorithms matter less than clean, working code you can explain.

RoundWhat they checkHow you pass
Resume screen (often an ATS)Keywords from the JD, years, a relevant projectMirror the JD's words honestly: PERN, FastAPI, SSO, Docker. One page. Section 1.
Recruiter call (10–20 min)Current CTC, expected CTC, notice period, location, communicationHave the three numbers ready. Speak clearly about one project in 60 seconds.
Online assessmentMCQs on JS, SQL, OS and networks, plus 1–2 easy-to-medium coding problems on HackerRank, HackerEarth or MettlSection 10 patterns, daily practice, timed.
Technical round 1JS fundamentals, React, Node, often live coding (a component, an API, a SQL query)Sections 3–6. Think aloud. Write working code first, then improve it.
Technical round 2A deep dive on your project, database design, auth, a small system design, debuggingSections 8, 11, 12. Know every decision in your project and why you made it.
Manager / cultureOwnership, teamwork, handling pressure and conflictSTAR stories, section 13.
HROffer, CTC breakup, joining date, documentsNegotiate politely with data, section 13.

About the experience line. The JD says 2–3 years but also lists "total work: 1 year (preferred)". Ranges like this are soft. If your real project work (AIRA: React, Node, PostgreSQL, games with their own backends, AI features) is shown clearly, apply. Count shipped production work honestly, and let the project do the arguing.

Check yourself

1. Which round usually decides most offers for this role?

2. What does the online assessment typically contain at this level?

3. The JD asks for 2–3 years and you have strong project work but less. What does the course suggest?

1 · Your application

1.1 Map the JD to proof

Interviewers ask about what's on your resume. So put only what you can defend, and make each line point to proof.

JD lineProof you should have
PERN / MERN web appsThe capstone (section 12) plus your real work. Name the stack in the project title line.
RESTful APIs in Express and FastAPICapstone: the Express main API and a FastAPI AI service, both with OpenAPI docs and tests.
Performance, quality, responsivenessA number: "cut page load from 3.1 s to 1.2 s by code-splitting and an index". Measure something real.
Code reviews, best practicesA GitHub repo with PRs, a CI workflow, lint and tests. Reviewers do look.
Authentication, authorization, SSOCapstone: OIDC login with Google or Keycloak, role-based access, refresh handling. The most valuable item here, because many candidates can't explain it.
Docker, Kubernetes (plus)A docker-compose.yml that starts everything with one command. A tiny K8s manifest is a bonus.
GenAI and LLMs (plus)The FastAPI service: summarise with an LLM, stream the answer, handle timeouts and cost. You already have real experience here, so say so.

1.2 Resume rules

  • One page, PDF, simple layout (ATS parsers break on columns, tables and icons).
    ATS-readable resume Hard to parse ColumnsTablesIcons Lost text One-page PDFContactJD wordsExperience + numbersTwo projects + linksATS reads proof
  • Top: name, phone, email, city, LinkedIn, GitHub, portfolio link.
  • Two-line summary using the JD's words: "Full-stack developer (React, Node/Express, PostgreSQL, Python/FastAPI) building production web apps with SSO and LLM features."
  • Experience bullets: action + what + result with a number. "Built the parent dashboard in React and Express, used by N families, p95 API latency under 200 ms."
  • Skills grouped: Frontend / Backend / Databases / DevOps / AI. No skill bars, no ratings.
  • Projects: two, each with a live link and a repo.

weak bullet Worked on React and Node for various features.

strong bullet Shipped a multiplayer word game (React, Node, WebSockets, PostgreSQL) for 2–4 players, with token checks against the main API.

1.3 Where to apply

  • Naukri: most recruiters here search it. Update your profile every few days, because freshness affects ranking. Put the JD keywords in the headline and key skills.
  • LinkedIn: turn on "Open to work" for recruiters only. Message the hiring manager or a team engineer, not just HR.
  • Instahyre, Cutshort, Wellfound, Hirist, Foundit: startups and product companies.
  • Referrals: the highest response rate. A short, specific message works best:
Hi <name>, I saw the Full Stack Developer (PERN + FastAPI) opening at <company>.
I build React/Node/Postgres apps with SSO and LLM features. Example: <live link>.
Would you be open to referring me? Resume attached. Happy to answer anything.

1.4 The money conversation

  • CTC (cost to company) is not what you take home. In-hand is lower, after the employee PF contribution (12% of basic), professional tax, and income tax (TDS). Variable pay and gratuity are inside the CTC too. Always ask for the breakup.
    CTC to in-hand pay CTCFixed payVariable payGratuity Pay before deductionsEmployee PFProfessional taxIncome tax (TDS)In-hand pay −−−12% of basicAsk for thebreakup
  • Expected CTC: research the band (it's in this JD: ₹4–7.5 LPA). Give a range whose bottom you're happy with: "Based on the role and my experience with SSO and FastAPI, I'm looking at ₹6.5–7.5 LPA."
  • Notice period: be exact. Shorter notice is a real advantage in this band. Say if it's negotiable.

Check yourself

1. Why should a resume avoid columns, tables and icons?

2. What makes a resume bullet strong?

3. Why is in-hand salary lower than CTC?

2 · The 8-week plan

The eight-week planEight weeks, about 2 hours a dayWeek 1JS+ TS coreWeek 2ReactWeek 3Node+ ExpressWeek 4Postgres+ MongoWeek 5Python+ FastAPIWeek 6Auth+ SSOWeek 7Docker+ LLMsWeek 8Mocks+ applyEvery day: 1 DSA problem · 20 min of the capstone project · 5 quiz questions

Two hours a day. Each week: learn the topic (the section below), answer its interview questions out loud, and add that feature to the capstone. Week 8 is mock interviews and applying hard. Start applying in week 5. Interviews take two to four weeks to arrive, so by the time they do, you'll be ready.

3 · JavaScript and TypeScript

Round 1 almost always opens here. These are the topics that come up again and again, each with the answer you should be able to give.

The event loopCall stackmain()handler()JSON.parse()Microtask queuepromise .then, awaitTask (macrotask) queuesetTimeout, I/O, eventslibuv / Web APIstimers, sockets,file system, thread poolWhen the stack is empty: run ALL microtasks, then ONE task, then repeat.So a resolved promise always runs before setTimeout(fn, 0).
What does this print, and why? (event loop)
console.log("A");
setTimeout(() => console.log("B"), 0);
Promise.resolve().then(() => console.log("C"));
console.log("D");

A, D, C, B. Synchronous code runs first (A, D). When the stack empties, all microtasks run (the promise callback C), then one macrotask (the timer B).

What is a closure? Give a real use.

A function that remembers the variables of the scope where it was created, even after that scope has returned.

Closure keeps state debounce(fn, ms)let tReturnedcallback Outer call returnsClosure keeps tReturned callback Call 1Call 2clearTimeout(t)setTimeout(...)
Uses: private state (a counter factory), event handlers that remember an id, debounce.

function debounce(fn, ms) {
  let t;                                   // lives on in the closure
  return (...args) => { clearTimeout(t); t = setTimeout(() => fn(...args), ms); };
}
var vs let vs const?

var is function-scoped and hoisted with the value undefined. let and const are block-scoped and in the "temporal dead zone" until declared. const stops reassignment, not mutation: const a = []; a.push(1) is fine. The classic trap: for (var i...) setTimeout(() => console.log(i)) prints the final value every time; let gives each iteration its own i.

How does this work?

It's decided by how a function is called. obj.f() gives obj. A plain call gives undefined in strict mode. new F() gives the new object. call, apply and bind set it explicitly. Arrow functions have no own this; they use the surrounding one, which is why they're handy in callbacks.

== vs ===, and null vs undefined?

=== compares without type conversion; always use it. undefined means "not assigned"; null means "deliberately empty". ?? falls back only on those two, while || also falls back on 0 and "".

Promise.all vs allSettled vs race vs any?

all rejects on the first failure. allSettled waits for every result, success or not. race takes the first to settle. any takes the first success. Run independent awaits in parallel with await Promise.all([a(), b()]), not one after the other.

Deep vs shallow copy?

Spread ({...obj}) copies one level; nested objects are shared. structuredClone(obj) does a real deep copy, including Dates and Maps. That matters in React state updates.

TypeScript: interface vs type, generics, narrowing, unknown vs any
type Result<T> = { ok: true; data: T } | { ok: false; error: string };   // discriminated union

function unwrap<T>(r: Result<T>): T {
  if (r.ok) return r.data;           // narrowing: TS knows r.data exists here
  throw new Error(r.error);
}

function parse(input: unknown) {      // unknown forces you to check before use; any switches TS off
  if (typeof input === "string") return input.trim();
}

Interfaces can be extended and merged; types can express unions and mapped types. Either is fine for object shapes, so pick one and stay consistent. Useful utilities: Partial, Pick, Omit, Record.

Live-coding warm-ups (do each in under 10 minutes): debounce, throttle, a deep flatten, Promise.all from scratch, group an array of objects by a key, an LRU cache with Map.

Check yourself

1. What does debounce do?

2. for (var i = 0; i < 3; i++) setTimeout(() => console.log(i)); prints…

3. Why prefer unknown over any for untrusted input in TypeScript?

4 · React

What triggers a re-render, and how do you avoid wasted ones?

State change, parent re-render, or context change. A parent re-render re-renders its children even if their props are the same.

React render tree ParentState changeContext change Cheap childExpensive childParent re-renderParent re-render React.memoSame props can still renderMeasure withProfiler first
Fixes: lift state down (keep it close to where it's used), React.memo on expensive children, useMemo/useCallback to keep props stable, and splitting context. Measure with the React Profiler first. Don't sprinkle memo everywhere.

Why do lists need keys? Why not the index?

Keys tell React which item is which between renders. With index keys, inserting at the top shifts every key, so React reuses the wrong DOM and component state (a checkbox stays ticked on the wrong row). Use a stable id.

useEffect: dependencies and cleanup
useEffect(() => {
  const ctrl = new AbortController();
  fetch(`/api/tasks?team=${teamId}`, { signal: ctrl.signal })
    .then(r => r.json()).then(setTasks).catch(() => {});
  return () => ctrl.abort();          // cleanup: cancel if teamId changes or we unmount
useEffect cleanup cycle [teamId] dependency Render:teamId AEffect:fetch AteamIdchangesCleanup:abort AEffect:fetch B UnmountCleanup:abortStale fetch
}, [teamId]); // re-run only when teamId changes

Missing dependencies give stale values; an object created on every render as a dependency gives an infinite loop. Many effects shouldn't exist: derive values during render instead of syncing state with an effect.

Controlled vs uncontrolled inputs

Controlled: React state owns the value (value + onChange), which is good for validation as you type. Uncontrolled: the DOM owns it, and you read it via a ref or FormData, which is simpler for big forms. Libraries like React Hook Form mix both for speed.

Where should state live? (useState, Context, Redux/Zustand, server state)

Local UI state: useState. Shared and rarely changing (theme, current user): Context. Complex client state: Zustand or Redux Toolkit. Server data (lists from the API): TanStack Query or SWR, which handle caching, refetching and loading states. Interviewers like this split because it shows you don't put everything in Redux.

Making a slow React page fast

Code-split routes with React.lazy, virtualise long lists, memoise expensive children, debounce search inputs, avoid big context values, and lazy-load images. Check bundle size. Always say what you measured before and after.

Live-coding warm-ups: a search box with debounce that fetches results; a todo list with add, toggle and delete; a paginated table; a custom hook useFetch(url) with loading, error and data states.

Check yourself

1. A parent re-renders. Its child's props did not change. What happens by default?

2. Which is a sign you don't need a useEffect?

3. What is the best first step before optimising a slow React page?

5 · Node and Express

One request through the stackReact + TSbrowserExpress APIauth · validationPostgreSQLSQL, transactionsFastAPIPython AI serviceLLM providersummaries, chatJSONEach arrow is an interview question: CORS, validation, SQL, timeouts, retries, secrets
How does Node handle many requests on one thread?

The event loop. I/O (network, disk) is handed to the OS or libuv's thread pool, and the JS thread moves on, so one process serves thousands of waiting connections. CPU-heavy work (image processing, big JSON) blocks everyone. Move it to worker threads, a queue, or another service.

Middleware, and a proper error handler
const app = express();
app.use(express.json({ limit: "1mb" }));
app.use(helmet());                                   // security headers
app.use(cors({ origin: process.env.WEB_ORIGIN, credentials: true }));

app.get("/api/tasks", requireAuth, async (req, res, next) => {
  try {
    const { rows } = await db.query(
      "SELECT id, title, done FROM tasks WHERE team_id = $1 ORDER BY id LIMIT 50", [req.user.teamId]);
    res.json(rows);
  } catch (err) { next(err); }                       // hand errors to the error middleware
});

app.use((err, req, res, next) => {                   // 4 arguments = error handler, registered last
  req.log?.error(err);
  res.status(err.status ?? 500).json({ error: err.expose ? err.message : "Internal error" });
});

Middleware runs in order. Each one calls next() or ends the response.

Express middleware chain Requestexpress.jsonhelmetcorsrequireAuthRouteResponse next()next()next()next()next()next() Error handlerRegistered lastError responsenext(err)
Note the $1 placeholder: never build SQL by concatenating strings, or you invite SQL injection.

REST design: status codes and conventions
DoCode
Created a resource (POST)201 + its location or body
Success with no body (DELETE)204
Bad input (validation failed)400
Not logged in / bad token401
Logged in but not allowed403
Not found404
Conflict (duplicate email)409
Too many requests429

Plural nouns (/tasks/42), verbs from HTTP methods, pagination (?limit=&cursor=), filtering in query params, versioning (/v1), and validation at the edge with zod or Joi. PUT replaces a resource and PATCH updates part of it; GET, PUT and DELETE should be idempotent.

Security checklist they love to ask
  • Validate all input; use parameterised queries.
  • Hash passwords with bcrypt or argon2, never encrypt or store them plain.
  • helmet, a strict CORS allowlist, rate limiting on login.
  • Secrets in environment variables, never in git.
  • Cookies HttpOnly, Secure, SameSite=Lax; CSRF protection if you use cookies for auth.
  • Don't leak stack traces in production errors.
Scaling Node in production

Run several processes (PM2 cluster or several containers) behind a load balancer. Keep servers stateless (sessions in Redis or Postgres), add caching, a connection pool for the database, and graceful shutdown on SIGTERM.

Check yourself

1. What blocks a Node server for every user?

2. Which status should POST /users return when the email already exists?

3. Which HTTP methods should be idempotent?

6 · PostgreSQL and MongoDB

6.1 SQL you must write without help

-- tables for the capstone
CREATE TABLE teams (id serial PRIMARY KEY, name text NOT NULL);
CREATE TABLE users (id serial PRIMARY KEY, email text UNIQUE NOT NULL, team_id int REFERENCES teams(id), role text NOT NULL DEFAULT 'member');
CREATE TABLE tasks (id serial PRIMARY KEY, team_id int NOT NULL REFERENCES teams(id), assignee_id int REFERENCES users(id),
                    title text NOT NULL, done boolean NOT NULL DEFAULT false, created_at timestamptz NOT NULL DEFAULT now());
CREATE INDEX tasks_team_created ON tasks (team_id, created_at DESC);

-- open tasks per user, including users with none (LEFT JOIN + GROUP BY)
SELECT u.email, COUNT(t.id) FILTER (WHERE NOT t.done) AS open_tasks
FROM users u LEFT JOIN tasks t ON t.assignee_id = u.id
GROUP BY u.email ORDER BY open_tasks DESC;

-- second-highest-paid style question, with a window function
SELECT * FROM (
  SELECT t.*, ROW_NUMBER() OVER (PARTITION BY team_id ORDER BY created_at DESC) AS rn FROM tasks t
) x WHERE rn <= 3;          -- latest 3 tasks per team
Indexes: when do they help, and when do they hurt?

An index is a sorted lookup structure (usually a B-tree). It speeds up WHERE, JOIN and ORDER BY on its columns and slows down writes, because every insert updates it. A composite index (team_id, created_at) serves filters on team_id alone or both, but not on created_at alone (leftmost prefix).

B-tree leftmost prefix (team_id, created_at) Team 1Team 2Team 3 WHERE team_idWHERE team_id +created_atWHERE created_at only direct branchbranch then slotdotted scan across all groupsWrites also update index
Prove it with EXPLAIN ANALYZE: look for a sequential scan turning into an index scan.

Transactions and ACID

Atomic (all or nothing), Consistent (constraints hold), Isolated (concurrent transactions don't see each other's half-done work), Durable (committed means saved). Transfer money, or "create order + reduce stock", inside BEGIN … COMMIT. Postgres's default isolation is Read Committed. Know that SELECT … FOR UPDATE locks rows to prevent a double-booking race.

Normalization, and the N+1 query problem

Normalize to remove duplicated facts (a user's email lives in one row). Denormalize on purpose for read speed. N+1: loading 50 tasks, then querying the assignee for each one is 51 queries. Fix it with one JOIN, or a single WHERE id = ANY($1) batch.

6.2 MongoDB, for the MERN half

When Mongo, when Postgres? How do you model one-to-many?

Mongo suits flexible, document-shaped data read as a whole (a product with variable attributes, event logs). Postgres suits relational data with joins and strong constraints (users, teams, payments). One-to-many in Mongo: embed when the children are few and always read with the parent (an order's line items); reference when they are many or shared (a user's thousands of posts).

MongoDB embed or reference EmbedReference OrderLine itemLine itemLine itemFew; read together UserPostPostPostMany or shared

// aggregation: open tasks per assignee
db.tasks.aggregate([
  { $match: { done: false } },
  { $group: { _id: "$assigneeId", open: { $sum: 1 } } },
  { $sort: { open: -1 } }
]);

Check yourself

1. Which join returns every user, even those with no tasks?

2. What does SELECT … FOR UPDATE do inside a transaction?

3. How do you prove a new index is used?

7 · Python and FastAPI

from fastapi import FastAPI, Depends, Header, HTTPException
from pydantic import BaseModel, Field

app = FastAPI(title="ai-service")

class SummariseIn(BaseModel):
    text: str = Field(min_length=1, max_length=20_000)
    max_words: int = Field(default=80, ge=10, le=300)

class SummariseOut(BaseModel):
    summary: str

def current_user(authorization: str = Header(...)):      # dependency injection
    user = verify_token(authorization.removeprefix("Bearer "))
    if not user:
        raise HTTPException(status_code=401, detail="Invalid token")
    return user

@app.post("/summarise", response_model=SummariseOut)
async def summarise(body: SummariseIn, user=Depends(current_user)):
    summary = await llm_summarise(body.text, body.max_words)   # async: don't block the event loop
    return SummariseOut(summary=summary)

Run it with uvicorn main:app --reload and open /docs for the automatic OpenAPI page. That's a great thing to show in an interview.

Why FastAPI? What does Pydantic do?

Type hints become validation, serialisation and API docs automatically. Pydantic parses input into typed models and returns a 422 with details when it's invalid.

FastAPI request lifecycle POST /summariseSummariseInDepends(current_user)asyncsummariseSummariseOutResponse Invalid input→ 422Invalid token→ 401await llm_summarise
It's async-native (built on Starlette), and Depends gives clean, testable dependency injection for auth and database sessions.

async def vs def in FastAPI

async def runs on the event loop, so use it with async libraries (httpx, asyncpg). A blocking call inside it (requests.get, time.sleep) freezes every request. Plain def endpoints run in a thread pool, which is safer for blocking code.

Python questions that come up
  • List vs tuple vs set vs dict, and their lookup costs (set and dict are O(1) on average).
  • Generators and yield for streaming big data.
  • Decorators: a function wrapping a function (how @app.post works).
  • Virtual environments and pinning dependencies.
  • The GIL: threads don't run Python bytecode in parallel; use processes for CPU work, async for I/O.

"Python SDK" in the JD most likely means building or using a client library: a small package that wraps an API so others can call client.tasks.list(). Section 12 has a mini project for it.

Check yourself

1. What does Depends() give you in FastAPI?

2. Where do you see a FastAPI app's interactive API docs?

3. Why use threads less than processes for CPU-heavy Python work?

8 · Authentication, authorization and SSO

This is the part that separates candidates. Most people can say "JWT". Few can explain SSO. Learn this section well.

WordMeans
Authentication (authN)Who are you? (login)
Authorization (authZ)What may you do? (roles, permissions)
OAuth 2.0A protocol for delegated access: an app gets a token to call an API for you
OpenID Connect (OIDC)A thin layer on OAuth 2.0 that adds identity: an id_token saying who logged in. This is what "Login with Google" and most modern SSO use.
SAMLAn older XML-based SSO standard, common in large enterprises (Okta, Azure AD, ADFS)
IdPIdentity provider: Google, Okta, Azure AD/Entra ID, Keycloak
SSOLog in once at the IdP, get into many apps without logging in again
SSO login with OIDC and PKCEBrowserYour app (backend)Identity provider (SSO)1 · Log in with company SSO2 · redirect: client_id, scope=openid, state, code_challenge3 · user signs in at the IdP (password, MFA)4 · redirect back with ?code=…&state=…5 · browser delivers the code6 · back channel: code + code_verifier + secret7 · id_token (who) + access_token (what)8 · set an HttpOnly session cookieSteps 6 and 7 never touch the browser. That is what makes the code flow safe.

8.1 The flow, in words

  1. The user clicks "Log in with company SSO". Your backend creates a random state and a PKCE code_verifier, and redirects to the IdP with the SHA-256 hash of the verifier (code_challenge).
  2. The user signs in at the IdP, with MFA if required. Your app never sees the password.
  3. The IdP redirects back to your redirect_uri with a short-lived code and the same state. Check that state matches, which stops CSRF on login.
  4. Your backend exchanges the code plus the verifier (and client secret) for tokens, server to server.
  5. Verify the id_token: signature against the IdP's published keys (JWKS), iss, aud equal to your client id, expiry, and nonce.
  6. Find or create the user, then start your own session: an HttpOnly, Secure, SameSite cookie.
Sessions vs JWTServer sessionJWT (stateless token)BrowserServersession storeRedis / Postgrescookie: sidRevoke = delete the row. Costs a lookup.BrowserServerBearer eyJ…verify signatureno lookupRevoking early is hard. Keep it short-lived.
Sessions or JWT? (asked constantly)

Server sessions: the cookie holds a random id, and the server looks it up. Easy to revoke, needs a store. JWT: signed claims the server verifies without a lookup. Scales simply, but you can't revoke one early, so keep access tokens short (5–15 minutes) and use a refresh token that can be revoked and is rotated on use. For a normal web app, an HttpOnly session cookie is the safest default. Never keep tokens in localStorage if you can avoid it, because any XSS can read it.

Authorization: RBAC done properly
const can = { admin: ["task:delete", "task:write", "task:read"], member: ["task:write", "task:read"], viewer: ["task:read"] };
const requirePerm = (perm) => (req, res, next) =>
  can[req.user.role]?.includes(perm) ? next() : res.status(403).json({ error: "Forbidden" });

app.delete("/api/tasks/:id", requireAuth, requirePerm("task:delete"), deleteTask);
// AND check ownership in the query: WHERE id = $1 AND team_id = $2   (else: IDOR bug)

Always check on the server. Hiding a button in React is UX, not security. The classic bug is IDOR (insecure direct object reference): /tasks/43 belongs to another team and you return it anyway.

RBAC and team scope DELETE /api/tasks/:id Authenticated?task:delete?id AND team_idmatch? admin: deletemember: write/readviewer: read 401403 404Delete taskWHERE id = $1 AND team_id = $2

XSS vs CSRF, in one breath

XSS: attacker script runs in your page and can read anything JS can (so tokens in localStorage are exposed). Defence: escape output (React does by default; beware dangerouslySetInnerHTML), plus a Content-Security-Policy. CSRF: another site makes the browser send a request with your cookies. Defence: SameSite cookies, CSRF tokens, checking Origin.

Check yourself

1. Which is authorization, not authentication?

2. Where should a web app keep its session token, ideally?

3. What must you check when verifying an OIDC id_token?

9 · Docker, Kubernetes and LLMs

9.1 Docker

# api/Dockerfile: multi-stage, small image, non-root
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev

FROM node:20-alpine
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
USER node
EXPOSE 4000
CMD ["node", "src/server.js"]
# docker-compose.yml: the whole app with one command
services:
  db:  { image: postgres:16, environment: { POSTGRES_PASSWORD: dev }, volumes: [pgdata:/var/lib/postgresql/data] }
  api: { build: ./api, environment: { DATABASE_URL: postgres://postgres:dev@db:5432/postgres }, depends_on: [db], ports: ["4000:4000"] }
  ai:  { build: ./ai,  environment: { LLM_API_KEY: "${LLM_API_KEY}" }, ports: ["8000:8000"] }
  web: { build: ./web, ports: ["5173:80"], depends_on: [api] }
volumes: { pgdata: {} }
Image vs container, and why layer order matters

An image is a read-only template; a container is a running instance of it. Each instruction is a cached layer. Copy package.json and install before copying the source, so code changes don't reinstall dependencies.

Docker layer cache Reused cache FROM node:20-alpineCOPY package*.jsonRUN npm ci --omit=devCOPY source Source editRebuild sourcelayer Image → runningcontainer

9.2 Kubernetes, the part they ask at this level

Pod: one or more containers scheduled together. Deployment: keeps N pod replicas running and rolls out new versions. Service: a stable network name in front of changing pods. Ingress: HTTP routing from outside. ConfigMap / Secret: configuration. Liveness / readiness probes: restart a stuck pod; don't send traffic to one that isn't ready. That's enough to answer most questions honestly. Say you've used Docker Compose, and know these terms.

9.3 Generative AI in a product

  • Call the model server-side, never from the browser with your key.
  • Stream long answers (Server-Sent Events) so the UI feels fast.
  • RAG (retrieval-augmented generation): find relevant documents with embeddings and a vector index (pgvector works inside Postgres), put them in the prompt, and ask the model to answer from them.
    RAG retrieval flow DocumentsEmbeddingsVector index (pgvector) User questionFind relevant documentsPrompt + contextLLMAnswer QueryRetrieved textModel output is untrusted
  • Guard it: timeouts and retries with backoff, a per-user rate limit, a token or cost budget, validated structured output (JSON schema), and never trusting model output as code or SQL.
  • Prompt injection: text in a document can try to instruct the model. Treat model output as untrusted input. (Your tacit course is about exactly this.)

Check yourself

1. What is the difference between an image and a container?

2. A readiness probe fails on a pod. What does Kubernetes do?

3. Why should an LLM API key never be used from the browser?

10 · DSA for the online test

At this level the tests are easy-to-medium. Learn the patterns and you'll recognise most problems.

PatternSignal in the problemClassic problems
Hash map / set"find a pair", "first duplicate", "count"Two Sum, Group Anagrams, First Unique Character
Two pointerssorted array, pairs, in-placeValid Palindrome, Container With Most Water, 3Sum
Sliding window"longest/shortest subarray or substring that…"Longest Substring Without Repeating Characters, Max Sum Subarray of size K
Stackmatching, "next greater", undoValid Parentheses, Daily Temperatures
Binary searchsorted, or "minimum X such that…"Search in Rotated Array, Koko Eating Bananas
BFS / DFSgrids, trees, graphs, levelsNumber of Islands, Level Order Traversal, Clone Graph
Prefix sumsmany range-sum queriesSubarray Sum Equals K
Simple DP"number of ways", "min cost"Climbing Stairs, House Robber, Coin Change
Worked example 1 · Two Sum in O(n)
function twoSum(nums, target) {
  const seen = new Map();                    // value -> index
  for (let i = 0; i < nums.length; i++) {
    const need = target - nums[i];
    if (seen.has(need)) return [seen.get(need), i];
    seen.set(nums[i], i);
  }
  return [];
}

One pass. Say the complexity out loud: O(n) time, O(n) space.

Worked example 2 · Longest substring without repeating characters
function longestUnique(s) {
  const last = new Map();                    // char -> last index seen
  let start = 0, best = 0;
  for (let i = 0; i < s.length; i++) {
    if (last.has(s[i]) && last.get(s[i]) >= start) start = last.get(s[i]) + 1;
    last.set(s[i], i);
    best = Math.max(best, i - start + 1);
  }
  return best;                               // "abcabcbb" -> 3
}

Routine: one problem a day, timed at 25 minutes. If stuck, read the solution, close it, and re-solve from scratch the next day. Practise in the language you'll use in the test (JavaScript or Python). Also revise MCQ basics: SQL output questions, HTTP methods and status codes, OS processes vs threads, and Big-O.

Check yourself

1. Many range-sum queries on a fixed array: which pattern fits?

2. Checking whether brackets are balanced uses which structure?

3. What is the time complexity of the one-pass Two Sum with a hash map?

11 · System design, lite

Round 2 may ask for a small design. They want structured thinking, not a Netflix architecture. Use this script for any prompt:

System design answer script 1 ClarifyUsers / scale / features2 API4–6 endpoints3 Data modelTables / keys 4 ArchitectureClient → API → database5 BottlenecksFirst break at 10×6 Trade-offsOne choice and why

  1. Clarify: users, scale (requests per second, data size), must-have features.
  2. API: the 4–6 endpoints.
  3. Data model: tables and keys.
  4. Architecture: client → API → database, plus cache, queue and storage only where needed.
  5. Bottlenecks: which part breaks first at 10× load, and the fix (index, cache, read replica, queue).
  6. Trade-offs: say one thing you chose not to do and why.
Practice: design a URL shortener

API: POST /links {url} → {code}, GET /:code → 301 redirect. Table: links(code PK, url, owner_id, created_at, clicks). Code: base62 of an auto-increment id, or 7 random characters with a uniqueness check. Reads far outnumber writes, so cache hot codes in Redis. Count clicks asynchronously (a queue or a batched increment) so redirects stay fast. Add rate limiting and a malicious-URL check.

Practice: design SSO for 5 internal apps

One IdP (Keycloak or Okta) with OIDC. Each app is a client with its own redirect URI. Apps keep their own sessions; global logout uses the IdP's end-session endpoint plus short session lifetimes. Roles come from IdP groups mapped to app permissions. Talk about token verification with JWKS caching, clock skew, and what happens when the IdP is down (existing sessions keep working; new logins fail with a clear message).

Practice: a notifications service

Producers put events on a queue (Redis streams, RabbitMQ or SQS). Workers fan out to email, SMS and push providers, with retries and a dead-letter queue. Store user preferences. Make sending idempotent with an event id, so a retry doesn't send twice.

Check yourself

1. What is the first step of the system design script?

2. URL shortener: reads far outnumber writes. What helps most?

3. Why make notification sending idempotent with an event id?

12 · Projects

12.1 The capstone: TeamBoard

One project that covers every line of the JD. Build it across the eight weeks, one milestone per week, and deploy it (Render, Railway or a small VPS). Put the live link at the top of your resume.

WeekMilestoneDone when
1Repo, TypeScript, lint, prettier, CI on GitHub ActionsA PR runs lint and tests automatically
2React + TS frontend: task list, add, toggle, filter, with TanStack QueryWorks against a mock API; loading and error states shown
3Express API: tasks CRUD with zod validation and a central error handlerSupertest tests pass; correct status codes
4PostgreSQL: schema from 6.1, migrations, an index proven with EXPLAINA dashboard query under 50 ms on 100k seeded rows
5FastAPI AI service: /summarise a team's week of tasks with an LLMPydantic models, 401 without a token, a timeout handled cleanly
6SSO: OIDC login with Google or a local Keycloak, HttpOnly session, RBAC (admin, member, viewer)A viewer gets 403 on delete; another team's task gets 404
7Docker Compose for all four services; a README with an architecture diagramA stranger runs docker compose up and it works
8Polish: a demo video, seed data, a "decisions" section in the READMEYou can explain every decision in 2 minutes
Week 6 hint: OIDC in Express with openid-client
// npm i openid-client express-session   (API shape varies by version: check its README)
import * as client from "openid-client";

const config = await client.discovery(new URL(process.env.OIDC_ISSUER), process.env.OIDC_CLIENT_ID, process.env.OIDC_CLIENT_SECRET);

app.get("/auth/login", async (req, res) => {
  const verifier = client.randomPKCECodeVerifier();
  req.session.pkce = verifier;
  req.session.state = client.randomState();
  const url = client.buildAuthorizationUrl(config, {
    redirect_uri: process.env.OIDC_REDIRECT_URI, scope: "openid email profile",
    code_challenge: await client.calculatePKCECodeChallenge(verifier), code_challenge_method: "S256",
    state: req.session.state,
  });
  res.redirect(url.href);
});

app.get("/auth/callback", async (req, res) => {
  const tokens = await client.authorizationCodeGrant(config, new URL(req.originalUrl, process.env.APP_URL), {
    pkceCodeVerifier: req.session.pkce, expectedState: req.session.state,
  });
  const claims = tokens.claims();                    // verified id_token: sub, email, ...
  req.session.user = await upsertUser(claims.sub, claims.email);
  res.redirect("/");
});

Library APIs change between major versions, so treat this as the shape of the solution and follow the library's current README. What matters in the interview is that you can explain each line against the flow in section 8.1.

12.2 Small projects (one evening each)

  1. Rate limiter middleware: a token bucket per IP in memory, then in Redis. Return 429 with Retry-After. Check: 10 rapid requests with a limit of 5 give five 200s and five 429s.
  2. Typed fetch client: api.get<Task[]>("/tasks") with timeouts, retries with backoff, and typed errors. Check: a 500 retries three times, then throws a typed error.
  3. A Python SDK for your API: a teamboard package with Client(token).tasks.list(), published to TestPyPI. That covers the "Python SDK" line. Check: pip install from TestPyPI works in a fresh venv.
  4. Streaming chat endpoint: FastAPI StreamingResponse or SSE from Express, rendered word by word in React. Check: the first words appear before the full answer is ready.
  5. Query clinic: seed 1M rows, find three slow queries, fix each with an index or rewrite, and write up before/after EXPLAIN output. Great interview story.

13 · Every round, rehearsed

13.1 The two-minute project story

You'll be asked "walk me through a project" in every technical round. Practise this structure until it's natural:

  1. What it is and who uses it (one line).
  2. The architecture (draw it: React → Express → Postgres, plus FastAPI and the LLM).
  3. The hardest problem and how you solved it.
  4. One number (users, latency, time saved).
  5. What you'd improve next.

13.2 Behavioural questions, STAR answers

Situation, Task, Action (what you did), Result (with a number). Prepare one story for each:

STAR answer structure S · SituationT · TaskA · ActionR · Result What happened?What was your job?What did you do?What changed?Include a number

  • A bug in production you fixed under pressure.
  • A disagreement with a teammate or reviewer.
  • A time you missed a deadline, and what you changed afterwards.
  • Something you learned fast for a project.
  • Feedback you received and acted on.
Example STAR answer

S: Our multiplayer game started refusing every player after a deploy. T: I owned the game service and had to restore it during peak hours. A: I checked logs, found the token check was calling the wrong main-API URL from a stale environment variable, fixed the config, added a startup check that fails loudly on a mismatch, and documented it. R: Restored in about 20 minutes, and that class of outage hasn't happened again.

13.3 HR questions with good answers

Why are you leaving / looking for a change?

Positive and forward-looking: "I want to work on a larger product with a team, especially on SSO and AI features like this role describes." Never criticise a current employer.

What is your expected CTC?

"Based on the role and my experience with React, Node, Postgres and FastAPI, I'm looking at ₹X–Y LPA. I'm flexible for the right role." Have your current CTC and its breakup ready if asked.

Where do you see yourself in 3 years?

"Owning major features end to end, mentoring juniors, and going deeper into backend and AI systems."

Do you have questions for us?

Always yes: "What would success look like in my first 90 days?" "How do you do code reviews?" "Which auth provider do you use for SSO?" (That last one shows you read the JD.)

13.4 Mock interview schedule (week 8)

DayMock
1Timed online test: 20 MCQs + 2 coding problems in 90 minutes
2Tech round 1: a friend (or an AI) asks section 3–5 questions; you answer aloud, recorded
3Live coding: build the debounced search component in 30 minutes, screen shared
4Tech round 2: project deep dive + design the SSO system from 11
5Behavioural + HR: all 5 STAR stories and the 4 HR questions
6–7Watch your recordings, fix the weakest answers, redo that mock

About Angular and Java. The JD lists them as "preferred". If asked, say honestly that your frontend is React and that you can pick up Angular quickly because the concepts (components, services, dependency injection, RxJS observables) map to things you know. Don't claim experience you don't have; interviewers probe it.

Check yourself

1. In STAR, what should the Action part focus on?

2. Asked why you are leaving your job, what works best?

3. The JD lists Angular as preferred and you only know React. What should you say?

14 · Quiz

Answers are shuffled and of similar length, so you have to know them. Retake it each Sunday of the plan.

0 / 0 answered

Reference shelf

TopicSource
JavaScriptjavascript.info (event loop, closures, promises); MDN Web Docs
TypeScriptTypeScript Handbook
Reactreact.dev/learn, especially "You Might Not Need an Effect"
Node + Expressnodejs.org/en/learn; Express error handling
PostgreSQLPostgres tutorial; Use The Index, Luke
MongoDBMongoDB data modeling
FastAPIFastAPI tutorial
Auth and SSOHow OpenID Connect works; OWASP Cheat Sheets (Authentication, Session Management, CSRF); Keycloak getting started
Docker / K8sDocker get started; Kubernetes basics
DSA practiceLeetCode "Top Interview 150"; NeetCode roadmap
Security basicsOWASP Top 10