Skip to content

About

CoreComponents API for the NodeNormalization service

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

36 Commits

Folders and files

Repository files navigation

This repository maintains a set of biomedical knowledgebase APIs built with the BioThings SDK. These APIs are either the Knowledgebase APIs built for the Translator Project or the "pending" APIs to be integrated into the official BioThings APIs (e.g. MyGene.info, MyVariant.info, MyChem.info, MyDisease.info etc.)

The list of Translator-associated knowledgebase APIs are hosted at: https://biothings.ncats.io.

There are additional APIs are hosted at https://pending.biothings.io.

Knowledgebase APIs for the Translator Project

Each knowledgebase API is created as a "data plugin" (see examples under plugins folder). The BioThings SDK package will then process the data plugin and turn it into a hosted "BioThings API". You can follow the tutorial of the data plugin for more details.

How to add a new API

Our internal developement team will handle the process of adding a new data plugin and deploying it as a new API. For our internal developers, please follow this documentation

How to update data for an existing API

For external collaborators who have submitted their "data plugins" as new APIs, you can follow this workflow to request a update of your data:

https://github.com/biothings/biothings_explorer/blob/main/docs/README-maintaining-a-data-source.md

The documentation is maintained at the biothings_explorer repository, as each knowledgebase API will be integrated into the BioThings Explorer application become a Translator's standard KP (Knowledge Provider) API.

Running Node Normalization locally

With the project installed in .venv, start the server from the repository root:

NODENORM_DEPLOYMENT_ENV=local .venv/bin/python -m nodenorm --host=127.0.0.1 --port=8000

This explicitly reports "deployment_environment": "local" in GET /status.

The default configuration connects to Elasticsearch at http://localhost:9200 using the annotator-nodenorm index or alias. Add --conf=/path/to/config.local.json for local overrides such as connection headers.

Local defaults enable webserver.SETTINGS.debug and webserver.SETTINGS.autoreload. Changes to imported Python files automatically restart the server. JSON configuration and OpenAPI version changes require a manual restart. The Docker configuration disables debug mode and autoreload.

Reporting the deployment environment

GET /status reports deployment_environment from explicit deployment configuration. Set NODENORM_DEPLOYMENT_ENV to the environment name: CI, Prod, Test, or local. Use NODENORM_DEPLOYMENT_ENV=local when running locally to override the unknown fallback. You can also set "deployment_environment": "local" at the top level of the JSON file passed through --conf. A nonempty environment variable takes precedence. The value is loaded at startup and defaults to unknown when unset; request headers, hostnames, and test-runner settings do not determine it.

Kubernetes deployments pass the pod's gov.nih.ncats.appenv label into NODENORM_DEPLOYMENT_ENV through the Downward API. Set that label in the environment's Helm values under ncats.labels. DevOps supplies CI, Prod, or Test, and the API preserves that capitalization. The chart defaults to unknown. Restart the service after changing the environment configuration.

Selecting a Babel release in Node Normalization

Starting with API version 1.1.0, clients can select an available Babel release for each request. Omitting babel_version uses the configured default release.

  • GET /get_normalized_nodes, GET /get_semantic_types, and both GET and POST /get_setid accept the optional babel_version query parameter.
  • POST /get_normalized_nodes accepts an optional babel_version string in its JSON body.
  • POST /get_setid keeps its existing array body; its query parameter selects one release for the entire batch.

For example, select 2025sep1 with GET /get_normalized_nodes?curie=MESH:D014867&babel_version=2025sep1, or send:

{
  "curies": ["MESH:D014867"],
  "babel_version": "2025sep1"
}

Successful data responses include X-Babel-Version, which is also exposed to browser clients through CORS. Unknown, removed, or malformed selectors return HTTP 400 with the available release names. An unavailable backend returns an error. Requests never fall back to another release.

GET /status groups release information under babel. Its default_version field names the default release, available_versions maps each available release to its release URL, and indices maps the same release names to arrays of selected concrete Elasticsearch index names:

{
  "status": "running",
  "deployment_environment": "CI",
  "version": "1.2.0",
  "babel_version": "2026jul22",
  "babel_version_url": "https://github.com/NCATSTranslator/Babel/blob/main/releases/2026jul22/README.md",
  "backend": "elasticsearch",
  "biolink_model_toolkit_version": "v4.4.3",
  "babel": {
    "checked_at": "2026-10-23T12:00:00+00:00",
    "default_version": "2026jul22",
    "available_versions": {
      "2025sep1": "https://github.com/NCATSTranslator/Babel/blob/main/releases/2025sep1/README.md",
      "2026jul22": "https://github.com/NCATSTranslator/Babel/blob/main/releases/2026jul22/README.md"
    },
    "indices": {
      "2025sep1": ["nodenorm_2025sep1"],
      "2026jul22": ["nodenorm_2026jul22"]
    }
  }
}

