model-mid-questions
Section 1: Frameworks, Libraries & Abstraction (Pages 1–3, PDF
1)
Q1: What is a framework, and how does it differ from a library in terms of control flow?
A: A framework is an opinionated, reusable foundation that dictates the structure of your
application. The key difference is inversion of control: with a library, you call it when you
need it; with a framework, it calls your code—you fill in the blanks. This is the “Hollywood
principle”: “Don’t call us, we’ll call you.”
Q2: Give two reasons why developers choose to use a framework.
A:
1. Speed & conventions: Pre-built patterns and boilerplate reduce development time.
2. Enforced architecture: The framework imposes a consistent structure, making
codebases easier to maintain and understand across a team.
Q3: Explain the trade-off involved in using a framework.
A: You gain development speed and structural guidance but give up some low-level control
and flexibility. Understanding what the framework does underneath remains essential; it is
not a substitute for system-level knowledge.
Q4: List the four rungs on the “abstraction ladder” from basic code reuse to full-stack
structure.
A: Functions → Modules → Libraries → Frameworks.
Q5: Using the house-building analogy, how do functions, libraries, and frameworks
compare?
A: Functions are individual tools (hammer, saw). Libraries are toolboxes for a specific trade
(electrical kit, plumbing kit). A framework is a complete construction blueprint with pre-built
scaffolding that tells you where every part belongs.
Q6: On the backend framework spectrum from maximum control to maximum structure,
where do Express, Fastify, NestJS, and [Link] sit?
A: Raw [Link] gives maximum control; Express adds routing convenience; Fastify is similar
but with better performance; NestJS enforces a modular, decorator-based architecture;
[Link] is a full-stack React framework that also defines data-fetching and routing patterns,
giving the most structure of the four.
Section 2: End-to-End Data Flow (Pages 3–4, PDF 1)
Q7: List the seven layers a request passes through from client to database and back.
A: Client → DNS + TLS → Proxy/Gateway → API Server → Service Layer → ORM →
Database. The response travels back up through every layer in reverse order.
Q8: What is the responsibility of the proxy/gateway layer?
A: It sits in front of your services and handles cross-cutting concerns like load balancing,
SSL termination, rate limiting, caching, and routing. Examples: Nginx, AWS API Gateway,
Kong.
Q9: Why should the service layer be deliberately kept free of HTTP concerns like req and
res objects?
A: Keeping it free of HTTP makes the business logic reusable across different entry points
(e.g., REST APIs, GraphQL, background jobs) and easier to test. It also enforces a clean
separation of concerns.
Q10: What is an ORM, and why is it not a database?
A: An ORM (Object-Relational Mapper) is a translation layer that maps between objects in
code and tables in a relational database. The actual storage and query execution still
happen in a database engine (PostgreSQL, MySQL, etc.). The ORM generates SQL queries
but does not store data itself.
Section 3: Types of Databases (Pages 4–5, PDF 1)
Q11: What is the rule of thumb for choosing between a relational (SQL) and a NoSQL
database?
A: Start with a relational database (e.g., PostgreSQL) unless you have a specific, clear
reason not to—such as highly scalable, schema-flexible requirements. Do not choose
NoSQL just because it is trendy.
Q12: Name the five subtypes of NoSQL databases and give one purpose or example for
each.
A:
Document: JSON-like documents, self-contained; MongoDB, Firestore.
Key-value: Simple key-to-value mapping, extremely fast; caching/sessions; Redis,
DynamoDB.
Wide-column: Rows can have varying columns, built for massive write throughput;
Cassandra, HBase.
Graph: Nodes and edges model relationships natively; social networks, recommendation
engines; Neo4j.
Search engine: Optimized for full-text search and ranking; Elasticsearch, Meilisearch.
Section 4: UI Systems (Pages 5–6, PDF 1)
Q13: Distinguish between a UI library, a component registry, and a design system.
A:
UI Library: A pre-built set of interactive components (buttons, modals) installed as a
package dependency; handles behaviour and accessibility.
Component Registry: A catalog of components you copy into your codebase; you own
the code afterward, no ongoing package dependency (e.g., shadcn/ui).
Design System: A holistic set of principles, tokens, and guidelines (typography, colours,
spacing) that ensures visual consistency across an entire product; components are just
one part.
Q14: How does shadcn/ui illustrate the difference between a library and a registry?
A: shadcn/ui operates as a registry. When you run npx shadcn-ui add button , it copies
the component source directly into your project. You then own and can modify that code. It is
not a library you install as a dependency that remains in node_modules .
Section 5: Encryption, Hashing & JWT (Pages 6–8, PDF 1)
Q15: What is the fundamental difference between encryption and hashing?
A: Encryption is a reversible transformation using a key; it provides confidentiality. Hashing
is a one-way function; it cannot be reversed and is used for integrity and password
verification, not secrecy.
Q16: Explain symmetric vs. asymmetric encryption and how they are used together in TLS.
A:
Symmetric: Same key encrypts and decrypts; fast (AES-256). Problem: secure key
exchange.
Asymmetric: Public/private key pair; public key encrypts, private key decrypts. Solves
the key-exchange problem but is slower.
In TLS, asymmetric encryption is used to securely exchange a symmetric session key.
The rest of the session then uses symmetric encryption for speed.
Q17: Why are fast hashing algorithms like MD5 or SHA-256 unsuitable for password
storage?
A: They are too fast, making brute-force and rainbow-table attacks cheap. Password
hashing should be deliberately slow to raise the cost of cracking. Use algorithms like bcrypt,
argon2, or scrypt.
Q18: Walk through the steps of secure password hashing and login verification.
A:
Hashing:
1. Generate a random salt.
2. Run a slow hash (bcrypt/argon2) over password + salt .
3. Store the salt and resulting hash together; never store the plain password.
Login:
1. Retrieve the stored salt and hash for the user.
2. Hash the submitted password with the stored salt.
3. Compare the result to the stored hash using a constant-time comparison.
4. If they match, the user is authenticated.
Q19: Why is a salt needed when hashing passwords?
A: A salt is a random value unique to each user. It prevents two users with the same
password from having identical hashes, and defeats precomputed rainbow tables.
Q20: What are the three parts of a JWT, and what does each contain?
A: [Link]
Header: Metadata like the signing algorithm ( {"alg":"HS256","typ":"JWT"} ).
Payload: Claims about the user/session (e.g., sub , role , exp ).
Signature: A cryptographic hash of the header and payload, created with a secret or
private key; it proves the token’s integrity.
Q21: What is the difference between verifying and validating a JWT?
A:
Verification confirms the signature is correct, proving the token came from a trusted
issuer and has not been tampered with.
Validation checks the claims: is the token expired ( exp ), is the issuer correct ( iss ),
does the audience match ( aud )? Both must succeed before trusting the token.
Q22: Why must you never store sensitive data in a JWT payload?
A: The payload is base64url-encoded, not encrypted. Anyone who intercepts the token can
easily decode and read its contents. JWTs provide integrity (tamper-proofing) via the
signature, not confidentiality.
Section 6: Authentication Strategies (Pages 8–9, PDF 1)
Q23: What is the core difference between stateless and stateful authentication?
A:
Stateless: The server stores no session state. Every request is self-authenticating (e.g.,
pure JWT).
Stateful: The server maintains session information (e.g., refresh token in a database),
allowing for features like immediate token revocation.
Q24: In a stateful JWT setup, what is the role of the access token and the refresh token?
A:
Access token: Short-lived (minutes to an hour), sent with every API request; its short life
limits the damage window if stolen.
Refresh token: Long-lived (days to weeks), stored securely and used only to obtain new
access tokens when the current one expires. It is stored server-side to enable
revocation.
Q25: Describe the complete token lifecycle from login to logout in a stateful system.
A:
1. Login → server issues short-lived access token and long-lived refresh token (stored in
DB).
2. Client uses access token for API calls; server only verifies signature (fast).
3. Access token expires → client calls /auth/refresh with refresh token.
4. Server checks refresh token in DB (not revoked) → issues new access token.
5. Logout → refresh token is deleted from DB. All future refresh attempts fail, forcing re-
authentication.
Q26: What are the recommended storage locations for access tokens and refresh tokens,
and why?
A:
Access token: Stored in memory (JavaScript variable). Avoids persistent storage that
XSS can read; lost on tab close.
Refresh token: Stored in an HttpOnly, Secure, SameSite=Strict cookie. HttpOnly
prevents JavaScript access (XSS protection); Secure ensures HTTPS only; SameSite
prevents CSRF. Never use localStorage for refresh tokens.
Section 7: Authorization & RBAC (Pages 9–10, PDF 1)
Q27: Distinguish authentication from authorization.
A: Authentication verifies identity (“Who are you?”). Authorization determines permissions
(“What are you allowed to do?”). They always happen in that order: you cannot authorize
someone you have not identified.
Q28: Define the three main concepts in Role-Based Access Control (RBAC).
A:
Role: A named collection of permissions (e.g., admin , editor ).
Permission: An allowed action on a resource (e.g., posts:create , users:delete ).
User → Role: A user is assigned one or more roles and inherits all their permissions.
Q29: Walk through how authorization is enforced in a typical middleware pipeline.
A:
1. Authentication middleware runs first: validates JWT, extracts user ID and roles, attaches
them to the request context.
2. Authorization guard/middleware runs next: checks if the user’s roles contain the required
permission for the endpoint.
3. If authorized, the controller runs.
4. If not, return 403 Forbidden immediately, without hitting the service or database.
Q30: Name and briefly describe two authorization models beyond RBAC.
A:
ABAC (Attribute-Based Access Control): Decisions based on user attributes, resource
attributes, and environment (e.g., “User can edit a post if they are the author AND the
post is a draft”).
ReBAC (Relationship-Based Access Control): Access determined by the relationship
between the user and the resource (e.g., “User can view a document if they are a viewer
of the containing folder”). Inspired by Google Zanzibar.
Q31: What is the principle of least privilege?
A: Grant users and services only the minimum permissions required to do their job. Start
restrictive and loosen as needed—it’s harder to revoke permissions later than to grant them.
Section 8: ORM Concepts & Drizzle (Pages 2–4, PDF 2)
Q32: What is an ORM, and what are its three main benefits?
A: An ORM is a layer that lets you interact with a database using your programming
language instead of raw SQL. Benefits: type safety, query building (avoiding string
concatenation), and built-in relationship management and migration support.
Q33: Complete the abstraction ladder for database access: Raw SQL → _ → ORM with
schema.
A: Query Builder → ORM with schema
Q34: In Drizzle ORM, what is the purpose of the schema file?
A: It is a TypeScript file defining your database tables using Drizzle’s type-safe functions. It
acts as the single source of truth for both the database structure and the automatically
inferred TypeScript types (e.g., Product , NewProduct ).
Q35: Drizzle queries are lazy. Explain what this means and name the three execution
methods.
A: Lazy means nothing runs against the database until you explicitly execute the query. The
execution methods are:
.all() – returns an array of all matching rows.
.get() – returns a single row or undefined .
.run() – executes the query without returning data (used for UPDATE , DELETE ).
Section 9: Migrations (Pages 4–5, PDF 2)
Q36: What is a database migration, and why is it preferred over editing the database
manually?
A: A migration is a versioned SQL script that alters the database schema. It is tracked in
version control, reproducible by any team member, applied sequentially, and auditable.
Manual changes are invisible, unreproducible, and prone to conflicts.
Q37: Describe the three-step workflow for creating and applying a migration with Drizzle Kit.
A:
1. Edit the Drizzle schema file.
2. Run npx drizzle-kit generate – Drizzle compares the current schema with the
previous state and creates a new SQL migration file in the drizzle folder.
3. Run npx drizzle-kit migrate – Drizzle applies all unapplied migration files in order,
recording applied migrations in __drizzle_migrations .
Q38: What are the two most important rules when working with migration files?
A:
1. Sequential: Migrations are numbered (0000, 0001, …) and must be applied in order.
2. Additive/Immutable: Never edit a migration that has already been applied. To fix a
mistake, create a new migration that corrects it.
Q39: How do migrations prevent team conflicts when two developers change the schema
simultaneously?
A: Version control will flag a conflict on the migration files themselves. This forces
developers to resolve the conflict intentionally, preventing silent, inconsistent database states
that would occur from manual edits.
Section 10: Hono Web Framework (Pages 6–8, PDF 2)
Q40: What is Hono, and what are its key differentiators?
A: Hono is a small, fast web framework for building HTTP APIs. Key features: extremely
lightweight, one of the fastest JavaScript HTTP frameworks, first-class TypeScript support,
runs on multiple runtimes ([Link], Bun, Cloudflare Workers, Deno), and follows a familiar
Express-like middleware pattern.
Q41: In a Hono route handler, what is the Context object ( c ) and what four things can you
commonly access from it?
A: c is the request context. Common uses:
[Link]() – URL path parameters ( :id ).
[Link]() – asynchronous JSON body parsing.
[Link]() – query string parameters ( ?page=1 ).
[Link](data, status) – send a JSON response with optional status code.
Q42: Match the following HTTP methods to their conventional REST API usage: GET, POST,
PUT, PATCH, DELETE.
A:
GET – Retrieve data, no side effects.
POST – Create a new resource.
PUT – Replace an entire resource.
PATCH – Partially update a resource.
DELETE – Remove a resource.
Q43: What is a common mistake when parsing a JSON body with [Link]() ?
A: Forgetting to await it. [Link]() returns a Promise, so you must await
[Link]() to get the actual body object.
Section 11: Building APIs with Hono + Drizzle (Pages 8–9, PDF 2)
Q44: Given the Drizzle query [Link]().from(products).where(eq([Link],
id)).get() , what happens if no product with that ID exists?
A: The .get() method returns undefined .
Q45: In a PATCH route, why do we use returning().get() after [Link] ?
A: returning() tells the query to return the updated row (rather than just executing silently
with .run() ). .get() extracts that single row so we can send it back in the response. If no
row was found, the .get() will return undefined , which we use to send a 404.
Section 12: Route Grouping with [Link]() (Pages 9–10, PDF
2)
Q46: How does [Link]() help organize a growing API?
A: It allows you to mount a child Hono app under a base path. For example,
[Link]('/products', productRoutes) . All routes defined inside productRoutes are
now accessible under the /products prefix. This keeps each resource in its own file.
Q47: If orderRoutes defines a GET '/ handler and is mounted at /orders , what is the
full URL path?
A: GET /orders
Section 13: Service Layer (Pages 11–13, PDF 2)
Q48: List four benefits of extracting business logic into a dedicated service layer.
A:
1. Reusability: The same function can be called from multiple routes or background jobs.
2. Testability: Services are plain functions with no dependency on HTTP context ( c ),
making them easy to unit test.
3. Clarity: Routes handle HTTP concerns only; services handle data and business rules.
Each file has a single responsibility.
4. Maintainability: Changing data access logic requires editing one service function, not
every route that uses it.
Q49: Show how a route handler changes when you move logic to a service. (Use the GET
all products example).
A: Without service: the route handler writes [Link]().from(products).all() directly.
With service: the handler calls getAllProducts() and returns
[Link](getAllProducts()) . The route becomes a thin wrapper that only parses the
request and sends the response.
Q50: State the simple mental model that describes the separation between routes, services,
and the database.
A: Routes are the door (receive request, send response). Services are the engine (contain
all logic, no HTTP knowledge). The database is the storage (services talk to it; routes never
do directly).
Q51: According to the notes, what is a good rule of thumb for moving logic out of a route
handler?
A: If you find yourself writing a Drizzle query inside a route handler and you would ever need
the same query from another route, move it to a service function. Also, if a route handler is
longer than about 10 lines, the logic probably belongs in a service.
This set covers every major definition, explanation, workflow, and comparison in your study
notes. Good luck on your exam!