Coding Guidelines - API

== Basics ==
These rules are supplemental to the general Coding Guidelines, they are not an alternative. 
A guide for creating a new api endpoint can be found here. 
== RESTful API ==
Swagger2/OpenAPI is a RESTful API Specification Language; It allows us to describe our RESTful API in such a way that others may benefit from automated API consumer code generation in their applications. Care should be taken to 'design' your API routes as thoroughly as possible to enable others to use it reliably. 
Koha code wise, we have opted to base our new API on the modern Perl web framework Mojolicious and use the Mojolicious Plugin for Swagger2 to take advantage of 'Specification first programming'. 
=== REST1: Resources (Swagger Paths) ===
==== REST1.1: Distinct routes ====
Distinct paths should be used for distinct operations, don't be tempted to re-use a path. For example: 
USE
"/users": {
 "...": "..."
},
"/users/{user-id}": {
 "...": "..."
}
NOT
"/users/{user-id}": {
 "...": "...",
 "parameters": [
 { 
 "name": "user-id",
 "required": false,
 "...": "..."
 }
 ]
}
 
==== REST1.2: Resource names ====
As far as possible an api route should be 'guessable' for the uninitiated api consumer. Thus, routes that are not discipline specific should attempt to use generic widely recognised terms.
Resource names, and their attributes names can be discussed in developer meetings and the use of RFCs is encouraged.
Example: 
USE 
/users
 
NOT 
/borrowers
OR
/patrons
OR
/members
 
In this case, 'borrowers'/'patrons' and 'members' are very Koha specific. Along with that they do not actually distinguish between staff and public users. The generic and widely recognised 'users' term is more appropriate here (and this is likely highlighting a bad coding decision from koha's history ;) ) 
NOTE: It is also recommended to use plural form for route names, and singular form/verbs for actions suffixes 
==== REST1.3: Resource description ====
All resources should be defined in full in their own definition file. 
===== REST1.3.1: type =====
All fields of a resource should have a type associated with them 
===== REST1.3.2: required =====
All resources should have a list of required fields specified 
===== REST1.3.3: description =====
Although not strictly required, a brief description of what the field should contain is very useful for the api consumer 
===== REST1.3.4: mapping =====
API fields require a mapping from database/object field name to api field definition. The following guidelines must be followed to maintain consistency across endpoints
====== REST1.3.4.1 date/datetime/timestamp fields ======
Where a field contains a 'date' it should be consistently named *_date as opposed to date_* and it should always return a full datetime.
====== REST1.3.4.2 action record fields ======
Fields containing a record of an action should be consistently named as `action_data` and the action should be in the past tense.
Examples:
* `created_date` - date of creation of the record
* `modified_date` - date of last modification of the record
* `submitted_date` - ...
* `accepted_date` - ...
====== REST1.3.4.3 relation fields ======
Fields containing a key for a related object should be named related_id where related is the relation name from the object and if called via embed should be replaced by just the relation name.
Examples:
* `patron_id` with `patron` returning a Koha::Patron object
* `manager_id` with `manager` returning a Koha::Patron object
* `creator_id` with `creator` returning a Koha::Patron object
=== REST2: HTTP Methods ===
We are following RESTful best practice which means using HTTP methods to denote stateful actions.. this roughly equates to: 
*POST - Create 
*GET - Read 
*PUT - Update 
*DELETE - Delete
With the addition of PATCH for partial update.
Sometimes 'actions' do not align nicely to the main object. For these cases the recommendation is to map an action resource atop the main resource; for example POST /users/{id}/block to block a user and DELETE /users/{id}/block to remove it. 
=== REST3: Requests and Responses ===
==== REST3.1: Request Parameters ====
All request parameters should be specified in the swagger specification file 
==== REST3.2: Response Codes ====
All achievable response codes should be specified in the swagger specification file 
===== REST3.2.1 POST =====
Successful create operations must return 201.
===== [DRAFT] REST3.2.1.1 POST (error, duplicate) =====
When a create operation fails due to duplicate object, it must return 409.
===== REST3.2.2 GET =====
Successful read operations must return 200.
===== REST3.2.3 PUT =====
Successful update operations must return 200.
===== REST3.2.4 DELETE =====
Successful delete operations must return 204.
===== [DRAFT] REST3.2.4.1 Conflict =====
When a DELETE operation fails due to a conflict (e.g. FK constraint blocks deletion), it must return 409.
==== REST3.3: Response Bodies ====
===== REST3.3.1 POST =====
Successful create operations should always return the created resource representation.
===== REST3.3.2 GET =====
Successful read operations should always return the resource representation.
===== REST3.3.3 PUT =====
Successful update operations should always return the updated resource representation.
===== REST3.3.4 DELETE =====
Successful delete operations should always return an empty response body.
==== REST3.4 Response headers ====
===== REST3.4.1 POST =====
Create operations should always include the Location header, pointing to the created object.
=== REST4: Controller code [DRAFT] ===
Once you write a spec for your endpoint, you need to write a method that handles the request.
The /cities endpoint is considered the reference for new endpoints. They are the simplest use cases
but should probably cover most of what is needed.
As such, it is considered mandatory to look at the Koha::REST::V1::Cities class before starting to code.
==== REST4.0: The basics ====
Every controller needs to start with
my $c = shift->openapi->valid_input or return;
This snippet triggers the request parameters validation (headers, query parameters, body, etc).
All controller code needs to be wrapped inside a try/catch block, and exceptions properly handled
(i.e. mapped to the right combination of error codes, descriptions and HTTP status). There needs to
always be a fallback to the $c->unhandled_exception($_) helper.
Example:
return try {
 $object->do_something;
 ...
} catch {
 if ( blessed($_) ) {
 if ( ref($_) eq 'Koha::Exceptions::...' ) {
 # handle specific situation
 }
 }
 # fallback to a generic 500, handled properly
 $c->unhandled_exception($_);
};
==== REST4.1: Returning Koha::Object(s)-based artifacts ====
Every Koha::Object(s)-based class knows how to render itself for the API response. This includes
attribute name mapping, hidding and even calculated values.
If at some point your controller has Koha::Object(s)-based objects that need to be sent as
the response payload, this should be done like this (assuming a 200 status code here, but
each use case might need it's own code):
return $c->render(
 status => 200,
 openapi => $c->objects->to_api($koha_object_s_based_thing),
);
==== REST4.2: Using query parameters to find the required objects ====
Often, you will need to use the query parameters to retrieve the required objects. This is done
like this:
my $city = Koha::Cities->find($c->params('city_id'));
A more complex could be GET /patrons/:patron_id/checkouts. In this case you would do:
my $patron = Koha::Patrons->find($c->param('patron_id'));
my $checkouts = $patron->checkouts;
==== REST4.3: Handling non-existent resources ====
In the section above, in both cases the requested resource might not exist. Either the city_id or the patron_id.
This situation needs to be handled by using the render_resource_not_found() helper (i.e. not manually returning 404
if possible. If the use case requires a different handling it should be discussed with QA).
Picking the patron_id use case, the code should look like:
my $patron = Koha::Patrons->find($c->param('patron_id'));
return $c->render_resource_not_found('Patron')
 unless $patron;
my $checkouts = $patron->checkouts;
==== REST4.4: Deleting a resource ====
When a resource is deleted, REST3.2.4 and REST3.3.4 mandate what the response has to be.
Instead of crafting it manually, the render_resource_deleted() helper needs to be used:
$city->delete;
return $c->render_resource_deleted();
Category:Development