Backend Java
Build the backend that frameworks hide: parse raw HTTP by hand, write a JSON parser from scratch, route requests with path parameters, chain middleware, map objects to in-memory tables, pool connections, cache with LRU and TTL, validate request bodies, mint and verify session tokens, and wire it all into a working REST service. The server underneath Spring, from first principles.
10 projects, 250 hands-on levels, run in your browser.
Syllabus
- The HTTP Protocol: Every web framework is, at bottom, a string parser: a browser sends a few lines of text, and the server sends a few lines back. This project builds that text protocol from scratch, splitting a request into method, path, and version, reading headers with case-insensitive lookup, decoding query strings and percent-escapes, mapping status codes to their reason phrases, and assembling a well-formed response. No sockets, just the wire format every backend speaks.
- A JSON Parser from Scratch: Behind every @RequestBody and res.json() is a parser that turns a stream of characters into a tree of objects. This project builds one: a tokenizer that recognizes strings, numbers, and punctuation; a recursive-descent reader that assembles maps, lists, and scalars; and a serializer that walks the tree back to text with the escaping the spec demands. Once you have written it, JSON stops being magic.
- Routing: A router is a lookup table from (method, path) to handler. This project builds one from scratch: matching exact paths, extracting path parameters like /users/:id, distinguishing a missing route (404) from a wrong method (405), ordering routes so the most specific wins, and assembling a Router class that dispatches a request to the right handler. The piece of every framework that decides which of your functions runs.
- Middleware: Between the router and your handler sits a stack of middleware: functions that log, authenticate, parse, compress, and short-circuit. This project builds the chain from scratch, a shared request context, before-and-after hooks, the onion model where each layer wraps the next, auth gates that stop the request early, and a composed pipeline. Once you have written it, app.use() stops being magic.
- The Repository Layer: Behind every save() and findById() is a data store and a mapping. This project builds a mini-ORM in memory: a table of rows keyed by an auto-incrementing id, full CRUD, mapping rows to and from entity objects, finding by arbitrary fields, and a small query DSL with filtering, sorting, and paging. No database, just the HashMap and the abstractions that make it look like one.
- Connection Pooling & Rate Limiting: Opening a database connection is expensive, so servers keep a POOL and lend them out. Sending unlimited requests is dangerous, so servers RATE-LIMIT. This project builds both from scratch: a bounded pool with borrow, return, exhaustion, validation, and statistics; and a token-bucket limiter that refills over time. Time and randomness are passed in so everything stays deterministic and testable.
- Caching: A cache trades memory for speed, and the hard parts are eviction and freshness. This project builds them: an LRU cache that drops the least recently used entry when full, TTL expiry that retires stale entries, the cache-aside pattern that loads on a miss, hit/miss statistics that tell you if the cache is worth it, and ETag conditional responses that skip the body entirely. Time is passed in, so every test is deterministic.
- Validation & DTOs: A request body arrives as untrusted data; before your handler touches it, it must be validated and bound into a typed object. This project builds that layer: individual constraints (required, range, length, pattern), a validator that collects EVERY violation instead of failing on the first, binding a map into a DTO, and the problem-detail error response that tells the client exactly what went wrong. The boundary between the wild internet and your clean domain.
- Sessions & Auth: Authentication answers 'who are you?' and authorization answers 'what may you do?'. This project builds both: a signed token (using a simple deterministic hash, a TEACHING stand-in for real HMAC, never use it in production), verification that detects tampering, a session store with expiry, bearer-header extraction, and role and permission checks. The shape of every auth system, with the cryptography deliberately simplified so the structure stays visible.
- Capstone: A REST Service: Ten projects of machinery, one running service. This capstone assembles a small in-memory user API: a repository holds the data, a router maps requests to handlers, the JSON layer parses bodies and serializes responses, middleware authenticates, and a cache speeds reads. Each chapter wires one more layer, and the finale takes a raw HTTP request string and returns a raw response string, the whole backend you built, end to end.
Key concepts
- 400 vs 422: 400 Bad Request is for input the server cannot even parse; 422 Unprocessable Entity is for well-formed input that fails validation. The distinction tells the c…
- 404 vs 405: 404 Not Found means the path does not exist; 405 Method Not Allowed means the path exists but not with this method. A router that conflates them hides useful i…
- Authentication: Establishing who the caller is, by verifying a credential: a signed token, a session id, a username and password. Answers 'who are you?' before authori…
- Authorization: Deciding what an authenticated caller may do, via roles, permissions, and ownership. Secure systems deny by default and allow only what is explicitly granted.
- Backpressure: Signaling upstream to slow down when a system is overloaded, by queueing, rejecting (503), or shedding low-priority work. The alternative to collapsing under l…
- Bearer token: A token sent in the Authorization: Bearer header; whoever bears it is granted access. The server extracts it, verifies it, and reads the user from its claims.
- Cache: A fast store of recently computed or fetched values, trading memory for speed. The hard parts are eviction (what to drop when full) and freshness (when to cons…
- Cache stampede: When a hot key expires and many concurrent requests all miss and hammer the source at once. Single-flight loading (only the first miss loads, others wait) prev…
- Cache-aside: The dominant caching pattern: check the cache, and on a miss load from the source, store it, and return. Writes invalidate the entry so the next read reloads.…
- Claim: A statement inside a token's payload, like the user id, roles, or expiry. The server reads claims to make auth decisions after verifying the signature.
- Conditional request: A request carrying a validator (If-None-Match with an ETag) so the server can answer 304 Not Modified when nothing changed, skipping the body. The protocol-lev…
- Connection pool: A fixed set of reusable database connections lent out and returned, because opening one per request is slow. Borrow takes a free connection, release returns it…
- Constraint: A single rule a field must satisfy: not blank, within a range, matching a pattern. Each returns pass or fail with a message; a validator runs many and gathers…
- Content-Length: The header declaring the body's size in bytes. A well-formed request's body length matches it; servers use it to know where the body ends.
- Content-Type: The header naming the body's media type, like application/json or text/html; charset=utf-8 . The part before the ; is the media type; the rest are paramete…
- CRUD: Create, Read, Update, Delete: the four basic operations on a resource, mapping to POST, GET, PUT/PATCH, and DELETE in a REST API. The vocabulary of the reposit…
- Deny by default: Authorizing nothing unless an allow-list explicitly permits it, so a missing rule fails closed rather than open. The safe default for any access-control system.
- Dirty checking: Comparing an entity's current fields to its loaded snapshot to find what changed, so the ORM issues an UPDATE only for modified columns. Saves work and avo…
- DTO: Data Transfer Object: a plain typed object that carries request or response data across a boundary, separate from your domain model. Binding turns an untrusted…
- Entity mapping: Converting between a database row (a map of columns) and a typed domain object. toRow serializes the object; fromRow reconstructs it. The heart of any ORM.
- Error boundary: An outer middleware that catches exceptions from inner layers and turns a crash into a clean 500 response. Without it, an uncaught error leaks a stack trace or…
- Escaping: Replacing characters that would break a format with safe sequences: in JSON, a quote becomes backslash-quote and newline becomes backslash-n. Unescaping revers…
- ETag: A fingerprint of a response body sent in a header. The client returns it as If-None-Match; if it still matches, the server answers 304 Not Modified with no bod…
- Fixed window: A rate-limit counter that resets each window (say, per minute). Simple, but allows a burst at the window boundary when two windows' worth of requests clust…
- Handler: The function a route dispatches to, which reads the request and produces a status and body. Frameworks call it a controller action, view, or endpoint; the rout…
- Hit ratio: The fraction of cache lookups that find a value: hits / (hits + misses). The single number that says whether a cache is worth its memory; a low ratio means the…
- HTTP: The text protocol browsers and servers speak: a request (method, path, headers, body) and a response (status, headers, body). Stateless by design, every reques…
- HTTP header: A Name: value metadata line. Header names are case-insensitive, so Content-Type and content-type are the same header, a detail every parser must honor.
- HTTP method: The verb naming the intended action: GET (read), POST (create), PUT (replace), PATCH (modify), DELETE (remove). GET, HEAD, and OPTIONS are safe; they must not…
- Idempotent: An operation that has the same effect whether applied once or many times. PUT and DELETE are idempotent; POST usually is not. The property that makes safe retr…
- JSON: The dominant data format for web APIs: objects (maps), arrays, strings, numbers, booleans, and null. A parser turns its text into a tree of language objects; a…
- Keep-alive: HTTP/1.1 reuses one TCP connection for several requests unless the client sends Connection: close . Avoiding a fresh handshake per request is a major performan…
- LFU eviction: Least Frequently Used: evict the entry with the fewest accesses, rather than the oldest. Better for stable hot sets, worse for shifting access patterns than LR…
- Load shedding: Deliberately dropping low-priority requests under heavy load so high-priority ones still succeed. A controlled degradation that keeps the critical path alive.
- LRU eviction: Least Recently Used: when the cache is full, drop the entry untouched for the longest. Tracking access order (often with a linked hash map) keeps hot entries a…
- Mass-assignment guard: Rejecting request fields the DTO does not declare (like a sneaky isAdmin), so a client cannot set properties you never meant to expose. Strict binding closes t…
- Middleware: A function in the chain between router and handler that can read, mutate, or short-circuit a request: logging, auth, parsing, compression. Each runs in order o…
- Onion model: Middleware that wraps the next layer, running code before AND after the handler, like nested shells. The first layer added is the outermost; logging and timing…
- ORM: Object-Relational Mapping: a layer that maps rows to objects and back, generating queries from method calls. This track builds a mini-ORM in memory to show wha…
- Pagination: Returning results in pages (skip (page-1)*size, take size) instead of all at once, so a large collection does not overwhelm the client or the server. The LIMIT…
- Path: The part of the request target identifying the resource, like /users/42 . Split into segments for routing; the part after ? is the query string, not the path.
- Path parameter: A captured path segment in a route pattern, written :id in /users/:id . Matching binds it to the concrete value, so /users/42 yields id=42 for the handler.
- Percent-encoding: URL encoding that escapes unsafe characters as %XX hex (and + for space), so hello world travels as hello+world or hello%20world . The server decodes it back.
- Pool exhaustion: When every pooled connection is in use and a new request cannot borrow one. The pool then queues the waiter or rejects with 503, the backpressure that protects…
- Problem details (RFC 7807): A machine-readable error body carrying a status, a type URI, and per-field messages, so clients can react programmatically instead of scraping prose. The stand…
- Query DSL: A chainable, fluent API for building queries, like query.where(...).orderBy(...).limit(...), that an ORM translates into SQL. Each call narrows or shapes the r…
- Query string: The key-value parameters after ? in a URL, like ?q=java&page=2 . Each value is percent-encoded so special characters survive the trip and must be decoded o…
- Rate limiting: Capping how many requests a client may make in a time window, to protect the server from overload and abuse. Exceeding the limit returns 429 Too Many Requests…
- RBAC: Role-Based Access Control: users have roles, roles grant permissions, and a check asks whether any of the user's roles grants the required permission. The…
- Recursive-descent parsing: Parsing by a set of mutually recursive functions, one per grammar rule: an object parser calls a value parser which may call the object parser again. The natur…
- Repository: An abstraction over data storage exposing save, find, and delete, hiding whether the rows live in a database or a HashMap. The seam that lets you swap the stor…
- Request binding: Reading fields from the raw request map into a typed DTO, coercing types (a String "2" to int 2), applying defaults, and rejecting unknown fields. Th…
- Request context: The object that travels with one request, carrying attributes set by one middleware and read by the next (a parsed user id, the response being built). The shar…
- Request line: The first line of an HTTP request: METHOD PATH VERSION, for example GET /users HTTP/1.1 . The server parses these three tokens before anything else.
- Resource: A named thing an API exposes at a URL, like a user or an order. A collection (/users) and its members (/users/42) are both resources, each answering the method…
- Resource leak: A borrowed connection never returned (often because an exception skipped the release), shrinking the pool until it is empty. The fix is to release in a finally…
- REST: An architectural style for APIs: resources at URLs, manipulated with HTTP methods, returning representations (usually JSON). GET /users lists, POST /users crea…
- Route precedence: When several patterns match a path, the most specific wins: a literal beats a parameter, a parameter beats a wildcard. Routers score patterns and check the spe…
- Routing: Mapping a request's method and path to the handler that serves it. A router matches exact paths and patterns with parameters, picks the most specific when…
- Safe method: A method that does not change server state: GET, HEAD, OPTIONS. Caches and crawlers rely on this guarantee, which is why side effects must never hide behind a…
- Sanitization: Normalizing accepted input rather than rejecting it: trim whitespace, lowercase emails, strip control characters, escape HTML. Cleans data before it reaches th…
- Serialization: Turning in-memory objects back into a transmittable string, escaping strings and formatting numbers per the spec. The inverse of parsing; a correct pair round-…
- Service facade: One class presenting a clean API over the whole stack, wiring repository, router, JSON, middleware, and cache behind a single entry point. Callers hand it a re…
- Session: Server-side auth state: a session id (in a cookie) maps to stored user data, with creation, expiry, and revocation. The stateful alternative to self-contained…
- Short-circuiting: A middleware halting the chain before the handler runs, as an auth gate does on a missing token (401) or a rate limiter on too many requests (429). A halt flag…
- Signed token: A self-contained credential carrying a payload and a signature, so the server can trust it without storing state. Verification re-signs the payload and compare…
- Sliding window: A rate limiter that counts requests in the last N seconds from now, rather than a fixed calendar window. Smoother than fixed windows, at the cost of tracking r…
- Stale-while-revalidate: A freshness policy that serves a slightly-expired entry immediately while refreshing it in the background, trading perfect freshness for low latency. Common in…
- Stateless server: A server that keeps no per-client memory between requests, putting all needed state in the request itself (a token) or a shared store. Statelessness is what le…
- Status code: The three-digit result of a request, grouped by first digit: 2xx success, 3xx redirect, 4xx client error, 5xx server error. Each pairs with a reason phrase lik…
- Token bucket: A rate-limit algorithm: a bucket holds tokens, each request spends one, and tokens refill at a steady rate up to a capacity. Allows short bursts (up to capacit…
- Token expiry: An exp claim after which a token is no longer valid, limiting the damage of a leaked credential. A small clock-skew grace and a refresh window handle real-worl…
- Tokenizer: The first stage of a parser: it scans characters and groups them into tokens (strings, numbers, punctuation), classifying each by its first character. The pars…
- TTL (time to live): An expiry stamped on a cache entry; after it, the entry is stale and treated as a miss. Sliding TTL extends the life on each access; a grace period enables ser…
- Upsert: Insert-or-update by a natural key: if a row with that key exists, update it; otherwise insert. One operation where two would otherwise race.
- Validation: Checking request data against constraints (required, range, length, pattern) before the handler trusts it. Good validators collect EVERY violation, not just th…
- Wait queue: A FIFO line of requests waiting for a free pooled resource. When a connection is released, the first waiter is handed it, bounding how long any request waits.
- Wildcard route: A pattern segment ( * ) that matches anything, used for catch-all routes like static file serving. Less specific than a literal or a parameter, so it loses pre…
- Write-through: A write that updates the cache AND the source together, keeping them consistent at the cost of a slower write. Contrast write-around, which writes the source a…