REST
We exclusively use:
- JSON or images as content.
- GET to retrieve resources.
- POST to submit new resources to the server.
- PUT to fully update an existing resource (fails if it does not exist).
- PATCH to update parts of an existing resource (fails if it does not exist).
- DELETE to remove resources.
Both PUT and PATCH should be implemented idempotent. I.e., doing the same API calls again must be a NOP.
Status Codes
On success (e.g., when mutating the database), a 2XX status code should be returned. For validation errors, a 4XX status code is appropriate. A 5XX status code indicates a server error.
Returning 5XX is always a bug.
For more information on semantically correct status codes, please refer to MDN - HTTP response status codes.
Endpoints
The endpoint paths use:
- hierarchical structure
- nouns instead of verbs
- only plural (exception: config)
- all endpoints need authorization (exception: config)
- all paths below
/api
- layer-specific paths below
/maps/<map_id>/layers/<name of layer>/
e.g./maps/<map_id>/layers/plants/plantings
Parameters
- Use singular if there is only one item, plural for collections.
- Search should have its own endpoint and parameter should be
name
- Return values are called
Page*
if they support paging. We use the parameterpage
andper_page
(type integer) for pagination. - Currently we don't use filtering or sorting.
- If in doubt, leave it out: keep parameters minimal.
Versioning
The frontend is the only user, so we only need minimal API versioning. The frontend only need to know if a reload is needed.
Files
We use utility functions to access files in Nextcloud.
Documentation
Documentation of APIs is done via utopia
:
- utoipa::path must be present for every endpoint e.g.
#[get(...)]
- all possible responses should be documented
- specific 2xx codes, e.g., we use
201 Created
for successfulPOST
requests - all ways client could behave wrongly1 using 4xx error codes
- specific 2xx codes, e.g., we use
1 i.e., how the API could be wrongly used (preconditions not met etc.)
Example:
#[utoipa::path(
context_path = "/api/maps",
responses(
(status = 200, description = "Fetch a map by id", body = MapDto)
),
)]
Security
All endpoints except of /api/config
must use Keycloak's jsonwebtoken and indicate so using:
#[utoipa::path(
security(
("oauth2" = [])
)
)]