Original guides / Guide
Build and test a small API contract
Implement a pure request handler, validate data and check status codes before connecting a real server.
You will learn to
- Separate routing from domain logic
- Validate untrusted input
- Test accepted and rejected requests
Before you start
JavaScript objects
Start with a contract
POST /tasks accepts a non-empty title and returns a created task. GET /health reports availability. Other paths return 404. The exercise runs a pure function on request objects; it does not open a socket or store permanent records.
Write status codes and response shapes before implementation. That separates a missing route, invalid input and successful creation. A UI should not have to parse vague prose to identify which category occurred.
Validate at the boundary
Check that title is a string, trim whitespace and reject an empty result. Restrict length before storage or downstream processing. A disabled button is not validation: callers can bypass the browser.
In a real API, parse JSON with a size limit, authenticate the caller, authorize the action, then validate and persist. This function omits persistence and accounts intentionally. It must not replace an existing protected backend.
Make tests observable
Assert status and returned title for a valid request, then check blank and missing titles. Test a wrong method and unknown route too. Testing the same internal expression as the implementation can reproduce its mistake rather than detect it.
Real create operations also need retry handling. Random identifiers do not prevent duplicate actions. Constraints and idempotency keys need a storage design beyond this in-memory model.
Implement and extend
Return {status, body}. Use id: 1 as the single fixture task identifier, not as a production ID generator. Return the trimmed title. After the checks pass, add a maximum title length and a boundary test.
To host this later, adapt it to your existing server framework and add persistence, ownership checks, quotas and monitoring. The working result here is an executable API contract, not a falsely advertised deployed service.
Connect the contract to a real server later
The in-page handler is a pure function receiving a method, path and body. It does not open a port or persist records. That makes its behavior reproducible while you decide the contract. A production adapter must parse the incoming body, enforce a size limit, authenticate the caller where required and serialize the returned status and payload. Those responsibilities are not replaced by a passing unit check.
Try a POST with a missing title, a whitespace-only title and a valid title. Compare them with an unknown path. The status codes should communicate which condition occurred; the UI should not claim that a request succeeded merely because it received JSON. Before adding storage, decide how duplicate submissions, concurrent updates and authorization should work.
Try it yourself
function handle(method, path, body = {}) {
return {status:404, body:{error:"Not found"}};
}
console.log(handle("POST", "/tasks", {title:" Learn "}));Enable JavaScript for this interactive activity. You can read all lesson explanations above without it.