/clinical-api-plugins

DrugBank Medication Search Plugin Technical Implementation Guide

Primary LanguageHTMLMIT LicenseMIT

Clinical Plugins Technical Implementation Guide

This guide details how to use our clinical plugins. It includes information on how to download our plugin library as well as information on compatibility, authentication options, and debugging that is common to all plugins. It also contains specific information on setting up our Medication Search Plugin, Condition Search Plugin, ICD-10 Search Plugin, and Drug-Drug Interactions Plugin.

Compatibility

The plugins provided here are Web Components. They should work in most modern browsers, including: Firefox (as of version 63), Chrome, Opera, Safari, and Edge (as of version 79).

Download and setup

Download the drugbank-ui.min.js file here. As of v0.2.0, you do not need to include Vue separately.

Place it in the same location in your project that other 3rd party javascript files reside. On an HTML page where you want to use it, you will need to load this file before you can use the web component.

<script src="YOUR/JAVASCRIPT/LOCATION/drugbank-ui.min.js"></script>

For v0.1.0 (deprecated)

Previous versions of this library had a dependency on VueJS 2.6.12 and required Vue to be loaded separately via:

<script src="https://unpkg.com/vue@2.6.12"></script>

Current versions of the library are based on Vue 3.2's new web component support and include an inline version of Vue, so you do not need to load it separately. This will also not conflict with another version of Vue you may have running.

Authentication

Authentication, via JSON Web Tokens (JWTs) works the same for all DrugBank clinical plugins. This allows you to implement our plugins easily, without any additional server side setup, though for production code, we do recommend some minimal server side code (mentioned below) to facilitate automatic refreshing of short-lived JWTs.

JWT Attributes

  • jwt: A raw JWT string. If you set this, your component can run without any interaction with your server until the JWT expires.
  • refresh-jwt: A relative or full URL which, when it receives a GET request will provide a new, valid JWT for the DrugBank API. More detailed instructions for implementing this endpoint can be found below.
  • refresh-interval: When setup with refresh-jwt, the plugins will automatically check whether the current JWT has expired. This is a completely client-side check, and by default we do it every 60 seconds. If that is too short or too long, you may want to adjust this parameter. Note that your tokens should have a lifetime that is at least 3x the refresh-interval, as once you get within 2x the refresh-interval, the plugins will try to get a new token.
  • shared-jwt: Set this to true if you want to share JWTs between plugins to cut down on calls to refresh JWTs. More explanation below (default: false).

Obtaining a JWT

You can find detailed information about generating tokens in our API docs. Generation of JWTs should always be done in server side code, as it requires your API key, which you should never expose to end users.

Automatic refreshing of JWTs

JSON Web Tokens allow authenticated access to our API without having to expose your API key. You will still want to limit their lifespan as much as possible to ensure that they are only usable on your site. We recommend creating JWTs that expire in 15 minutes or less for this purpose. While you can generate your JWT and insert it via the jwt attribute as part of your page rendering, we also provide another way of keeping your component authenticated with short-lived tokens.

The following sets a path that the component can access to get a refreshed JWT:

<db-medication-search name="medication-1" refresh-jwt="/path/to/refresh"></db-medication-search>

