Node.js / TypeScript SDK v1.0 Released
Official Node.js and TypeScript SDK for OMOPHub - typed client with discriminated { data, error } returns, 37 methods across 8 resources, full FHIR Resolver support from day one.
The Node.js / TypeScript SDK is out, joining Python and R on the official client list. Same 37 methods, same 8 resources, full FHIR Resolver coverage from day one - but with a TypeScript-first design and a few Node-idiomatic touches you don't get in the other SDKs.
Installation
npm install @omophub/omophub-node
Requires Node 22 or 24. The package ships as ESM with TypeScript declarations.
Quick Start
import { OMOPHub } from '@omophub/omophub-node';
// Uses OMOPHUB_API_KEY env var by default
const client = new OMOPHub();
// Search across vocabularies
const { data: results } = await client.search.basic('type 2 diabetes', {
vocabularyIds: ['SNOMED'],
standardConcept: 'S',
});
// Get a concept by ID
const { data: concept } = await client.concepts.get(201826);
console.log(concept?.concept_name); // "Type 2 diabetes mellitus"
// Map between vocabularies
const { data: mappings } = await client.mappings.get(201826, {
targetVocabulary: 'ICD10CM',
});
Errors as Values, Not Exceptions
The SDK's distinguishing design choice: every network call returns a discriminated Response<T> = { data, error, meta, headers }. Errors don't throw - they land in error as a typed object, and TypeScript narrows data to non-null in the success branch:
const { data, error } = await client.concepts.get(999999999);
if (error) {
if (error.name === 'not_found') {
console.log('Concept does not exist');
} else if (error.name === 'rate_limit_exceeded') {
console.log(`Retry after ${error.retryAfter}s`);
}
return;
}
// TypeScript knows `data` is non-null here
console.log(data.concept_name);
16 stable error codes (not_found, restricted_api_key, rate_limit_exceeded, validation_error, timeout_error, connection_error, ...) - switch on error.name for programmatic handling. The thrown-exception world of Python try/except is gone; TypeScript's exhaustiveness checking gates every branch instead.
Built-In Retries & Backoff
Production-ready retry behaviour out of the box - no extra config needed:
- Retries on
429,502,503,504, and transient network errors. - Full-jitter exponential backoff:
500ms → 1s → 2s → 4s → 8s, capped. Retry-Afterhonoured up to 60 seconds (parsed strictly per RFC 9110).- POST/PATCH retried only when
Idempotency-Keyis set, except for429- pre-processing rejections are retry-safe regardless of method.
const client = new OMOPHub({
maxRetries: 5, // default: 3
timeoutMs: 60_000, // default: 30s per call
});
Async Iterators for Pagination
for await walks pages without manual bookkeeping:
// Stream concepts page by page
for await (const concept of client.search.basicIter('insulin', { pageSize: 100 })) {
console.log(concept.concept_name);
}
// Or collect everything with a page cap
const { data, pagesFetched } = await client.search.basicAll('insulin', {
pageSize: 100,
maxPages: 5,
});
The iterator throws OMOPHubIteratorError mid-stream on a failed page - you choose whether to retry the page or bail. basicAll collects partial results plus the error list so you can recover from a transient failure without re-walking the whole corpus.
FHIR Resolver Included
The FHIR Resolver is in v1.0.0 from day one - including the v1.8.0 FHIR-to-OMOP IG features (Value-as-Concept, user-selected priority, on_unmapped sentinel):
const { data } = await client.fhir.resolve({
system: 'http://snomed.info/sct',
code: '44054006',
resourceType: 'Condition',
});
console.log(data?.resolution.standard_concept.concept_name);
console.log(data?.resolution.target_table); // "condition_occurrence"
// Accepts either flat form (above) or nested coding form:
await client.fhir.resolve({
coding: { system: 'http://snomed.info/sct', code: '44054006' },
resourceType: 'Condition',
});
resolveBatch (1-100 codings) and resolveCodeableConcept (1-20 codings with OHDSI vocabulary preference) round out the FHIR surface.
Vocabulary Versioning
Pin to a specific vocabulary release for reproducible research:
const client = new OMOPHub({ vocabVersion: '2025.2' });
// Or pin per call:
const { data } = await client.concepts.get(201826, { vocabRelease: '2025.2' });
Full Surface Parity
37 methods across 8 resources, matching Python:
client.concepts - get, getByCode, batch, suggest, related, relationships, recommended
client.search - basic, basicIter, basicAll, advanced, autocomplete, semantic,
semanticIter, semanticAll, bulkBasic, bulkSemantic, similar
client.vocabularies - list, get, stats, domainStats, domains, conceptClasses, concepts
client.domains - list, concepts
client.hierarchy - get, ancestors, descendants
client.relationships - get, types
client.mappings - get, map
client.fhir - resolve, resolveBatch, resolveCodeableConcept
Plus standalone helpers: omophubFhirUrl(version) for the FHIR Terminology Service base URL, and getApiKey() / setApiKey() / hasApiKey() for env-backed configuration.
Resources
Issues and contributions welcome on GitHub.