I am making a web UI and an HTTP API for editing JSON documents in collaboration (role and versioning system).
There are several types of JSON documents. Each type is described by a JSON schema, let us say:
schema_a, schema_b
Each user is assigned a role for editing a JSON document, among:
editor_1, editor_2, reviewer
Besides the "initial" JSON document, each revision of a JSON document is stored, and only one can be marked as "final":
initial, rev_1, rev_2, rev_3, …, final
In the web UI, the user first selects a schema (which then displays the list of documents following that schema), then a document, then a role (which then displays the list of revisions for that role), then a revision (among "initial", "rev_1", "rev_2", "rev_3", …, "final"). In this order. Then the user loads the selected revision in the editor. He works on it and eventually saves his work, which creates a new revision with the current revision number + 1. Before saving, he can mark his revision as "final", in which case the new revision is saved as "final" instead.
What is the best URI structure for this hierarchical model?
Here are the two structures that come to mind (notice the trailing slashes, denoting collection resources, as opposed to item resources):
Structure 1
In this structure, path segments are organized in a sequence of collection resource–item resource pairs:
/
/schemas/
/schemas/{schema}
/schemas/{schema}/documents/
/schemas/{schema}/documents/{document}
/schemas/{schema}/documents/{document}/roles/
/schemas/{schema}/documents/{document}/roles/{role}
/schemas/{schema}/documents/{document}/roles/{role}/revisions/
/schemas/{schema}/documents/{document}/roles/{role}/revisions/{revision}
With this structure I would allow GET
on all the resources, PUT
and DELETE
on all the item resources and POST
only on this collection resource: /schemas/{schema}/documents/{document}/roles/{role}/revisions/.
Examples. — I have omitted the headers to simplify.
Request 1:
GET /schemas/ HTTP/1.1
Response 1:
HTTP/1.1 200 OK
["/schemas/schema_a", "/schemas/schema_b"]
Request 2:
GET /schemas/schema_a HTTP/1.1
Response 2 (I use JSON Schema):
HTTP/1.1 200 OK
{
"type": "object",
"properties": {
"x": {"type": "number"},
"y": {"type": "boolean"},
"z": {"type": "string"}
},
"required": ["x", "y"]
}
Structure 2
In this structure, all path segments denote collection resources but the last one which denotes an item resource if the path is complete (the longest URIs):
/
/documents/
/documents/{schema}/
/documents/{schema}/{document}
/revisions/
/revisions/{schema}/
/revisions/{schema}/{document}/
/revisions/{schema}/{document}/{role}/
/revisions/{schema}/{document}/{role}/{revision}
/schemas/
/schemas/{schema}
With this structure I would allow GET
on all the resources, PUT
and DELETE
on all the item resources and POST
only on this collection resource: /revisions/{schema}/{document}/{role}/.
Examples. — I have omitted the headers to simplify.
Request 1:
GET /documents/ HTTP/1.1
Response 1:
HTTP/1.1 200 OK
["/documents/schema_a/", "/documents/schema_b/"]
Request 2:
GET /documents/schema_a/ HTTP/1.1
Response 2:
HTTP/1.1 200 OK
["/documents/schema_a/document_foo", "/documents/schema_a/document_bar"]
Request 3:
GET /documents/schema_a/document_foo HTTP/1.1
Response 3:
HTTP/1.1 200 OK
{
"x": 48,
"y": true
}
/documents/{id}
?/business/123/departments/
. C) Individual entities details are always retrieved directly, never from a nested context, so to get a department, you would use/departments/321/
. This gives you flexibility in the future if your relations change. I have never found it necessary or useful to nest more than one level deep, though that's less firm in my mind.