The /path/to/refresh should exist on your site (e.g. https://your-site.com/path/to/refresh) behind your own authentication/security (eg. cookie/session based logins). The only requirement is that the endpoint returns the JWT as a raw string. The following is an example of an endpoint using the ruby sinatra framework that provides JWTs that last for 15 minutes. You can adapt this to your language/framework of choice.

require 'uri'
require 'net/http'

class MockApp < Sinatra::Base
  def refresh_jwt
    @conn ||= Faraday.new(
      url: "https://api.drugbank.com",
      headers: {
        'Content-Type' => 'application/json',
        'Authorization' => 'your-super-secret-api-key',
        'Cache-Control' => 'no-cache'
      }
    )
    res = @conn.post("/v1/tokens") do |req|
      req.body = { ttl: '15m' }.to_json
    end
    JSON.parse(res.body)['token']
  end
end

By default, the component checks whether the JWT needs to be refreshed every 60 seconds. This can be changed by setting the refresh-interval attribute.

Sharing JWTs (new in 0.3.0)

If you have many plugins on a page, where each one can refresh its token independently, this can result in unnecessary network traffic. For any plugin that has refresh-jwt set, you can now also set shared-jwt="true" in order to have them opt into token sharing. When multiple plugins have this setting, only the first one that detects a need to refresh a token will make the network call and store the results in session storage. Any other plugins will first look in the session storage to see if there's a valid token they can use before trying to get one via a network call.

The shared token will also persist over page reloads, which can result in much quicker initialization in subsequent steps that also use DrugBank plugins. This storage will be cleared when the user closes the browser tab.

Debug logging

We sometimes log warnings if the plugin receives settings that aren't appropriate, even if it can recover from them. However, all logging is turned off by default. A good first step to take if the plugin isn't operating as expected is to include the following meta tag where you include the drugbank-ui script tag:

<meta name="DRUGBANK_UI_LOG_LEVEL" content="info" />

We recommend using this when developing. The level can also be set to 'trace', 'debug', 'warn', 'error', or 'silent' (the default).

Medication Search Plugin

medical-search-plugin

Basic use

The most basic way you can use our medication search is the following:

<form>
  <db-medication-search name="medication-1" jwt="your-jwt-token"></db-medication-search>
</form>

When you submit this form, the DrugBank product concept ID representing your selected medication will be sent in the same way that regular form <input> values are sent, where the parameter name will match the name value. In the above example, the selected medication DBPCID would be accessible via the "medication-1" parameter in whatever code is processing the form.

Initializing with an existing value

If you've stored the DBPCID for a previously selected medication, you can initialize the lookup with that by setting the value attribute. For example:

<db-medication-search 
  name="medication-1"
  value="DBPC0726274" 
  jwt="your-jwt-token"
></db-medication-search>

will initialize the search with "Acetaminophen 100 mg /3mL Oral Liquid".

Listening to change events

If you are using this in a simple form where you only need to know the value of the medication search upon a POST of the form, then the approach above of looking for the named parameter in the code that processes the form is the simplest way to do it. However, if you need to know the value before form submission, you can listen for updates that are sent with every change to the product concept that's selected.

Here's how you can listen to the component defined above (name="medication-1") via pure Javascript:

// Get a reference to the component
medicationSearch = document.getElementsByName("medication-1")[0]

// Attach a listener
medicationSearch.addEventListener("db-value-changed", function(e) {
  console.log(e.detail.drugbank_pcid)
})

The above code simply logs the drugbank_pcid. The db-value-changed event is a CustomEvent, and its detail object looks like this:

{
  "drugbank_pcid": "DBPC1234567"
}

The drugbank_pcid will be null if nothing is selected or the user clears the selection.

Note: This event was previously named medication-changed; although this event is still emitted, it should be considered deprecated. In addition, previous documentation referred to a drugbank_id value in the medication-changed event. While this value still exists, it is also considered deprecated. You should change any code using this search plugin to capture db-value-changed events and only rely on the drugbank_pcid key within the detail object.

Customization

There are many optional attributes that can be set on the lookup that corresponds to Medication Name Search API parameters.

Attributes

  • search-stages: set the order of stages to narrow down the specified product concept. Possible values include strengths, route, and form, and can be passed as a string or a list. Any stages not specified will be ignored.
  • min-level: set a level between 1 and 5 that is the minimum product concept level that should be returned.
  • max-level: set a level between 1 and 5 that is the maximum product concept level that should be returned. It cannot be set below the min-level. A warning level log message will be issued and the value will be internally set to min-level in this case.
  • level: set a level between 1 and 5 that is the only level of product concepts to return. If min-level and/or max-level is set, a warning level log message will be issued. The API will ignore these when level is given.
  • min-search-chars: set the minimum number of characters to enter before making calls to the API. Must be between 1 and 5 (default 3).
  • unbranded: if true, will exclude all branded product concepts from the search (returns generic entries). This can be useful to decrease the number of similar options returned by the search.
  • region: set the region(s) to filter on search results. By default, search results will come from any region in a subscription. This can either be a single region (example: us) or multiple comma-separated regions (example: us,ca,it). See selecting your region in the API documentation for all available region codes. Invalid region codes will cause an error when making the subsequent API call. Whenever there is an API error like this, the lookup will change into an error state and indicate an error communicating with the API.
  • dropdown-on-focus: When set to false, the selection dropdown will only appear when there's text being entered. The default (true) behaviour is for the dropdown to always be open when the search field has keyboard focus.

Custom styling

There are a few aspects of the medication search plugin's style that you can change by setting CSS variables in a containing element:

.containing-class {
  /* Add extra padding to either the lookup or its tags. You cannot add negative padding. */
  --db-lookup-extra-padding-vertical: 10px;
  --db-lookup-extra-padding-horizontal: 20px;
  --db-tag-extra-padding-horizontal: 10px;
  --db-tag-extra-padding-vertical: 10px;
  /* Change the radius of corners on the lookup or its tags. */
  --db-lookup-corner-radius: 5px;
  --db-tag-corner-radius: 10px;
  /* Change the border width around the entire lookup. This cannot be less than 1px. */
  --db-lookup-border-width: 2px;
  /* Change the font used for plugin content. */
  --db-lookup-font-family: courier;
}

Expired JWT Warning

If the JWT expires for any reason, the plugin will no longer remain active and will display in light grey along with a prominent error message:

jwt-expired

The plugin will not be active again until a valid JWT is provided.

API Errors

Although the plugin encapsulates API calls to ensure they are called appropriately, there may still be cases where the underlying call fails. When this happens, the plugin will turn red and an error message will appear:

search-error

Unlike in the case of an expired JWT, the plugin remains active; another successful API call will restore the original plugin colours and remove the error text.

Condition Search Plugin

condition-search-plugin

Basic use

Similar to our medication search plugin, the condition search can be added to a form:

<form>
  <db-condition-search name="condition-1" jwt="your-jwt-token"></db-condition-search>
</form>

When you submit this form, the DrugBank condition ID representing your selected condition will be sent in the same way that regular form <input> values are sent, where the parameter name will match the name value.

Initializing with an existing value

If you have a previously specified condition ID, you can initialize the search using the value prop:

<db-condition-search 
  name="condition-1"
  value="DBCOND0027886" 
  jwt="your-jwt-token"
></db-condition-search>

In this specific example, the search will populate with "Diabetes Mellitus".

Listening to change events

Similar to the medication search plugin, you can also listen for change events.

This works almost identically, with the component emitting a db-value-changed event where the detail object will look like:

{
  "drugbank_id": "DBCOND0027886"
}

The drugbank_id will be null if nothing is selected or the user clears the selection.

Customization

Attributes

At the moment, there is a single attribute that can be specified:

  • min-search-chars: set the minimum number of characters to enter before making calls to the API. Must be between 1 and 5 (default 3).

Custom styling

There are a few aspects of the condition search plugin's style that you can change by setting CSS variables in a containing element. These are the same as outlined for the medication search plugin

Warnings

The condition search handles JWT and search errors the same as the medication search plugin.

ICD-10 Search Plugin

icd10-search-plugin

Basic use

Similar to our medication search plugin, the ICD-10 search can be added to a form:

<form>
  <db-icd10-search name="icd10-1" jwt="your-jwt-token"></db-icd10-search>
</form>

When you submit this form, the ICD-10 identifier representing your selected concept will be sent in the same way that regular form <input> values are sent, where the parameter name will match the name value.

Initializing with an existing value

If you have a previously specified ICD-10 identifier, you can initialize the search using the value prop:

<db-icd10-search 
  name="icd10-1"
  value="A01.1" 
  jwt="your-jwt-token"
></db-icd10-search>

In this specific example, the search will populate with "Paratyphoid fever A (A01.1)".

Listening to change events

Similar to the medication search plugin, you can also listen for change events.

This works almost identically, with the component emitting a db-value-changed event where the detail object will look like:

{
  "identifier": "A01.1"
}

The identifier will be null if nothing is selected or the user clears the selection.

Customization

Attributes

At the moment, there is a single attribute that can be specified:

  • min-search-chars: set the minimum number of characters to enter before making calls to the API. Must be between 1 and 5 (default 3). Note that for the ICD-10 search specifically, it is not advisable to set this attribute to the max value as users may wish to search for ICD-10 concepts by their alphanumeric code, which may be as short as three characters!

Custom styling

There are a few aspects of the ICD-10 search plugin's style that you can change by setting CSS variables in a containing element. These are the same as outlined for the medication search plugin

Warnings

The ICD-10 search handles JWT and search errors the same as the medication search plugin.

Drug-Drug Interactions (DDI) Plugin Guide

ddi-wide-display

Basic use

Our DDI plugin is designed to show the results of an underlying API call to our DDI endpoint. As a display component, it can be included in any reasonable HTML component, such as a div:

<div>
  <db-ddi-viewer name="ddi-1" jwt="your-jwt-token"></db-ddi-viewer>
</div>

When provided with appropriate input properties (see below), the plugin will update its view to reflect the underlying drugs searched and any pairwise interactions found for those drugs. These properties can be specified directly as attributes on the db-ddi-viewer tag and updated at any time. The tag will automatically react to any changes to them.

The results are displayed as a series of entries, each with an expandable footer containing more detailed information about the interaction. By default, interactions are listed in the order major, moderate, minor; clicking the black triangle icon next to the SEVERITY heading reverses this order. Any drug included in the search that does not generate at least one interaction will be displayed at the top in a greyed out label.

Accepted input ID properties

The DDI plugin accepts multiple different kinds of inputs, either as an array or as a comma-separated string:

  • drugbank-id: Drugbank drug IDs
  • drugbank-pcid: Drugbank product concept IDs
  • Any local product ID for our supported regions, specifically:
    • ndc: US products
    • dpd: Canadian products
    • ema-ma-number: EU products with an MA number
    • ema-product-code: other EU products
    • at-product-id: Austrian products
    • co-product-id: Colombian products
    • idn-product-id: Indonesian products
    • it-product-id: Italian products
    • my-product-id: Malaysian products
    • sg-product-id: Singaporean products
    • th-product-id: Thai products
    • tr-product-id: Turkish products

Managing the display

Currently, the DDI plugin has two optional display modes which can be controlled using the display prop. The wide mode (set by default), is intended for most normal web applications:

ddi-wide-display

Setting the display prop to narrow shifts the content around, and is intended for use in applications where allocated screen width is a concern, such as for mobile:

ddi-narrow-display

Examples of Clinical Plugins Implementation

This provides examples of how to use DrugBank's clinical plugins in a real project.

We are writing this in ruby, with the sinatra framework, but you can use whatever language/framework you like.

Getting started

Docker is the easiest way to get this sample application up and running. If you want to set it up without Docker, the Dockerfile will still be a useful resource to understand the required software.

If you have Docker installed, you will just need to do some minimal configuration. Go to the root of your project and run:

cp config.yml.example config.yml

Edit your config.yml to set your DrugBank API key. Then simply run:

docker-compose up -d sample-app

Images will be built if they haven't been built already, and a container will run to serve the sample application on port 8080. You can change this if you like by editing the docker-compose.yml file.

Using the app

Once the app is up and running, go to:

localhost:8080 in your web browser, and you should see a screen like this:

not-logged-in

Clicking the "here" link will log you in as user 345, and you should see the first lookup, where we have a preset value now shows a medication ("Acetaminophen [Tylenol] 160 mg Oral Powder"), while the other two are blank.

logged-in

Enter one or two more medications or change the existing medication if you like, and click submit.

Troubleshooting

Here are some of the most common problems we've encountered in setting this up for the first time, along with basic troubleshooting steps. If none of these work or you run into something else, please contact us.

I can't see anything when I go to localhost:8080 or my browser says the site can't be reached.

Make sure that the services hosting the sample app are up and running. To do this, check:

docker-compose ps

If everything is successfully running, you should see Up under the State column. If you don't see this, try running docker-compose up -d sample-app again. If it still doesn't stay Up, you might be able to find out why from the logs: docker-compose logs sample-app.

The sample app is running, but all the plugins say "Your token has expired".

First make sure you've clicked the "LOGIN" button to simulate a login. This only lasts while the sample app is running, so if you've shut down your computer or Docker and restarted it, your previous login will be lost.

If you still see "Your token has expired" after logging in, make sure you have a proper config.yml file with your API key as explained above. If you have that but are still having problems, check for errors in the server log by running the following in your terminal/console:

docker-compose logs -f sample-app

And then refresh your browser page. You should see new lines appear in the terminal window. Look for any that include ERROR --. The sample app uses your API key to get a temporary token that's safer to expose to client-side applications. If the key was somehow entered incorrectly in config.yml, you'll see messages that indicate it's expired or invalid.

CHANGELOG

v0.5.0:

  • automatic synchronization of internal plugin selection with web component value attribute for all components (making it much easier to set the value of these plugins from custom Javascript elsewhere on the page)
  • fixed issue with setting medication search-stages to an empty string. This will now correctly make the db-medication-search only do the first (non-optional) step of searching for the ingredient (the default when not setting this attribute at all is still to search by ingredient, strengths, route, then form)
  • added dropdown-on-focus option to the db-medication-search, which defaults to true. When set to false, the lookup dropdown will only appear when there's text being entered. The default behaviour is for the dropdown to always be open when the lookup has keyboard focus.
  • added name to db-value-changed event detail for db-medication-search and db-condition-search plugins
  • added description do db-value-changed event detail for db-icd10-search plugin
  • changed meta tag property to the more standard name attribute for turning on various debugging information

v0.4.0:

  • added the db-condition-search and db-icd10-search plugins
  • updated to a uniform search icon across all search plugins
  • changed the medication-changed event to db-value-changed. The existing event is still emitted in addition to the db-value-changed event but should be considered deprecated and will be removed in the future.

v0.3.0:

  • added the db-ddi-viewer component to display drug-drug interaction information
  • fixed db-medication-search not updating when value attribute is changed
  • added ability to set shared-jwt="true" to turn on token sharing, which can reduce network calls when using many plugins on a page
  • reorganized and improved plugin guide
  • medication-changed event now returns an object with a drugbank_pcid. It also provides the same value in drugbank_id, but drugbank_id is now considered deprecated and will be removed in a future release.

v0.2.0:

  • added ability to override the order of and skip search steps via the search-stages attribute
  • added "medication-changed" event to make it easy for other Javascript code to capture changes in the component's state
  • search string highlighting shows matching portions of results while typing
  • added warnings and ability to see them in the browser console with appropriate meta tag setting
  • added max-level, min-level, and level properties, corresponding to max_level, min_level, and level Medication Name Search API parameters
  • added unbranded property, allowing searches to focus on unbranded (generic) options
  • added min-search-chars property to control when the plugin starts performing API calls
  • added region property, corresponding to the parameter of the same name in the Medication Name Search API
  • better error state warnings when there are issues with the JWT token or there are search errors due to improper configuration (though we try to prevent these from happening when possible)
  • added ability to provide custom styling on lookup and tag corner radius, lookup and tag padding, lookup border width, and lookup font family
  • updated the internal Vue library used (to Vue 3) and inline Vue within the package. You no longer need to import Vue separately and it will not conflict with any other versions of Vue you may need on the same page.
  • updated all tag attributes to use more conventional dashes instead of underscores (refresh_jwt is now refresh-jwt and refresh_interval is now refresh-interval)
  • various bug fixes and minor UI improvements