Resources
Give collections and items clear identifiers.
CodeBhavyaDesign predictable resource requests, representations and response outcomes.
A web API defines how a client requests application operations and interprets their outcomes. This lesson connects resource identifiers, HTTP methods, status codes and JSON to the controller, service and database boundaries already developed. You will distinguish a representation from stored state and design predictable errors.
The local lab models a small task API: read a collection or item, create a task, replace an existing task and delete it. It runs synchronous JavaScript only. No network request, backend, account, authentication or database is involved.
Give collections and items clear identifiers.
Distinguish reads, creation, replacement and deletion.
Match status, headers and body to the outcome.
Validate JSON and conditional changes deliberately.
An API exposes controlled operations rather than direct database access. A client sends an allowed request, the backend validates and authorizes it, application code performs its work, and a response describes the outcome. The browser does not need database credentials to consume that contract.
A representation is a particular description of a resource, such as JSON for a task. It is not necessarily a database row serialized without filtering. Keep internal fields, secrets and implementation details out of public responses.
An endpoint returning JSON is not automatically a complete REST design. It can be a useful HTTP API while still lacking architectural constraints or a consistent application protocol.
Constructs a request.
Validates and authorizes.
Coordinates the operation.
Returns accepted public data.
REST describes an architectural style with constraints including client/server separation, stateless interactions, cacheability, a uniform interface and layered systems; code-on-demand is optional. Resource identification, representations and meaningful protocol controls help clients interact predictably.
Stateless interaction means a request carries the context needed to understand it rather than depending on hidden conversational server state. It does not mean a server has no database, a resource cannot change or every authentication design is identical.
Hypermedia links and controls can describe available application transitions. Many practical APIs use only part of this style. Describe their actual contract instead of claiming that plural paths plus JSON prove full REST compliance.
Separate presentation and application responsibilities.
Carry the needed interaction context.
State whether a response can be reused.
Use resources and protocol semantics deliberately.
The lab is intentionally a narrow HTTP resource model, not a complete REST or server implementation. It provides no cache, hypermedia workflow or transport integration.
A collection URL such as /api/tasks identifies a set of tasks. An item URL such as /api/tasks/1 identifies one task. The route is an application identifier, not a promise about source filenames, framework classes or database table names.
Choose stable identifiers and a consistent path contract. Query parameters can describe filtering or pagination for a collection, but arbitrary query fields should not silently become SQL fragments or privileged choices.
The lab has an exact collection path and canonical one-digit item IDs 1–9. It rejects other paths as unknown. It has no query parsing, URL normalization or encoded-path handling beyond that stated contract.
GET /api/tasks read collection
POST /api/tasks create a task
GET /api/tasks/1 read one task
PUT /api/tasks/1 replace existing task fields
DELETE /api/tasks/1 delete an existing taskReal APIs can support additional routes and creation semantics. The toy item PUT updates existing tasks only; it does not create a missing item.
GET is a read with safe semantics: the client is not requesting a state-changing action. A server can still log or collect operational metrics, so safe does not mean absolutely no incidental server activity.
Idempotency concerns the intended effect of repeating the same request, not identical status codes or response bodies. PUT and DELETE have idempotent method semantics; POST does not provide that general guarantee. Retrying a creation POST can create another resource.
A second DELETE can return 404 because the first removed the resource, while the deletion effect remains the same. Conditional PUT can fail when the stored version changes without making an unsafe blind overwrite.
| Method | Task operation | Important distinction |
|---|---|---|
| GET | Read collection/item | No requested mutation |
| POST | Create in collection | Repeated accepted request can create another item |
| PUT | Replace existing title/completed fields | Full accepted field set, not a partial patch |
| DELETE | Remove existing item | Repeated response may differ |
PATCH is a separate method for partial modification with a defined patch format; the lab does not implement it. HEAD and OPTIONS also remain outside its small router.
JSON can represent objects, arrays, strings, numbers, booleans and null. Object property names and string values use double quotes. A syntactically valid JSON value can still be unacceptable application input.
The task body must be an object with exactly title and completed. Title trims to 1–80 Unicode code points with no C0/C1 controls or unpaired surrogates; completed must be boolean. The client cannot submit an ID, version, role or arbitrary extra field.
The lab parses JSON with JSON.parse after bounding the raw string to 1,000 JavaScript code units. That parser can keep the last value for duplicate property names; it does not provide duplicate-key detection. Real APIs should document or reject ambiguous payloads rather than assuming all parsers agree.
{
"title": "Review HTTP methods",
"completed": false
}Numbers such as NaN and Infinity are not JSON numeric literals. A JSON string "false" is also different from the boolean false required by this task contract.
Content-Type describes the representation carried in a message. A JSON request should declare an appropriate JSON media type. Accept expresses which response representations the client can receive; it is not a substitute for the request Content-Type.
The lab’s POST and PUT require exactly application/json. It rejects another media type with 415 before parsing or changing a task. This is a deliberately restricted teaching check, not a complete media-type parser or content-negotiation system.
GET and DELETE in this toy contract ignore the body and media-type controls. A successful delete has no response body. Clients should not always call a JSON parser regardless of status and representation.
POST /api/tasks
Content-Type: application/json
{"title":"Review HTTP","completed":false}
Expected accepted creation:
201 Created
Location: /api/tasks/3
Content-Type: application/jsonThe request text illustrates an HTTP contract; no request is sent when the lesson lab runs.
Status codes distinguish outcomes instead of hiding every result inside a 200 body. The lab uses 200 for accepted reads/replacement, 201 plus Location for creation and 204 with no body for successful deletion.
Unknown paths or missing items use 404. Unsupported methods use 405 with Allow. Invalid JSON or shape uses 400, unsupported media type 415, required missing precondition 428 and a stale accepted precondition 412. These are the toy application’s explicit decisions.
A bounded collection reaching its capacity returns 409 here because creation conflicts with the toy store’s current limits. A real system should define its own capacity/rate/availability behavior; those are not interchangeable errors.
Accepted result or created resource.
No response content to parse.
Invalid representation or media type.
Required condition absent or no longer matching.
The lab clears old previews on every error. A stale task card must not make a failed change look successful.
Routing and method checks determine the applicable operation. For a JSON write, media type, syntax, size and shape validation precede mutation. A creation limit and a conditional version check then decide whether accepted data can be applied.
A task’s ID is server-chosen in this model. The accepted title/completed values are copied rather than sharing a caller-owned object. Rejected requests allocate no ID and leave every stored task unchanged.
Validation is not authorization. A title can be well-formed while the caller still lacks permission to edit that task. This public fictional lab has no users or protected records and does not establish production access control.
Choose a known operation.
Accept media type and data contract.
Check limits or current item version.
Change only accepted task state.
In a real backend, version comparison and mutation must be coordinated atomically with database state. A synchronous local function cannot demonstrate concurrent requests or distributed enforcement.
An ETag is a representation validator. A client can read an item, retain its validator and send If-Match when changing it. If the current representation no longer matches, a server can reject the change rather than overwriting someone else’s work.
The lab gives item GET a strong teaching tag such as "task-1-v1". PUT and DELETE require one exact quoted tag from this format; missing returns 428, malformed/unsupported syntax 400 and a well-formed mismatching tag 412. It does not implement tag lists, wildcard or weak-validator rules.
A successful changed replacement advances the item version. Replacing it with the same accepted fields leaves its version unchanged in this model. After a change, GET the item again to obtain the new tag before another conditional write.
GET /api/tasks/1
→ 200, ETag: "task-1-v1"
PUT /api/tasks/1
If-Match: "task-1-v1"
Content-Type: application/json
{"title":"A changed title","completed":true}
GET again before editing with the new validator.The toy PUT normalizes title text and returns a generated public representation, so it omits a response validator rather than claiming the submitted bytes were saved unchanged. The following GET supplies the current ETag. An ETag is not a password or permission token.
A validator can support conditional interaction, while Cache-Control describes response reuse. An ETag does not automatically mean a response should be cached. Private or sensitive data needs a deliberate cache policy.
The lab marks every response no-store so the teaching model does not imply a browser cache. Its version checks demonstrate conditional changes, not cache revalidation. It does not implement If-None-Match or 304.
For an actual API, design cache headers, freshness and validation together. Avoid reusing a cached result as proof of authorization or assuming a stale client copy is always the latest server state.
Identify a representation version.
State when reuse is allowed.
Protect user-specific response data.
Verify actual cache/proxy behavior.
The 428 response is not cacheable; this lab’s no-store policy also covers that outcome explicitly.
A received HTTP error response is different from a failed network request. Fetch can resolve to a Response for an HTTP error, so a client must inspect its status and validate the intended representation rather than assuming resolution means success.
A 204 has no body. Other successful JSON outcomes require a suitable Content-Type and acceptable data shape. A parseable object is not automatically the model your UI expected.
The following client fragment is illustrative and is not called by the lab. Its fetch requires a real same-origin backend route; this frontend course patch supplies no live /api/tasks service.
async function readTasks() {
const response = await fetch("/api/tasks");
if (!response.ok) {
throw new Error("Task read was not accepted");
}
const type = response.headers.get("Content-Type") || "";
if (type.split(";")[0].trim().toLowerCase() !== "application/json") {
throw new Error("Unexpected representation");
}
const data = await response.json();
// Validate data shape before presenting task rows.
return data;
}A robust client should use an appropriate media-type check, controlled error display and response-model guard. Render task titles as text, not interpreted HTML.
Browsers apply origin-based rules to script access. CORS lets a server opt into certain cross-origin response access through headers; some requests trigger a preflight check. It is a browser protocol, not a login mechanism.
A request using JSON or a custom header such as If-Match may require preflight across origins. The backend needs an appropriate origin/method/header policy and must still authenticate and authorize operations.
Do not use no-cors as a way to read a blocked API: an opaque response cannot give the normal script access needed to read and validate its body. CORS also does not protect a server against non-browser clients.
Applies script access rules.
Server declares allowed cross-origin access.
Establishes caller identity.
Checks the requested operation.
The local lab sends no requests or CORS preflights. Its behavior cannot prove Cloudflare/Render origin configuration.
Authentication establishes who the caller is; authorization determines what that caller may do. A client-provided role, task ID or valid ETag is not sufficient proof of permission. Check protected operations on the backend.
HTTP authentication can use 401 with an appropriate challenge; 403 communicates refusal for a request the server will not authorize. An API can also deliberately hide resource existence. Choose a documented policy rather than exposing private records through inconsistent errors.
Keep public errors stable and useful without returning secrets or production stack traces. A machine-readable error code and a controlled message can help the client choose an appropriate next step.
{
"error": {
"code": "stale_version",
"message": "Read the task again before changing it."
}
}The next lesson develops Web Security. The task lab has no authentication and makes no claim of protecting real user data.
Bound collection results and specify stable ordering when an API grows beyond a small fixture. Cursor or offset pagination must have a defined contract. Adding filters should preserve the meaning of existing fields rather than silently changing accepted results.
Version breaking changes deliberately and document request fields, response fields, error behavior and limits. Generated API descriptions can help tooling, but they do not replace tested examples and a correct backend implementation.
Retries need operation-specific rules. Repeating POST creation can duplicate work; conditional PUT may reject a stale version; repeating DELETE may find the item already absent. An idempotency mechanism needs actual backend enforcement, not just a client header.
Specify order and pagination.
Make contract changes deliberate.
Distinguish repeatable effects from duplicate work.
Verify examples against behavior.
The lab deliberately limits active tasks to five, IDs to 1–9 and raw write input to 1,000 code units. It has no pagination, persistent storage, retry cache or idempotency-key implementation.
Test routing, method Allow headers, invalid representations, accepted creation, full replacement, deletion and stale preconditions. Check stored state before and after each rejection and avoid carrying an old successful preview into an error run.
For the real backend, separately verify HTTP serialization, body/header limits, atomic version enforcement, authentication, CORS, cache policy and concurrent requests. Use controlled fixtures rather than publishing real credentials or private task data in traces.
The lab checks local JavaScript state transitions and safe rendering only. The visualizer follows one accepted creation; the interactive controls let you examine reads, writes, errors and preconditions.
No ID allocation or state mutation.
Controlled outcome and public representation.
Reject without overwriting current state.
Verify real headers/network independently.
Next: Level 28 — Web Security. Keep the distinction between a predictable API contract and actual security enforcement clear.
Follow an accepted task creation through routing, validation, state change, response and safe presentation. This approved five-stage player explains the request contract without sending HTTP.
POST to the task collection requests creation.
POST /api/tasksA collection write differs from item read/replacement/deletion.
This synchronous JavaScript model runs no network, server, authentication or database. Its in-memory task collection starts with task1 Review HTTP methods and task2 Practise JSON <objects>, both incomplete. Only completion uses local storage.
Collection: GET reads, POST creates. Item: GET reads, PUT replaces exactly title/completed, DELETE removes. POST/PUT require exact application/json and a JSON object with exactly title (trimmed 1–80 Unicode code points, no controls/lone surrogates) and completed (boolean). Raw input is bounded to 1,000 JavaScript code units.
PUT/DELETE require the exact current single strong mock ETag. Read GET first and copy the quoted ETag into If-Match. Missing is 428; malformed/unsupported syntax 400; stale is 412. PUT normalizes data and omits response ETag; read again for the new tag. DELETE 204 has no body or Content-Type.
At most five active tasks and monotonic IDs1–9 are allowed. Accepted repeated POST can create another task; deleted IDs are not reused. Reset clears all task state and restores the original fixture. The limited router, media check and tag format are not full HTTP implementations.
Idle. Local JavaScript API model; no HTTP request sent.
No request yet.No trace yet.No response yet.No response body yet.Compare exact path and Allow before assuming body failure.
Check media type, syntax, keys and value types.
Read the current tag instead of overwriting a changed task.
A204 is complete without JSON content.
If a create succeeds twice, inspect whether the client retried a non-idempotent POST. If PUT fails with 412, read the current task and decide how to reconcile changes rather than deleting the precondition blindly.
If parsing fails after deletion, check the status before reading JSON. If the screen still shows an older task after an error, clear the previous preview and display the rejected outcome.
The lab is not a debugger for a deployed backend, CORS policy or authentication system. Verify those boundaries with real requests separately.
No. REST has architectural constraints beyond JSON and resource-looking paths; describe the actual API contract.
Repeating the same request has the same intended effect; status and response body need not be identical.
POST does not provide a general idempotent creation guarantee; a separate enforced retry policy may be needed.
The representation in the message; Accept describes what response representations the client can receive.
It communicates creation and identifies the created resource in this contract.
It has no response content; the client must handle the completed empty outcome.
It prevents replacing/deleting a task whose current version no longer matches the supplied condition.
No. It controls browser cross-origin access; authentication and authorization remain separate backend checks.
GET collection:200, two tasks. GET /api/tasks/1:200 with quoted tag task-1-v1.
POST collection with New task/false:201, Location /api/tasks/3, three stored items. Repeat: another item.
PUT item1 with changed fields and tagv1:200, version2 internally, no response ETag. GET again for tagv2.
Repeat changed PUT with old tagv1:412 and unchanged current task.
DELETE item1 with current tag:204 and empty body. Repeat:404; no additional mutation.
Try invalid JSON, completed as string, extra ID or text/plain on POST. Expect400/415 and no allocation.
Empty If-Match on PUT/DELETE:428. Malformed tag:400. Valid tag for another version/item:412.
Create title <Demo> & API. It remains text in JSON/preview. Reset restores original task state.
An application interface accessed through web requests and responses.
A concept identified and interacted with through the application protocol.
A particular description of resource state, such as JSON.
An architectural style with defined distributed-system constraints.
A method whose requested semantics are read-only.
The same intended effect when an identical request is repeated.
Metadata describing the representation carried by a message.
A validator identifying a representation version.
A request condition requiring a matching current representation validator.
A browser protocol for controlled cross-origin response access.
Write a collection/item contract with methods, exact accepted input, status/headers/body and bounded results. Separate client representations from internal state, and define what repeated creation or deletion does.
Require deliberate conditions for protected replacements, with a real atomic version check and a defined stale-update outcome. Validate title/completed and render titles as text; do not accept role or ID fields as permission.
Test invalid syntax/type/media,404/405,201+Location,204 empty content, stale conditions and state preservation after rejection. Separately verify actual HTTP, authentication, authorization, CORS, cache policy and concurrent updates.
Success criterion: predict both response and stored state for every accepted and rejected operation. Next: Level28 — Web Security.
Explain resource/method/representation distinctions, reproduce creation/replacement/deletion and stale preconditions, and handle204 without JSON parsing. Identify the actual transport/security checks the local lab cannot perform. Completion is a local study marker.