Skip to content

Design: Server CRUD APIs

Jonathan Niles edited this page Oct 9, 2018 · 3 revisions

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.

API Design

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.

Methods in Depth
  1. 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 an id and one or two human readable identifiers. Here are some examples:
  2. For /patients, the server may respond with the uuid, first_name, and last_name of every patient.
  3. For /accounts, the server should respond with the id, account_number and account_txt of every account.
  4. For /projects, the server should respond with the id and label of every project.
  5. ctrl.detail() The detail function returns a single JSON record from the database matching the requested id. 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:
  6. For /patients/:id, the server should respond with all properties in the patient table.
  7. For /inventory/:id, the server may respond with all properties in the inventory table, and may attach inventory group and inventory information.
  8. For /accounts/:id the server should respond with all properties of the account table, and may additionally attach account type information.
  9. ctrl.create() The create function is expected to create a resource, and return a JSON object with a single property: the id 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 a 400 Bad Request error.
  10. 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 a 404 NOT FOUND error. If the client provided invalid or corrupt data, the server should respond with a 400 Bad Request error.
  11. ctrl.delete() The delete function is expected to remove a resource from the database. If the resource is not found, the server should return a 404 Not Found error. If the deletion is successful, the server should simply return a 204 No Content message to the client without a body.

Linked Resources

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.

Server-Side versus Client-Side UUID Creation

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:

  1. Easier to test - integration tests can be made rapidly for a large number of test cases
  2. Cleaner client code (no need to import uuid, etc)
  3. Slightly smaller POST request.