- You should download HPO using the URLs1: http://purl.obolibrary.org/obo/hp.obo or http://purl.obolibrary.org/obo/hp.owl
1The official OBO URI/PURL of HPO is http://purl.obolibrary.org/obo/hp.owl.
- Please post issues at our github tracker
- If you want to make a term request, please read this first!
- Please add disease-HPO-class requests also to this tracker!
If you have questions, please contact dr.sebastian.koehler@gmail.com or peter.robinson@jax.org
Consider signing up the HPO mailing list at https://groups.io/g/human-phenotype-ontology
Follow us on twitter under the username @hp_ontology
A containerized REST API for querying the Human Phenotype Ontology. Two server implementations are available — pick whichever fits your stack:
| Implementation | Dockerfile | Base image | Ontology parser |
|---|---|---|---|
| Python (FastAPI) | Dockerfile |
python:3.12-slim |
pronto |
Node.js (Express, TypeScript → dist/) |
Dockerfile.node |
node:22-slim |
custom OBO parser |
Both expose the same endpoints on port 8000:
| Endpoint | Description |
|---|---|
GET /api/ontology |
Ontology metadata (name, version, term count) |
GET /api/terms/{id} |
Term detail — definition, synonyms, xrefs, parents, children |
GET /api/terms/{id}/parents?distance=N |
Ancestor terms up to N hops |
GET /api/terms/{id}/children?distance=N |
Descendant terms up to N hops |
GET /api/search?q=...&limit=N |
Full-text search across names and synonyms |
GET /health |
Health check |
GET /docs |
Interactive Swagger UI |
The compose file uses profiles so only one server runs at a time. Both mount hp.obo (and hp-base.owl for Python) from the repo root.
# Python server
docker compose --profile python up
# Node.js server
docker compose --profile node upAdd -d to run in the background, --build to force a rebuild.
The Node server uses npm workspaces at the repo root (package.json) with packages under packages/: @threevl/hpo-lib (parser + resolvers), @threevl/hpo-middleware (Web Request → Response API), @threevl/hpo-express (Express adapter), and @threevl/hpo-api (standalone server). Build output is under packages/*/dist/ (ignored by git). The root npm run build runs Turborepo (turbo run build via turbo.json), which schedules each package’s vite build in dependency order (hpo-lib → hpo-middleware → hpo-express → hpo-api). Each package uses Vite library mode to emit bundled CommonJS dist/index.js plus rolled-up dist/index.d.ts (see each package’s vite.config.mts). Run npm run build from the repo root so turbo and vite come from this repo’s devDependencies. The root tsconfig.json is minimal; package sources use packages/tsconfig.base.json for editor/typechecking (noEmit).
npm ci
npm run build
HPO_OBO_PATH=/path/to/hp.obo npm startUse npm run dev at the repo root to run vite build --watch in all four packages in parallel (via concurrently).
Packages use lockstep semver: one canonical version on the root package.json, mirrored on @threevl/hpo-lib, @threevl/hpo-middleware, @threevl/hpo-express, and @threevl/hpo-api. Internal deps stay aligned as ^<that-version>.
Bump and refresh the lockfile (manual, not CI):
npm run lockstep:patch # or lockstep:minor | lockstep:majorTo only re-copy the root version onto all workspace packages (e.g. you edited root version by hand): npm run lockstep:sync.
Prerelease tags (0.1.0-rc.1) are not supported by the script; use plain X.Y.Z.
Each published package includes a repository field pointing at this GitHub repo — npm uses it to validate Trusted publishing (OIDC). If you fork or rename the repo, update repository.url in all published packages to match.
Use Node ≥ 22.14 and npm ≥ 11.5.1 (required later for OIDC in Actions). Then:
npm login # browser or token; 2FA accounts need an OTP when publishing
npm ci
npm run build
OTP=123456 # replace with your current authenticator code (same code often works for all three if used quickly)
npm publish -w @threevl/hpo-lib --access public --otp=$OTP
npm publish -w @threevl/hpo-middleware --access public --otp=$OTP
npm publish -w @threevl/hpo-express --access public --otp=$OTP
npm publish -w @threevl/hpo-api --access public --otp=$OTPOr run npm run publish and enter the OTP when npm prompts (may prompt once per publish).
- On npmjs.com, open each of
@threevl/hpo-lib,@threevl/hpo-middleware,@threevl/hpo-express, and@threevl/hpo-api→ Settings → Trusted publishing → GitHub Actions. - Set Repository to your GitHub org/repo (must match
repository.urlinpackage.json, e.g.3VLINC/human-phenotype-docker). - Set Workflow filename to
publish-npm.yml(exact name, including.yml). - Save. Repeat for all four packages.
The workflow .github/workflows/publish-npm.yml uses permissions: id-token: write, Node 24, and upgrades the global npm to ≥ 11.5.1 before npm publish. Do not set NODE_AUTH_TOKEN on publish steps — the CLI uses OIDC automatically.
Optional: after OIDC works, under package Publishing access, you can restrict token-based publishing per npm’s “maximum security” guidance.
If Trusted publishing is not configured yet, add an NPM_TOKEN secret (classic type Automation) and temporarily restore NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} on each npm publish step. A classic Publish token triggers EOTP in CI because Actions cannot supply an OTP.
Official images on Docker Hub are threevl/hpo (Python) and threevl/hpo-node (Node).
In GitHub: add repository secrets DOCKERHUB_USERNAME and DOCKERHUB_TOKEN (a Docker Hub access token, not your account password). The token’s user must be allowed to push to the threevl organization’s hpo and hpo-node repositories.
Then run Publish Docker Hub images from the Actions tab (optional tag, default latest), or push a git tag matching v* (the tag name is used as the image tag, e.g. v2026-05-12).
# Python
docker build -t hpo-api:python -f Dockerfile .
# Node.js
docker build -t hpo-api:node -f Dockerfile.node .Mount your own .obo or .owl file into /data/ inside the container:
# Python — reads OBO first, falls back to OWL
docker run -p 8000:8000 \
-v /path/to/hp.obo:/data/hp.obo:ro \
-v /path/to/hp.owl:/data/hp.owl:ro \
hpo-api:python
# Node.js — reads OBO
docker run -p 8000:8000 \
-v /path/to/hp.obo:/data/hp.obo:ro \
hpo-api:node| Variable | Default | Description |
|---|---|---|
HPO_OBO_PATH |
/data/hp.obo |
Path to the OBO ontology file inside the container |
HPO_OWL_PATH |
/data/hp.owl |
Path to the OWL ontology file (Python only, used as fallback) |
PORT |
8000 |
Listening port (Node.js only; Python uses uvicorn's --port) |
# Log in (use a personal access token with write:packages scope)
echo "$GITHUB_TOKEN" | docker login ghcr.io -u YOUR_USERNAME --password-stdin
# Tag and push — Python
docker build -t ghcr.io/YOUR_ORG/hpo-api:python -f Dockerfile .
docker push ghcr.io/YOUR_ORG/hpo-api:python
# Tag and push — Node.js
docker build -t ghcr.io/YOUR_ORG/hpo-api:node -f Dockerfile.node .
docker push ghcr.io/YOUR_ORG/hpo-api:nodePublished by CI as threevl/hpo and threevl/hpo-node (see Publishing Docker Hub images above). Manual push with the same names:
docker login
# Python
docker build -t threevl/hpo:latest -f Dockerfile .
docker push threevl/hpo:latest
# Node.js
docker build -t threevl/hpo-node:latest -f Dockerfile.node .
docker push threevl/hpo-node:latestReplace threevl with your Docker Hub username or organization if you use a different namespace.
Tag images with the HPO release version for reproducibility:
HPO_VERSION=2026-02-16
# GitHub Container Registry
docker build -t ghcr.io/YOUR_ORG/hpo-api:python-${HPO_VERSION} -f Dockerfile .
docker build -t ghcr.io/YOUR_ORG/hpo-api:node-${HPO_VERSION} -f Dockerfile.node .
docker push ghcr.io/YOUR_ORG/hpo-api:python-${HPO_VERSION}
docker push ghcr.io/YOUR_ORG/hpo-api:node-${HPO_VERSION}
# Docker Hub (same names as CI)
docker build -t threevl/hpo:${HPO_VERSION} -f Dockerfile .
docker build -t threevl/hpo-node:${HPO_VERSION} -f Dockerfile.node .
docker push threevl/hpo:${HPO_VERSION}
docker push threevl/hpo-node:${HPO_VERSION}# Get ontology metadata
curl http://localhost:8000/api/ontology
# Look up a term by HPO ID
curl http://localhost:8000/api/terms/HP:0001250
# Search for terms
curl "http://localhost:8000/api/search?q=seizure&limit=5"
# Get ancestors up to 3 levels
curl "http://localhost:8000/api/terms/HP:0001250/parents?distance=3"