-
Notifications
You must be signed in to change notification settings - Fork 111
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 anidand one or two human readable identifiers. Here are some examples: - For
/patients, the server may respond with theuuid,first_name, andlast_nameof every patient. - For
/accounts, the server should respond with theid,account_numberandaccount_txtof every account. - For
/projects, the server should respond with theidandlabelof 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/:idthe 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: theidof 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 Requesterror. -
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 FOUNDerror. If the client provided invalid or corrupt data, the server should respond with a400 Bad Requesterror. -
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 Founderror. If the deletion is successful, the server should simply return a204 No Contentmessage 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 ids on creation. For some resources that have auto-increment ids, this process is done in the database. For uuids, 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
POSTrequest.