0) Problem Restatement
Microsoft asked: design REST APIs (and the backend) for a service that stores JSON documents (small files), organized in folders. Clients create, read, update, partially update, delete and list documents. Key topics: resource design, metadata, concurrency control when two clients edit the same document, and good error handling.
1) Resources and Endpoints
POST /v1/folders { name, parent_id? } → 201 folder
GET /v1/folders/{fid}/documents?cursor=&limit=50&sort=updated_at → 200 { items, next_cursor }
POST /v1/folders/{fid}/documents { name, content: {...} } → 201, Location, ETag
GET /v1/documents/{id} → 200 body = JSON, ETag: "v7"
PUT /v1/documents/{id} If-Match: "v7" { content } → 200, ETag: "v8" | 412
PATCH /v1/documents/{id} If-Match: "v7" (JSON Merge Patch or JSON Patch) → 200 | 412
DELETE /v1/documents/{id} If-Match: "v8" → 204 | 412
GET /v1/documents/{id}/metadata → { name, size, owner, created_at, updated_at, version }
GET /v1/documents/{id}/versions/{n} → an older version (optional)
Status codes: 201 created, 200 ok, 204 no content, 400 invalid JSON or schema error, 401/403 auth, 404 not found, 409 name conflict in the folder, 412 precondition failed (stale ETag), 413 too large, 429 rate limited.
2) Concurrency Control (the important part)
Two users open version 7 and both save changes. Without protection, the second save silently overwrites the first (lost update).
- Every document has a version. GET returns it as an ETag header.
- Updates must send
If-Match: "v7". The server does a conditional write:UPDATE ... SET content = ?, version = 8 WHERE id = ? AND version = 7. - If someone already saved v8, the condition fails → 412 Precondition Failed. The client re-fetches, merges or shows a conflict, then retries.
- PATCH lets clients change only certain fields (smaller payloads), with the same ETag check.
3) Backend
Architecture Diagram
flowchart LR
C["Clients"] --> API["Documents API - auth, validation"]
API --> META[("Metadata DB - folders, docs, versions, ACLs")]
API --> STORE[("Content store - document DB or object storage")]
API --> SCH[("JSON schemas (optional)")]- Metadata (name, folder, owner, size, version, timestamps, permissions) in a relational DB. There's a unique constraint on (folder_id, name), which gives the 409 on duplicate names.
- Content: small documents (< 1 MB) in a document database (e.g., stored as JSONB), and large ones in object storage, referenced by key. Old versions are kept for history, with retention limits.
- Validation: check that the body is valid JSON, and optionally validate against a JSON Schema registered for the folder.
- Listing: cursor pagination (sorted by updated_at + id), with filters by name prefix or date.
4) Extras
- Permissions: folder-level ACLs inherited by documents (owner, editor, viewer). Check them on every call.
- Large documents: max body size (413). For big ones, support upload via a pre-signed URL.
- Caching: GET with
If-None-Match: "v8"→ 304 Not Modified if unchanged, which saves bandwidth. - Soft delete + trash, so accidental deletes can be restored.
5) Wrap-Up
Model folders and documents as REST resources with clear verbs and status codes, and return the document's version as an ETag. Require If-Match on PUT, PATCH and DELETE, with conditional writes that return 412 on conflicts (no lost updates). Keep metadata (with a unique folder/name constraint) in a relational DB and content in a document store or object storage with version history, validate JSON (optionally against schemas), paginate with cursors, and support 304 caching.