Cheat sheet · Full-Stack Interview Questions for 4+ Years
Full-Stack Questions cheat sheet
The facts a 4+ year full-stack interview probes: current versions, comparison tables, traps and short model answers. Versions used in the lessons: Tailwind 4, TypeScript 7, React 19, Next.js 16, Express 5, Prisma 7.
blogs · jp-blogs.vercel.app/cheatsheets/series/fullstack-qa
HTML and CSSLesson 1 →
| Attribute | Behaviour |
|---|---|
| defer | Parallel download, runs after parsing, in order |
| async | Runs on arrival, no fixed order (analytics) |
| preload / preconnect | Fetch a known file now / open the connection early |
| fetchpriority="high" | Eager hint for the one LCP image |
- Cascade: importance and layers, then specificity (IDs, classes, types), then order. Unlayered CSS beats layers.
:where()scores zero;:is()takes its strongest argument.border-boxmakes width include padding and border.- Flex item overflows:
min-width: 0. Margin collapse is vertical only;display: flow-rootstops it.
Watch out: z-index only works inside a stacking context, and opacity or transform on a parent traps it. Sticky fails under overflow: hidden.
Tailwind CSS v4Lesson 2 →
| v3 habit | v4 |
|---|---|
@tailwind base; ... | @import "tailwindcss"; |
tailwind.config.js and content | @theme { } in CSS; automatic detection |
darkMode: 'class' | @custom-variant dark (...) |
bg-[--brand] | bg-(--brand) |
shadow-sm, outline-none, ring | shadow-xs, outline-hidden, ring-3 |
- Tailwind reads files as plain text: write full class names.
bg-${color}-500is never generated. cn()=clsx(join, skip falsy) +twMerge(last conflicting class wins).cvabuilds variant props.@theme inlinewhen a token points at another variable.hover:only fires on hover-capable devices.
TypeScript 6 and 7Lesson 3 →
| Old belief | Now |
|---|---|
strict is off by default | On by default |
@types/* load automatically | types is empty: add "types": ["node"] |
baseUrl with paths | baseUrl removed; put the prefix in paths |
moduleResolution: node | Removed: bundler or nodenext |
- noUncheckedIndexedAccess
arr[0]isT | undefined- exactOptionalPropertyTypes
x?: Tmeans missing, notundefined- verbatimModuleSyntax
- Needs
import type(error TS1484) - erasableSyntaxOnly
- No
enumor parameter properties
Tip: unknown = check before use, any = skip checks, never = impossible. Prefer an as const object plus union over enum.
React 19 reviewsLesson 4 →
- useEffect(() => setX(f(y)), [y])
- Derived state: compute during render
- fetch in effect, no cleanup
- Race:
AbortControlleror a data library - Context value `{{ a, b }}` inline
- New object every render: memoize or split
- `key={i}` on a changing list
- State moves to the wrong row: stable id
- `{n && <A/>}` with `n = 0`
- Renders
0: usen > 0 && - `items.sort()` on state
- Mutates:
[...items].sort()ortoSorted()
| Feature | Version |
|---|---|
use, Actions, useActionState, useOptimistic | 19.0 |
ref as a prop; Context as its own provider | 19.0 |
Activity, useEffectEvent | 19.2 |
Watch out: useFormStatus only sees a parent form element. Actions reset uncontrolled fields: return the submitted values.
Next.js 16Lesson 5 →
- Server Components by default. A Client Component also runs on the server when prerendering.
params,searchParams,cookies()are Promises: await them.middleware.tsis nowproxy.ts(Node runtime).- Turbopack is the default. Caching is opt-in with
use cache;fetchis not cached by default.
| Function | Use |
|---|---|
| updateTag(t) | Server Actions only. Expire now (read your own writes) |
| revalidateTag(t, "max") | Mark stale; serve old while rebuilding |
| revalidatePath(p) | One route, less precise |
| refresh() | Re-fetch the current route payload |
export async function getProducts() {
"use cache";
cacheLife("hours");
cacheTag("products");
return db.product.findMany();
}Watch out: A Server Action is a public POST. Authenticate, validate, and authorize inside it. NEXT_PUBLIC_ values are frozen at build.
Node.js and Express 5Lesson 6 →
- Order: sync code,
nextTick, promises, timers, I/O,setImmediate. In ES modules, promises run beforenextTick. - The libuv pool has 4 threads by default (
fs,dns.lookup,zlib, some crypto). Network I/O does not use it. - Respect
write()returningfalse, or usepipeline().
| Tool | Use it for |
|---|---|
| worker_threads | CPU-heavy work in one process |
| cluster | All cores on one machine, shared port |
| child_process | Other programs (ffmpeg, Python) |
| Express 4 | Express 5 |
|---|---|
Async errors need .catch(next) | Rejections reach the error handler |
/* | /*splat (array in req.params) |
req.body is {} without parser | undefined |
MongoDBLesson 7 →
| Embed when | Reference when |
|---|---|
| Read together, small, bounded list | Child read alone, or shared by many parents |
| Child changes only with the parent | Child changes often; list can grow forever |
- One document is always atomic. Limit is 16 MiB, so unbounded arrays are the top anti-pattern.
- Compound index order: Equality, Sort, Range. TTL index needs a date; it runs about every 60 s.
- Only one text index per collection. Multikey: at most one array field per compound index.
- ObjectId is 12 bytes (time, random, counter). Never compare it with a plain string.
Tip: Use w: "majority" for important writes. Transactions are for multi-document work, not one order with embedded items.
Prisma 7 and SQL trapsLesson 8 →
| Command | Where |
|---|---|
| migrate dev | Local only; shadow database |
| migrate deploy | CI and production; never resets |
| db push | Prototypes; no migration file |
| migrate resolve | Fix history without running SQL |
- In v7,
migrate devno longer runsgenerateor seed. The URL lives inprisma.config.ts. - Sequential
$transaction([...])vs interactiveasync (tx): only the second can use earlier results. includeruns 2 queries by default;relationJoinsmakes it 1.- Use tagged
$queryRaw.$queryRawUnsafewith joined strings is an injection hole.
Watch out: SQL traps: NOT IN with NULL, LEFT JOIN plus WHERE, join fan-out, window frames. Set a pool timeout.
GraphQL and HasuraLesson 9 →
| Aspect | Hasura v2 | Hasura v3 (DDN) |
|---|---|---|
| Engine | Haskell, talks to the DB | Rust, via data connectors |
| Config | Metadata, hasura CLI | OpenDD, ddn CLI |
| Schema | One per role | One built schema, role annotations |
- Hasura compiles GraphQL to one SQL statement, so DB relations avoid N+1. Index your columns.
- Permissions per role, table and operation use session variables (JWT claims or webhook).
- Event triggers are at least once; retries are off by default. Make handlers idempotent.
- Plain GraphQL: per-request DataLoader,
!bubbles to the nearest nullable parent, cursors beat offset on deep pages.
Watch out: Community Edition (v2) has no API limits, @cached or per-role introspection control.
LLMs, agents and RAGLesson 10 →
- Workflow
- Code decides the path (chain, route, parallel, evaluator)
- Agent
- Model uses tools in a loop with a stop rule
- MCP tools
- Model calls them (
get_order) - MCP resources
- App reads them (URI)
- MCP prompts
- User picks them (slash command)
| RAG symptom | Fix |
|---|---|
| Right doc exists, not retrieved | Hybrid search, better chunks, reranker |
| Model ignores good context | Fewer, better chunks; rerank |
| Confident with no source | Score threshold and a refuse path |
| Leaks private data | Filter by ACL inside the query |
Tip: Prompt first, RAG for your changing data, fine-tune for style. Measure recall@k, MRR, faithfulness. AI SDK: set stopWhen or the loop stops after one step.
Redis cachingLesson 18 →
| Pattern | Write path |
|---|---|
| Cache-aside | Write the DB, then delete the key |
| Write-through | Cache and DB together; both agree |
| Write-behind | Cache first, DB later; can lose writes |
- Hash
- A session or user's fields
- Sorted set
- Leaderboard, delayed jobs, rate window
- Stream
- Reliable queue with consumer groups
- HyperLogLog
- Approximate unique count
- Stampede: single-flight,
SET k v NX PX, early refresh, or serve stale. Always a TTL with jitter. - RDB = snapshot; AOF = log (
everysecloses about 1 s). Neither is a backup. - Default eviction is
noeviction. Caches wantallkeys-lruorallkeys-lfu.
Queues and webhooksLesson 19 →
| Delivery | Meaning |
|---|---|
| At-most-once | May be lost, never repeated |
| At-least-once | Never lost, may repeat |
| Exactly-once | Only special closed setups |
- Build at-least-once plus idempotent handlers. Retries: attempts, backoff with jitter, dead-letter queue.
- Outbox: event row and business row in one transaction, then a relay publishes.
- Webhooks: raw body, HMAC with constant-time compare, timestamp check, fast
2xx, dedupe by event id.
| Trait | RabbitMQ | Kafka | SQS |
|---|---|---|---|
| Replay | No | Yes (offset) | No |
| Order | Per queue | Per partition | FIFO group |
Auth: OAuth, OIDC, sessionsLesson 22 →
| Token | Purpose | Keep it |
|---|---|---|
| Access | Call an API; minutes | Server side |
| Refresh | Get new access tokens | Server only |
| ID (JWT) | Who logged in; read once | Not stored |
- `state`
- Stops login CSRF
- PKCE verifier
- Stops code theft
- `nonce`
- Stops ID token replay
- OAuth = what may this app do; OIDC = who is this user. Use code flow with PKCE; no implicit or password grant.
- Database sessions revoke at once. JWT sessions cannot before expiry.
- Link accounts only on a verified email. Passkeys are phishing-resistant; TOTP is phishable.
Watch out: proxy.ts is an optimistic check only. Check again in the data layer, every action, and inside the query.
TestingLesson 21 →
| Double | Real code? | Use |
|---|---|---|
| Stub | No | Fixed answer |
| Spy | Yes | Check a call happened |
| Mock | No | Check how it was called |
| Fake | Simple version | Replace a database or queue |
- getBy...
- Throws if missing: must be there
- queryBy...
- Returns
null: assert it is absent - findBy...
- Waits: it appears soon
- Vitest globals are off;
vi.mockis hoisted (vi.hoisted). MSW fakes the network, notfetch. - Playwright: use retrying
expect, neverwaitForTimeout. Test the DB with the real engine. - Coverage shows untested code, not tested behaviour.
Deploy, Docker and opsLesson 26 →
| Strategy | Trade-off |
|---|---|
| Rolling | Cheap; old and new run together |
| Blue-green | Instant rollback; double capacity |
| Canary | Few users hurt; needs metrics |
| Recreate | Simple; downtime |
- Dockerfile: multi-stage, copy the lockfile first,
npm ci,.dockerignore, non-rootUSER, startnodedirectly. - Migrations: expand then contract. Never drop or rename in the same release.
- Logs say what, metrics say that something is wrong, traces say where.
| SLO | Downtime in 30 days |
|---|---|
| 99.9% | 43.2 minutes |
| 99.99% | 4.3 minutes |
Interview one-linersLesson 24 →
| Question | Short answer |
|---|---|
| Merge vs rebase? | Merge keeps history. Rebase rewrites it. Never rebase shared commits. |
| reset vs revert? | reset moves the branch. revert adds an undo commit (safe if pushed). |
| Safe force push? | --force-with-lease, plus --force-if-includes. |
| Trunk-based vs Gitflow? | Trunk with flags for web apps. Gitflow for parallel versions. |
^1.2.3 vs ~1.2.3? | <2.0.0 vs <1.3.0. The lockfile is the truth; use npm ci. |
| Password hashing? | Argon2id (OWASP e.g. m=19456, t=2, p=1). bcrypt cuts input at 72 bytes. |
| SSRF defence? | Allow http(s), check resolved IPs, no redirects, isolate. |
| SSE or WebSocket? | SSE server to browser; WebSocket two-way. Add heartbeat. |
A cheat sheet is a reminder, not a teacher. Each card links to the lesson that explains it step by step.