-
Notifications
You must be signed in to change notification settings - Fork 105
Design: Server CRUD APIs
Server APIs are the backbone of any data-driven application. In bhima, most database entities have an HTTP frontend to allow the client to perform CRUD (Create, Read, Update, and Delete) operations on them. This guide will formalize the best practices for HTTP APIs so far. Note that /resource
should be a plural noun.
HTTP Verb | HTTP Route | Controller Method | Expected Response Codes |
---|---|---|---|
GET | /resource |
ctrl.list() |
200 (OK) |
GET | /resource/:id |
ctrl.detail() |
200 (OK), 404 (NOT FOUND) |
POST | /resource |
ctrl.create() |
201 (CREATED), 400 (BAD REQUEST) |
PUT | /resource/:id |
ctrl.update() |
200 (OK), 404 (NOT FOUND), 400 (BAD REQUEST) |
DELETE | /resource/:id |
ctrl.delete() |
204 (NO CONTENT), 404 (NOT FOUND) |
The above table shows the standard mapping from CRUD routes to HTTP endpoints. Server APIs for bhima should use this as a broad template of what to expect.
-
ctrl.list()
The list function is always expect to return a list of zero or more JSON objects to the client. Importantly, the JSONs returned are not the full resource! They are simply anid
and one or two human readable identifiers. Here are some examples: - For
/patients
, the server may respond with theuuid
,first_name
, andlast_name
of every patient. - For
/accounts
, the server should respond with theid
,account_number
andaccount_txt
of every account. - For
/projects
, the server should respond with theid
andlabel
of every project. -
ctrl.detail()
The detail function returns a single JSON record from the database matching the requestedid
. This object is expected to contain all relevant columns of the database table. In some case, it may include additional joiner tables when helpful. For example: - For
/patients/:id
, the server should respond with all properties in the patient table. - For
/inventory/:id
, the server may respond with all properties in the inventory table, and may attach inventory group and inventory information. - For
/accounts/:id
the server should respond with all properties of the account table, and may additionally attach account type information. -
ctrl.create()
The create function is expected to create a resource, and return a JSON object with a single property: theid
of the resource. If the client has not provided enough information to create the resource, provided invalid information, or send corrupt data, the server should respond with a400 Bad Request
error. -
ctrl.update()
The update function is expected to modify a resource, and return a JSON object with the full resource. If the resource is not found, the controller should respond with a404 NOT FOUND
error. If the client provided invalid or corrupt data, the server should respond with a400 Bad Request
error. -
ctrl.delete()
The delete function is expected to remove a resource from the database. If the resource is not found, the server should return a404 Not Found
error. If the deletion is successful, the server should simply return a204 No Content
message to the client without a body.
Some resources span multiple tables. For example, a cash payment is stored in the cash
and cash_item
tables. Other resources like this are journal vouchers, patient invoices, and purchase orders. This relationship is elegantly expressed in JavaScript via JSON.
// an example cash payment
var cashPayment = {
uuid : 'some-uuid',
currency_id : 1,
/* ... other cash payment properties ... */
/* the cash_items are stored in the cash.items property */
items : [{
uuid : 'some-uuid-2',
amount : 1.57,
// ...
}, {
uuid : 'some-uuid-3',
amount : 3.12
// ...
} /*, { ... } */]
};
Linked resources should be represented in JavaScript in this fashion. This means that GET /cash/some-uuid
would be expected to return the above resource. Similarly, to create this resource, a POST /cash
request would have been sent with the resource.
Every API endpoint should be able to generate id
s on creation. For some resources that have auto-increment id
s, this process is done in the database. For uuid
s, the uuid
is generated in JavaScript.
When considering where to create the uuid
, developers should always favor the server for the following reasons:
- Easier to test - integration tests can be made rapidly for a large number of test cases
- Cleaner client code (no need to import
uuid
, etc) - Slightly smaller
POST
request.