HTTP is the agreement between client and server
An API contract tells your frontend what to send and what a response means. TypeScript interfaces inside the frontend cannot define that agreement by themselves. The request crosses a network, and another client can send different data.
Taskboard exposes resources under /api. A workspace owns projects, and a project owns tasks. The route expresses that relationship.
GET /api/workspaces/workspace-id/projects/project-id/tasks HTTP/1.1
GET reads a resource. It must not create a task or send an invitation. Browsers, proxies, and clients can repeat reads, sometimes without a fresh user gesture.
POST asks the server to perform an operation such as creating a task. PATCH changes selected fields of an existing task. DELETE removes a resource. These method names help clients reason about behavior, but the server still has to implement the intended guarantees.
Status and body have different jobs
The status gives the broad result. The JSON body gives application details. Common results in Taskboard include an authentication failure, a forbidden role, an unavailable resource, and a conflict with a newer task version.
An error response follows this shape:
{
"error": {
"code": "SOME_STABLE_ERROR_CODE",
"message": "A human-readable explanation"
},
"requestId": "request-identifier"
}
This is an illustrative error, not a specific endpoint response. Frontend logic can branch on a stable code. People can read the message. The request identifier lets support locate the corresponding server logs.
Headers carry information outside the resource body. Content-Type describes the body format. A cookie carries the session credential. X-CSRF-Token protects authenticated writes made through the browser. Idempotency-Key identifies a retryable operation.
A route is input
A UUID in the URL identifies a record. It does not prove that the caller can access the record. Changing the workspace or task identifier is a normal capability of an HTTP client, even when the frontend hides those fields.
A common frontend mistake is treating every non-success response as a network failure. A 409 conflict needs different handling from an unreachable server. The first gives a known outcome. The second may leave the outcome uncertain.
Why include a workspace identifier if the task has a globally unique ID?
The workspace makes the intended tenant context explicit. The server still checks membership and constrains the query to that workspace.
Read an HTTP request as a complete agreement: method, path, headers, body, and documented response.
Read api/src/app.ts