> cs·fundamentals
interview 0% 26m read
7.3 ★ core [B][J] 10 interview Q's

Backend for frontend devs

The server-side vocabulary a UI dev needs to direct an AI to build a real backend — runtimes, endpoints, data, auth, jobs — and where each piece lives.

You own the UI. Now the app needs to save things, log people in, and talk to other services — that’s the backend. This is the server-side vocabulary a frontend dev needs to direct an AI to build a real backend, and to know where each piece lives. (Databases get the deep treatment in Module 3; auth in 2.3.)

CRUD is the whole shape

Almost every backend is the same four operations on data — Create, Read, Update, Delete — each with a natural HTTP method and SQL statement. Learn the mapping once and most endpoints write themselves. When you ask an AI for “an endpoint to archive a todo,” what you’re really asking for is an Update — a PATCH — and naming it that way gets you the right verb and status code on the first try.

A table aligning Create/Read/Update/Delete with HTTP methods POST/GET/PUT-PATCH/DELETE and SQL INSERT/SELECT/UPDATE/DELETE.operationHTTPSQLCreatePOSTINSERTReadGETSELECTUpdatePUT / PATCHUPDATEDeleteDELETEDELETE
FIG 1 · CRUD ↔ HTTP ↔ SQL The four operations line up across the stack. Name the row and the AI can fill the column.

One endpoint, end to end — both sides

A request arrives at a route, a handler validates it, the ORM writes a row, and a response goes back. Here’s the server side and the client side of the very same call, so you can see how they meet.

POST /api/todos — the server handler and the fetch that calls it
// SERVER — the handler behind POST /api/todos
app.post("/api/todos", async (req, res) => {
  const user = await requireAuth(req);            // authN: who is this?
  const { title } = req.body;
  if (!title?.trim()) {
    return res.status(400).json({ error: "title required" });
  }
  const todo = await db.todo.create({             // ORM writes the row
    data: { title, userId: user.id },
  });
  res.status(201).json(todo);                      // 201 Created + the new row
});
// CLIENT — the browser fetch that hits it
const res = await fetch("/api/todos", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ title: "Buy milk" }),
});
const todo = await res.json();                     // { id, title, userId, ... }

Note the server order: authenticate → validate input → do the work → respond with the right status code. That shape repeats for every write endpoint you’ll ever build. And note what the client sends: a method, a Content-Type header, and a JSON body — the exact three things the handler reads back out.

Four boxes left to right: API route, handler, ORM, Database, with arrows between them.API routehandlerORMDatabase
FIG 2 · through the backend The request flows route → handler → ORM → database, and the row flows back.

Why an ORM instead of SQL strings

You can write "SELECT * FROM todos WHERE id = " + id — and you’ll have just opened the door to a SQL-injection attack and a typo-prone mess. An ORM lets you write db.todo.findUnique({ where: { id } }): it builds safe, parameterized SQL for you, maps rows to typed objects, and gives the AI a schema it can autocomplete against. You still need to understand SQL (Module 3) — the ORM just stops you hand-gluing it.

Where does the backend live?

“Backend” isn’t one shape. Pick how it runs based on the workload — and know that the same CRUD code can usually run in any of these.

StyleWhat it isReach for it whenWatch out
Serverless functioncode that spins up per requestspiky traffic, simple APIs, quick startcold starts; no long-running work
Long-running serveran always-on process (Express, Fastify)websockets, background work, steady trafficyou manage scaling + uptime
Managed platform / BaaShosted DB + auth + APIs (Supabase, Firebase)you want a backend now, small teamlock-in; less control
Same CRUD, different runtime. Match it to the workload, not the hype.

The split that trips people up: serverless functions don’t stay running between requests. That’s great for spiky traffic (you pay per call, it scales to zero) but it means there’s no place to keep a websocket open or run a long job — for those you want an always-on server. Which brings us to the work that can’t fit in a request.

Background jobs, queues, and where the DB sits

Some work is too slow to finish before you respond: sending a welcome email, generating a thumbnail, charging a card. You don’t want the user’s request hanging for ten seconds. The pattern is a queue: the handler drops a job on the queue and responds immediately; a separate worker picks it up and does the slow work later.

A request hits an API which responds fast and enqueues a job; a worker later pulls the job from the queue and does slow work, both touching the database.API handlerfast responsequeueworkerDatabaseenqueuelater
FIG 3 · request vs background work Fast work happens in the request; slow work is queued and a worker handles it after the response is already sent.

The database sits behind the API, reachable by both the handler and the worker — but never by the browser directly (see 7.1). Its connection string is a secret, held in an env var, and it’s the one component whose data has to survive everything else restarting.

01 Learning objectives

0 / 6 done

02 Curated reading

03 Knowledge check