The top-level babel_version, babel_version_url, and backend fields are restored as a temporary compatibility remedy for existing clients such as ORION. babel_version always equals babel.default_version, and babel_version_url equals babel.available_versions[babel.default_version]; both follow default-release refreshes. backend is always "elasticsearch". New clients should use the babel object for multi-release metadata and explicitly select a release on normalization requests when needed.

Index names in this example are illustrative. Each indices array contains the concrete targets selected by the latest successful discovery snapshot. The default release uses every backing index of its configured alias; other releases select one index. Available releases come from Elasticsearch metadata, rather than a configured version list; these maps do not indicate backend health.

Discovering available releases

At startup and every 10 minutes in the background, the server uses its existing ES_HOST client to discover release indices from mappings._meta.src.nodenorm metadata on the associated cluster. No version list, version-to-index mapping, or per-release alias is required. The client's Elasticsearch permissions must allow reading mappings across that cluster. Discovery uses an explicit version field, or a dated release tag in the legacy source URL; it does not infer versions from index names. Unrelated indices and invalid release metadata are ignored.

For supported releases outside the default alias, group indices by their Babel release metadata and choose one build using this rule:

  1. Prefer the newest valid ISO build date in mappings._meta.build_date. Compare dates in UTC; timestamps without a timezone are treated as UTC. A valid date ranks above missing or malformed dates.
  2. If build dates are equal, or all candidates lack valid dates, choose the lexicographically greatest concrete index name. This fallback provides a stable ordering; it does not establish the age or release of an index.

The default is different: ES_INDEX / ES_ALIAS is authoritative for both its release and its exact concrete backing indices. All targets must identify the same Babel release. The default keeps those targets even if newer builds exist elsewhere on the cluster; a multi-index default queries all its backing indices. Explicitly requesting the default Babel version uses those same alias targets. After the alias moves to another release and discovery refreshes, the former default becomes a supported nondefault release and uses the build-date rule above.

The default release has no separate Babel configuration override. Configuration is loaded at startup. Adding a release or a newer build makes it available on the next successful background refresh; adding a release does not automatically change the default. Moving the configured default alias takes effect on the next successful refresh, without restarting the API. Changing the configuration itself requires a restart.

Each process refreshes independently, even without incoming requests. A refresh has a 10-second total deadline. It publishes the new release registry and default query indices together, so /status and new requests use the same snapshot. Each request keeps its selected concrete indices throughout processing, including conflation lookups. Keep previous indices available until every process has refreshed and requests using the previous snapshot have completed. Verify /status and sample results before removing indices; discovery relies on metadata accurately describing each index's contents.

babel.checked_at is the UTC time of the last successful discovery. A failed refresh retains that snapshot and timestamp, adds babel.refresh_error with code discovery_failed, and retries after 30 seconds. A successful retry clears the error. Discovery failures alone do not determine service health.

GET /status checks that every selected default index still exposes the expected Babel source version. These separate health results are cached for 5 seconds on success and 1 second on failure, with concurrent requests sharing one check and a 3-second total deadline. Changing the default version or its concrete indices causes the next status request to use a new health check. A failed check returns HTTP 503; an expired healthy result is never served after a failed check. Health checks do not trigger Babel discovery. All status responses send Cache-Control: no-store.

OpenAPI document

API version 1.2.0 corrects conflation and semantic-type responses and rejects invalid boolean flags. It retains the multi-Babel selectors, response headers and status health checks added in 1.1.0.

The API is described by src/nodenorm/webapp/openapi.json, which is written by hand and served verbatim at /webapp/openapi.json (with /openapi.json redirecting there); the Swagger UI at / renders it. /status reports its info.version as the API version, so bump that when the API changes. tests/test_openapi.py checks that the document validates and that its paths, methods, query parameters, POST body fields and the SetIDResponse schema match the handlers, so a handler change that isn't reflected in the JSON fails the test suite. The parameter and body checks find what each HTTP method reads by inspecting literal argument names in Tornado get_argument(s) and get_query_argument(s) calls, and literal body fields read with .get("…"). They follow handler helper methods and resolve local parameter references. Dynamic names still need explicit coverage.

About

CoreComponents API for the NodeNormalization service

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages