API constructor (beta)
Using Aidbox API constructor you can:
- define API, endpoints, handlers, and URL params schema
- specify your own middlewares (e.g. Smart on FHIR authorization middleware)
Use Run Aidbox locally to start Aidbox, here is configured API constructor example. Add these environment variables:
BOX_PROJECT_GIT_PROTOCOL=https
BOX_PROJECT_GIT_URL=https://github.com/Aidbox/aidbox-project-samples.git
AIDBOX_ZEN_ENTRYPOINT=mybox/box
BOX_PROJECT_GIT_SUB__PATH=aidbox-project-samples/api-constructor
Once Aidbox is running, open
Profiles
tab in the Aidbox UI. If everything is configured properly, page should contain namespace with AIDBOX_ZEN_ENTRYPOINT
symbol which is mybox/box
in this example. View of the symbol should show loaded routes.Here's a notebook with the example API demo usage :
https://aidbox.app/ExportedNotebook/df9ac147-daa4-4495-87b5-c4367cd441ef
Define an Aidbox server definition with
AIDBOX_ZEN_ENTRYPOINT
env, the value must be a namespace/symbol
, e.g.:AIDBOX_ZEN_ENTRYPOINT=mybox/box
The namespace with entrypoint symbol must be loaded: file containing namespace mentioned in
AIDBOX_ZEN_PATHS
and imported or specified directly inAIDBOX_ZEN_ENTRYPOINT
env.
More info on loading namespaceEntrypoint symbol must be tagged with
aidbox/system
tag. aidbox/system
describes a set of services to start and configurations. box
{:zen/tags #{aidbox/system}
:zen/desc "test server"
:services {:http server}}
A service contains
engine
and its configuration.aidbox/http
- Describes http service, contains set ofapis
. Each api must be tagged withaidbox.rest/api
.
server
{:zen/tags #{aidbox/service}
:engine aidbox/http
:apis #{api}}
An
api
describes routing, route can contain operations, subroutes and other apis. Operations must be tagged with aidbox.rest/op
.:middlewares
can be added to an api
. api
{:zen/tags #{aidbox.rest/api}
"multi-example" {"Patient" {:apis #{get-patient-api change-patient-api}}}}
change-patient-api
{:zen/tags #{aidbox.rest/api}
:middlewares [inject-tenant-mw]
:POST multi-box.operations/create-patient
[:id] {:PUT multi-box.operations/update-patient
:DELETE multi-box.operations/delete-patient}}
An
op
describes REST operation. :engine
specifies what operation handler should be used for this operation. Each engine can accept own parameters create-patient
{:zen/tags #{aidbox.rest/op}
:engine aidbox.rest.v1/create
:resource "Patient"
:format "fhir"}
aidbox.rest.v1/aidbox-action
- expects:action
, passes request to existing Aidbox action. You can see list of available operations with this request:GET /Operation?_elements=action&_result=array&_count=1000
aidbox.rest.v1/echo
- expects:response
in the definition, returns the response.
Expect target resource type as
:resource
and :format
(fhir
or aidbox
)aidbox.rest.v1/search
aidbox.rest.v1/create
aidbox.rest.v1/read
aidbox.rest.v1/update
aidbox.rest.v1/delete
aidbox.rest.v1/patch
aidbox.rest.v1/transaction
aidbox.rest.acl/search
aidbox.rest.acl/create
aidbox.rest.acl/read
aidbox.rest.acl/update
aidbox.rest.acl/delete
See full description and usage examples:
We provide Gateway feature to make Aidbox a proxy server for your backend application, so you can:
- maintain a single entry point for your application
- extend the default Aidbox APIs to include your own business logic
- secure access control to your service
As an example bellow we create CRUD endpoint that explains to Aidbox where is your server located and what endpoints we have to redirect
{ns system
import #{aidbox aidbox.rest aidbox.rest.v1}
custom-backend
{:zen/tags #{aidbox.rest/op}
:engine aidbox.rest.v1/gateway
:url "http://host.docker.internal:9999"
:secret #env secret_from_envs }
api
{:zen/tags #{aidbox.rest/api}
"extension-endpoint" {:GET custom-backend
:POST custom-backend
[:id] {:PATCH custom-backend
:DELETE custom-backend}}}
server
{:zen/tags #{aidbox/service}
:engine aidbox/http
:apis #{api}}
box
{:zen/tags #{aidbox/system}
:services {:http server}}}
ingestion.core/map-to-fhir-bundle
- expects :format
("fhir" or "aidbox") and :mapping
in the definition. Returns result of applying lisp/mapping in to the provided data structure and persisting it as Bundle.The result of the example below will be an
POST/ingestion/map-to-fhir
endpoint accepting the data structure on which the mapping will be applied as the body of the request.{ns ingestion-op-ns
import #{aidbox
aidbox.rest
ingestion.core}
my-mapping
{:zen/tags #{lisp/mapping}
:mapping {:resourceType "Bundle"
:type "transaction"
:entry [{:resource {:resourceType "Patient"}
:request {:method "PUT"
:url "/Patient/zero"}}
{:resource {:resourceType "Observation"
:status (get :status)
:code {:coding [{:system "http://loinc.org"
:code "8867-4"
:display "Respiratory rate"}]
:text "Breathing Rate"}
:subject {:reference "Patient/zero"}
:effectiveDateTime (get-in [:br 0 :dateTime]),
:valueQuantity {
:value (get-in [:br 0 :value :breathingRate]),
:unit "breaths/minute",
:system "http://unitsofmeasure.org",
:code "/min"}}
:request {:method "POST"
:url "/Observation"}}]}}
map-to-fhir
{:zen/tags #{aidbox.rest/op}
:engine ingestion.core/map-to-fhir-bundle
:mapping my-mapping
:format "fhir"}
api
{:zen/tags #{aidbox.rest/api}
"ingestion" {"map-to-fhir" {:POST map-to-fhir}}}
server
{:zen/tags #{aidbox/service}
:engine aidbox/http
:apis #{api}}
box
{:zen/tags #{aidbox/system}
:zen/desc "server"
:services {:http server}}}
Middlewares can change incoming request before executing
op
. Middlewares are applied to aidbox.rest/api
which contains operations or other apis. On request Aidbox routing determines what op
should be executed. Before executing the op
Aidbox collects all middlewares applied to the apis containing the op. Then collected middlewares are applied in sequence to the request.A middleware should be defined with specified
:engine
. The :engine
determines what the middleware will do. Depending on the chosen :engine
middleware can accept different parameters. inject-tenant-mw
{:zen/tags #{aidbox.rest/middleware}
:engine aidbox.rest.v1/transform-middleware
:rules {[:resource :meta :tenantId] [:oauth/user :data :tenantId]}}
change-patient-api
{:zen/tags #{aidbox.rest/api}
:middlewares [inject-tenant-mw]
:POST multi-box.operations/create-patient
[:id] {:PUT multi-box.operations/update-patient
:DELETE multi-box.operations/delete-patient}}
aidbox.rest.v1/match-middleware
- checks if a request satisfies to some pattern. Similar to theAccessPolicy
matcho
engineaidbox.rest.v1/params-middleware
- adds request query-string parameters taking data from the request contextaidbox.rest.v1/transform-middleware
- transforms incoming request taking data from the request contextaidbox.rest.middlewares/transform-request-data
- the same astransform-middleware
but provides more complex functionality
Aidbox configuration with search and read on
Observation
resource available at GET /access/Observation
and GET /access/Observation/<id>
. Defined endpoints also contain a middleware injecting patient
search parameter to ensure that user can only search for Observations for a specific patient.{ns mybox
import #{aidbox aidbox.rest aidbox.rest.v1}
box
{:zen/tags #{aidbox/system}
:zen/desc "test server"
:services {:http server}}
server
{:zen/tags #{aidbox/service}
:engine aidbox/http
:apis #{api}}
api
{:zen/tags #{aidbox.rest/api}
"access" {:apis #{access-api}}}
access-api
{:zen/tags #{aidbox.rest/api}
"Observation" {:middlewares [inject-patient-search-parameter-mw]
:GET obs-search-op
[:id] {:GET obs-read-op}}}
obs-search-op
{:zen/tags #{aidbox.rest/op}
:engine aidbox.rest.v1/search
:resource "Observation"
:format "aidbox"}
obs-read-op
{:zen/tags #{aidbox.rest/op}
:engine aidbox.rest.v1/read
:params {:patient observation-patient-param}
:resource "Observation"
:format "aidbox"}
inject-patient-search-parameter-mw
{:zen/tags #{aidbox.rest/middleware}
:engine aidbox.rest.v1/params-middleware
:params {:patient {:path [:user :data :pid]
:required true}}}
observation-patient-param
{:zen/tags #{aidbox.rest/param}
:engine aidbox.rest.v1/param
:search-by aidbox.rest.v1/search-by-knife
:name "patient"
:expression [["subject" "id"]]}}