knowledge check4 questions · pass ≥ 70%
  1. 01easy

    In CRUD, “Create” maps to which HTTP method?

  2. 02easy

    What does an ORM do?

  3. 03medium

    Authentication vs authorization:

  4. 04medium

    A validation check in the frontend is enough to keep an endpoint secure.

04 Interview questions

browse all ↗

What gets asked on this topic — tap a card for how to approach it, the follow-ups, and the trap. Company tags are best-effort & sourced.

  • Commonly asked junior concept very common What can server-side code do that browser code can't?

    The server runs on a machine you control, so it can hold secrets (API keys, DB credentials) the user never sees, reach the database directly, touch the filesystem, and call other services with trusted credentials.

    Browser code is shipped to and runs on the user's machine — it's fully visible and editable by anyone, so it can't be trusted to keep secrets or enforce rules. Any check that matters (auth, pricing, permissions) must happen server-side.

    Red flag Putting an authorization check only in the frontend. The user can bypass it; the server must re-check everything.

    source: MDN — Server-side programming: first steps ↗
  • Commonly asked junior concept very common Map CRUD to HTTP methods. Which methods are idempotent?

    CRUD ↔ HTTP: Create → POST, Read → GET, Update → PUT/PATCH, Delete → DELETE.

    Idempotent means calling it N times has the same effect as calling it once. GET, PUT, and DELETE are idempotent; POST is not (two POSTs create two records). PATCH is generally not guaranteed idempotent. This matters for retries: it's safe to retry a GET or PUT after a timeout, but retrying a POST may double-charge or double-create.

    Red flag Saying GET is idempotent because 'it doesn't change anything' — that's safety. Idempotency is about repeated calls having one effect (a correct GET is also safe, but the concepts differ).

    source: InterviewBit — REST API Interview Questions ↗
  • Commonly asked junior concept common What is an ORM, and why do people warn against hand-concatenating SQL strings?

    An ORM (Object-Relational Mapper, e.g. Prisma) lets you work with database rows as objects in your language instead of writing raw SQL — it generates the SQL for you and maps results back to typed objects.

    Hand-concatenating SQL from user input invites SQL injection: if you build "SELECT * FROM users WHERE name = '" + input + "'", a crafted input can break out of the string and run arbitrary SQL. ORMs (and parameterized queries) bind values separately from the query text, so input can never become executable SQL.

    Red flag Thinking an ORM is required for safety — the real fix is parameterized queries; an ORM is one convenient way to get them.

    source: Prisma — What is an ORM? ↗
  • Commonly asked junior concept very common Authentication vs authorization — what's the difference?

    Authentication is proving who you are (login, a token, a session). Authorization is what you're allowed to do once you're known (can this user delete that post?).

    Mnemonic: authentication is the bouncer checking your ID at the door; authorization is the rule about which rooms your ticket lets you into. You authenticate once, then authorize every sensitive action.

    Red flag Using the words interchangeably. A logged-in user (authenticated) still must be authorized per action; conflating them leads to privilege bugs.

    source: MDN — Server-side programming: first steps ↗
  • Commonly asked mid concept occasional Serverless vs a long-running server — when does each fit?

    Serverless (functions that spin up per request) shines for spiky, event-driven, or low-traffic workloads: you pay per invocation and scale to zero, but each call can have a cold start and there's no in-memory state between calls. A long-running server fits steady traffic, long-lived connections (websockets), background work, and cases where keeping things warm in memory matters.

    Orientation-level takeaway: serverless trades always-on cost and statefulness for automatic scaling and pay-per-use.

    Red flag Assuming serverless is always cheaper — at sustained high traffic a long-running server is often cheaper and lower-latency.

    source: MDN — Server-side programming: first steps ↗
  • ★ must-know Commonly asked junior concept very common Sketch a REST API for a 'notes' resource. What does the full set of CRUD endpoints look like?

    REST organizes the API around a resource (notes) and uses HTTP methods for the verbs. The standard set: GET /notes (list), POST /notes (create), GET /notes/:id (read one), PUT/PATCH /notes/:id (update), DELETE /notes/:id (delete).

    The pattern that makes it 'RESTful': the noun lives in the URL (the resource) and the verb lives in the HTTP method — never POST /createNote or GET /deleteNote/1. The same path /notes/:id serves read, update, and delete by varying the method. That convention is why anyone can guess your API once they know the resource.

    What a strong answer covers
    • Resource in the URL (/notes), action in the HTTP method.

    • List GET /notes, create POST /notes, read GET /notes/:id, update PUT/PATCH /notes/:id, delete DELETE /notes/:id.

    • Avoid verbs in the path (/createNote, /deleteNote/1) — that's the anti-pattern.

    • Predictable: knowing the resource lets a caller guess the endpoints.

    Quick self-check

    Which is the RESTful way to delete the note with id 42?

    Red flag Putting the verb in the URL (`GET /getNotes`, `POST /deleteNote`) — it breaks the REST convention and the method↔CRUD mapping.

    source: MDN — HTTP request methods ↗
  • Commonly asked mid concept common What's the difference between a session and a JWT for keeping a user logged in?

    Both answer 'how does the server know it's still you on the next request'. A session stores the auth state on the server (a session record) and gives the browser an opaque session ID (usually in a cookie); the server looks it up each request — easy to revoke, but it's stateful. A JWT is a signed token the server hands back that *contains* the claims (user id, expiry); the server just verifies the signature, no lookup needed — stateless and scalable, but hard to revoke before it expires.

    Tradeoff in one line: sessions are easy to invalidate but require server state; JWTs are stateless and scale well but you can't easily 'log someone out' until the token expires.

    What a strong answer covers
    • Session: state lives server-side; browser holds an opaque ID; easy to revoke, but stateful.

    • JWT: signed token carries the claims; server verifies signature, no lookup; stateless.

    • JWT scales well (no shared session store) but is hard to revoke before expiry.

    • Both typically ride in a cookie or Authorization header on each request.

    Red flag Storing sensitive data in a JWT thinking it's hidden — a JWT is signed, not encrypted; its payload is readable by anyone who has the token.

    source: MDN — HTTP authentication ↗
  • Commonly asked junior concept very common Why validate input on the server even when the frontend already validates the same form?

    Frontend validation is a UX feature — it gives instant feedback so users fix mistakes fast — but it provides zero security, because the client is fully under the user's control. Anyone can bypass the form entirely and POST raw data with cURL, disabled JavaScript, or DevTools.

    So the server must re-validate everything it receives as if no frontend existed: required fields, types, ranges, formats, and authorization. The two layers aren't redundant — they serve different jobs: the frontend for friendliness, the server for trust. Skipping server validation is how malformed and malicious data reaches your database.

    What a strong answer covers
    • Frontend validation = UX (fast feedback); it is not security.

    • The client is user-controlled — attackers bypass the form and POST directly.

    • The server must re-validate every request as if no frontend existed.

    • Both layers coexist: friendliness on the client, trust on the server.

    Quick self-check

    Your signup form checks the email format in JavaScript before submitting. Is server-side email validation still needed?

    Red flag Trusting client-side validation as a security boundary — it's trivially bypassed; the server is the only place validation actually protects you.

    source: MDN — Form data validation ↗
  • Commonly asked mid concept occasional Why move slow work (sending email, resizing an image, calling a slow API) to a background job instead of doing it in the request?

    An HTTP request should return fast. If you do slow work inline — sending a welcome email, resizing an upload, calling a slow third-party API — the user waits the whole time, the request may time out, and a failure in that work fails the whole request.

    The pattern is to enqueue the slow work and return immediately: accept the request, push a job onto a queue, respond '202 accepted / we're on it', and let a separate worker process the job later (with retries on failure). The user gets a snappy response; the slow, flaky, or retryable work happens out of band where a failure doesn't break the user's request.

    What a strong answer covers
    • Requests should be fast; slow inline work blocks the user and risks timeouts.

    • Enqueue the work, respond immediately, let a separate worker process it.

    • Background jobs can retry on failure without re-running the user's request.

    • Good fits: email, image/video processing, slow external API calls, report generation.

    Red flag Doing slow/flaky work inline in the request handler — one slow third-party call makes every user wait and turns a transient failure into a failed request.

    source: MDN — Server-side programming: first steps ↗
  • Commonly asked mid concept common What is CORS, and why does the browser block your frontend from calling an API on a different origin?

    CORS (Cross-Origin Resource Sharing) is a browser security mechanism. By default the same-origin policy stops JavaScript on app.example.com from reading responses from a different origin (different scheme, host, or port) — this prevents a malicious page from quietly calling APIs as you. CORS is the *opt-in* by which a server says 'these specific other origins are allowed', via Access-Control-Allow-Origin and related response headers.

    Key nuance for builders: CORS is enforced by the browser, on the server's say-so. A CORS error isn't your frontend misbehaving — it means the API you're calling hasn't allow-listed your origin. The fix is on the server (or a proxy), not in the browser.

    What a strong answer covers
    • Same-origin policy blocks cross-origin reads by default; CORS is the server's opt-in to relax it.

    • Enforced by the browser, but configured via the server's response headers.

    • Access-Control-Allow-Origin names which origins may read the response.

    • A CORS error means the target server hasn't allowed your origin — fix it server-side.

    Red flag Trying to 'fix CORS' in the frontend code — the browser enforces it from the server's headers; the change must happen on the API or via a backend proxy.

    source: MDN — Cross-Origin Resource Sharing (CORS) ↗