I want to propose a few changes to the REST module. Most of these are motivated by trying to improve the typings and documentations of our REST APIs, although some of them are miscellaneous. I don’t think any of the proposed changes breaks backwards compatibility.
Explicitly specify search handler id in URL when making search requests
Almost every REST resource provides a route to do a search. For example GET /ws/rest/v1/encounter?patient=<uuid>
searches for encounters by patient uuid. We want to better document what params are accepted as search parameters, but this turns out to be difficult.
- For a resource, it is possible to have multiple search handlers, and each search chandler can accept different parameters. A search handler is selected either by:
- explicitly passing in a
s=
GET param to specify the search handler id. For example,GET /ws/rest/v1/encounter?patient=<uuid>8&obsConcept=<uuid>&s=byObs
invokes the search handler with idbyObs
- implicitly infer which search handler to use based on the params that are passed in. For example,
GET /ws/rest/v1/encounter?patient=<uuid>8&obsConcept=<uuid>
will cause the REST module to do a best guess and find a search handler that handles the parameterspatient
andobsConcept
. In this case, this also happens to be thebyObs
search handler.
- explicitly passing in a
- It is possible that NO search handlers are selected based on the params passed in. In that case, some REST resources have their own
search()
function (via theSearchable
interface), and that gets invoked. (Essentially thisSearchable#search()
function plays a similar role toSearchHandler
, but it’s just not implemented as such). For example,GET /ws/rest/v1/encounter?patient=<uuid>
fails the param criteria of theEncounter
resources’ search handlers, but invokesEncounter.search()
as a fallback.
The above logic is coded here.
I proposed that:
- we add these routes to support explicitly selecting search handlers. This allows us to document which search handlers are available, and what parameters each search handler expects.
- we make these routes available as POST requests in addition to GET. This doesn’t help with typings, but it solves the problem of URL character limits we run into when our GET params are too long, especially when we provide long
v=custom:...
strings. (FHIR does something similar)
So the new routes would look something like this:
GET /ws/rest/<resource>/search/
selects the SearchHandler with id"default"
POST /ws/rest/<resource>/search/
(same as GET)GET /ws/rest/<resource>/search/<searchHandlerid>
selects the SearchHandler with the givensearchHandlerId
POST /ws/rest/<resource>/search/<searchHandlerid>
(same as GET)
SearchHandler
, compared to Searchable#search()
, has an additional advantage of needing to declare its required and optional parameters (example). While we still need to inspect the code to determine the typing of each parameter, they are easier to document that Searchable#search()
(see this for comparison). Overtime, I think we should rewrite our REST resources to favor SearchHandlers
over the search()
/ doSearch()
functions in the Resource
classes.
Explicitly return the representation of a resource.
When we fetch a resource, we can include a v=
parameter specify its representation. For most resources, we can do something simple, like v=default
, v=full
, or provide a custom representation string like v=custom:(uuid,display)
. It is often the case that the returned resource transitively include other resources as well. For example, when fetching a Visit
resource with default
rep, it also transitively returns its patient, location, and encounters. Here is a sample (some fields omitted):
{
"uuid": "c601d849-7f3a-4e8f-b006-4556887013ff",
"display": "Home Visit @ Site 42 - 03/09/2025 06:19 PM",
"patient": {
"uuid": "1336ec3e-7ea9-4883-8a5b-f0332b9e84d8",
"display": "10001F0 - Sandra Walker",
},
"visitType": {
"uuid": "d66e9fe0-7d51-4801-a550-5d462ad1c944",
"display": "Home Visit",
},
"indication": null,
"location": {
"uuid": "92dbdbdf-17da-4cf0-873c-ad15dfae71cb",
"display": "Site 42",
},
"startDatetime": "2025-03-09T18:19:21.000+0000",
"stopDatetime": "2025-03-09T18:41:21.000+0000",
"encounters": [
{
"uuid": "f616575b-2415-49df-9c57-e915f238dffa",
"display": "Lab Results 03/09/2025",
},
],
"resourceVersion": "1.9"
}
Currently, our documentation is not mature enough to specify the presentation for the transitively fetched resources. (Most of them should be of ref
representation, but there is no guarantee.) I proposed that, for each resource, we explicitly include an extra field to specify its representation. This allows us to better discern the typings of the resources that are returned. For example, the above JSON would be augmented to this:
{
"uuid": "c601d849-7f3a-4e8f-b006-4556887013ff",
"display": "Home Visit @ Site 42 - 03/09/2025 06:19 PM",
"rep": "default", // could also be "full", "custom" or other named representations
"patient": {
"uuid": "1336ec3e-7ea9-4883-8a5b-f0332b9e84d8",
"display": "10001F0 - Sandra Walker",
"rep": "ref"
},
"visitType": {
"uuid": "d66e9fe0-7d51-4801-a550-5d462ad1c944",
"display": "Home Visit",
"rep": "ref"
},
"indication": null,
"location": {
"uuid": "92dbdbdf-17da-4cf0-873c-ad15dfae71cb",
"display": "Site 42",
"rep": "ref"
},
"startDatetime": "2025-03-09T18:19:21.000+0000",
"stopDatetime": "2025-03-09T18:41:21.000+0000",
"encounters": [
{
"uuid": "f616575b-2415-49df-9c57-e915f238dffa",
"display": "Lab Results 03/09/2025",
"rep": "ref"
},
],
"resourceVersion": "1.9"
}
This will bloat the returns result by a bit (< 20 bytes per resource), but this should small enough to be acceptable.
Allow specifying returned data format (XML vs JSON) as a GET / POST param
Currently, the data format returned by the REST module is specified using the "Content-Type"
HTTP request header. A request with "Content-Type: application/json;
will return JSON data as response. If the header is not specified, it returns XML by default.
I propose that we add a new parameter format=
in our GET / POST request as an additional way to specify the returned data format. (FHIR does something similar with the _format
param). This just mostly helps with development / debugging.