A Node.js wrapper for the HL7 FHIR Validator CLI that provides a simple, promise-based API for validating FHIR resources.
- Maintainers: Grahame Grieve (looking for volunteers)
- Issues / Discussion: https://github.com/FHIR/fhir-validator-wrapper/issues / https://chat.fhir.org/#narrow/channel/179169-javascript
- License: Apache 2.0
- Contribution Policy: See Contributing.
- Security Information: To report a security issue, please use the GitHub Security Advisory "Report a Vulnerability" tab.
There are many ways to contribute:
- Submit bugs and help us verify fixes as they are checked in.
- Review the source code changes.
- Engage with users and developers on the dotnet stream on FHIR Zulip
- Contribute features or bug fixes via PRs:
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests
- Submit a pull request
This library manages the lifecycle of the FHIR Validator Java service and provides a clean Node.js interface for validation operations. It handles automatic downloading of the validator JAR, process management, HTTP communication, and provides typed validation options.
- Automatic JAR Management: Automatically downloads and updates the FHIR Validator CLI JAR from GitHub releases
- Version Tracking: Tracks installed version and checks for updates
- Resource Validation: Validate FHIR resources in JSON or XML format
- Profile Validation: Validate against specific FHIR profiles
- Implementation Guide Support: Load IGs at startup or runtime
- Terminology Testing: Run terminology server tests with
runTxTest
- Node.js 12.0.0 or higher
- Java 8 or higher
- Internet connection (for automatic JAR download, or manually download from GitHub releases)
npm install fhir-validator-wrapperconst FhirValidator = require('fhir-validator-wrapper');
async function validateResource() {
// The JAR will be automatically downloaded if not present
const validator = new FhirValidator('./validator_cli.jar');
try {
// Start the validator service (auto-downloads JAR if needed)
await validator.start({
version: '5.0.0',
txServer: 'http://tx.fhir.org/r5',
txLog: './txlog.txt',
igs: ['hl7.fhir.us.core#6.0.0']
});
// Validate a resource
const patient = {
resourceType: 'Patient',
id: 'example',
active: true,
name: [{ family: 'Doe', given: ['John'] }]
};
const result = await validator.validate(patient);
console.log('Validation result:', result);
} finally {
await validator.stop();
}
}Creates a new FHIR validator instance.
validatorJarPath(string): Path to the FHIR validator CLI JAR file (will be downloaded here if not present)logger(Object, optional): Winston logger instance for custom logging
Checks for and downloads/updates the validator JAR as needed. This is called automatically by start() when autoDownload is enabled.
Parameters:
options(Object, optional): Configuration objectforce(boolean): Force download even if current version is up to date (default: false)skipUpdateCheck(boolean): Skip checking for updates if JAR exists (default: false)
Returns: Promise<{version: string, updated: boolean, downloaded: boolean}>
Example:
const validator = new FhirValidator('./validator_cli.jar');
// Check for updates and download if needed
const result = await validator.ensureValidator();
console.log(`Version: ${result.version}`);
console.log(`Downloaded: ${result.downloaded}`);
console.log(`Updated: ${result.updated}`);
// Force re-download
await validator.ensureValidator({ force: true });
// Skip update check (use existing JAR)
await validator.ensureValidator({ skipUpdateCheck: true });Fetches the latest release information from GitHub.
Returns: Promise<{version: string, downloadUrl: string, publishedAt: string}>
Example:
const latest = await validator.getLatestRelease();
console.log(`Latest version: ${latest.version}`);
console.log(`Published: ${latest.publishedAt}`);Gets the currently installed validator version.
Returns: string|null - The installed version or null if not installed
Starts the FHIR validator service with the specified configuration.
Parameters:
config(Object): Configuration objectversion(string): FHIR version (e.g., "5.0.0", "4.0.1")txServer(string): Terminology server URL (e.g., "http://tx.fhir.org/r5")txLog(string): Path to transaction log fileigs(string[], optional): Array of implementation guide packagesport(number, optional): Port to run the service on (default: 8080)timeout(number, optional): Startup timeout in milliseconds (default: 30000)autoDownload(boolean, optional): Automatically download/update validator JAR (default: true)skipUpdateCheck(boolean, optional): Skip checking for updates if JAR exists (default: false)ssrfProtection(boolean, optional): SSRF protection in the validator (default: true). See SSRF Protection belowextraArgs(string[], optional): Additional raw command line arguments appended to the validator invocation
Returns: Promise<void>
Example:
await validator.start({
version: '5.0.0',
txServer: 'http://tx.fhir.org/r5',
txLog: './txlog.txt',
igs: [
'hl7.fhir.us.core#6.0.0',
'hl7.fhir.uv.sdc#3.0.0'
],
port: 8080,
autoDownload: true, // Download JAR if missing (default)
skipUpdateCheck: true // Don't check for updates every time
});From version 6.10.0 the FHIR validator enables Server-Side Request Forgery protection by default. With
protection on, the validator refuses http:// URLs, and refuses to connect to non-public addresses -
localhost, 127.0.0.1, and private network ranges. This protects against content under an attacker's
control directing the validator at internal network resources.
That default breaks any setup where the validator is deliberately pointed at a terminology server on the local machine, which is typical of test harnesses:
await validator.start({
version: '4.0',
txServer: 'http://localhost:9095/r5', // blocked when SSRF protection is on
txLog: './txlog.txt',
port: 9096,
ssrfProtection: false
});Setting ssrfProtection: false passes -ssrf-protection-enabled false to the validator, which turns off
both the https requirement and the non-public address check, globally, for that validator process. This
also applies to servers the validator is asked to reach later - for example the server parameter of
runTxTest().
Only do this where no untrusted party can influence any of the content being processed, or where the validator runs somewhere internal network access poses no risk. The wrapper logs a warning whenever protection is disabled.
When ssrfProtection is not set, the wrapper passes no option at all, leaving the validator to use its own
default and any ssrfProtectionEnabled setting in fhir-settings.json.
extraArgs appends raw arguments to the validator command line, for options this wrapper does not model:
await validator.start({
version: '5.0.0',
txServer: 'https://tx.fhir.org/r5',
txLog: './txlog.txt',
extraArgs: ['-fhir-settings', '/path/to/fhir-settings.json']
});Arguments are passed through unquoted via spawn(), so no shell escaping is needed or applied.
Validates a FHIR resource against the loaded implementation guides and profiles.
Parameters:
resource(string|Buffer|Object): The resource to validate- String: JSON or XML resource
- Buffer: Raw bytes of resource
- Object: JavaScript object representing the resource
options(Object, optional): Validation optionsprofiles(string[]): Profiles to validate againstresourceIdRule(string): Resource ID rule ("OPTIONAL", "REQUIRED", "PROHIBITED")anyExtensionsAllowed(boolean): Whether any extensions are allowed (default: true)bpWarnings(string): Best practice warning leveldisplayOption(string): Display option for validation
Returns: Promise<Object> - OperationOutcome as JavaScript object
Examples:
// Basic validation
const result = await validator.validate(patientResource);
// Validation with options
const result = await validator.validate(patientResource, {
profiles: ['http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient'],
resourceIdRule: 'REQUIRED',
bpWarnings: 'Warning'
});Validates a FHIR resource from raw bytes.
Parameters:
resourceBytes(Buffer): The resource as bytesformat(string, optional): The format ("json" or "xml", default: "json")options(Object, optional): Same asvalidate()method
Returns: Promise<Object> - OperationOutcome as JavaScript object
Validates a FHIR resource object.
Parameters:
resourceObject(Object): The resource as a JavaScript objectoptions(Object, optional): Same asvalidate()method
Returns: Promise<Object> - OperationOutcome as JavaScript object
Loads an additional implementation guide at runtime.
Parameters:
packageId(string): The package ID (e.g., "hl7.fhir.us.core")version(string): The version (e.g., "6.0.0")
Returns: Promise<Object> - OperationOutcome as JavaScript object
Example:
await validator.loadIG('hl7.fhir.uv.ips', '1.1.0');Runs a terminology server test against a specified server.
Parameters:
params(Object): Test parametersserver(string): The address of the terminology server to testsuiteName(string): The suite name that contains the test to runtestName(string): The test name to runversion(string): What FHIR version to use for the testexternalFile(string, optional): Name of messages filemodes(string, optional): Comma delimited string of modes
Returns: Promise<{result: boolean, message?: string}>
Example:
// Run a terminology server test
const result = await validator.runTxTest({
server: 'http://tx-dev.fhir.org',
suiteName: 'metadata',
testName: 'metadata',
version: '5.0'
});
if (result.result) {
console.log('Test passed!');
} else {
console.log('Test failed:', result.message);
}
// With optional parameters
const result = await validator.runTxTest({
server: 'http://tx-dev.fhir.org',
suiteName: 'expand',
testName: 'expand-test-1',
version: '5.0',
modes: 'lenient,tx-resource-cache'
});Stops the validator service and cleans up resources.
Returns: Promise<void>
Checks if the validator service is currently running.
Returns: boolean
Performs a health check on the running service.
Returns: Promise<void>
The library automatically manages the FHIR Validator CLI JAR file:
When you call start(), the library will:
- Check if the JAR file exists at the specified path
- If missing, download the latest version from GitHub releases
- Track the version in a
.versionfile alongside the JAR
Version information is stored in {jarPath}.version:
{
"version": "6.3.4",
"downloadUrl": "https://github.com/hapifhir/org.hl7.fhir.core/releases/...",
"downloadedAt": "2024-01-15T10:30:00.000Z"
}// Always check for updates (default)
await validator.start({
version: '5.0.0',
txServer: 'http://tx.fhir.org/r5',
txLog: './txlog.txt'
});
// Skip update check for faster startup
await validator.start({
version: '5.0.0',
txServer: 'http://tx.fhir.org/r5',
txLog: './txlog.txt',
skipUpdateCheck: true
});
// Disable auto-download entirely (JAR must exist)
await validator.start({
version: '5.0.0',
txServer: 'http://tx.fhir.org/r5',
txLog: './txlog.txt',
autoDownload: false
});const validator = new FhirValidator('./validator_cli.jar');
// Check what's available vs installed
const latest = await validator.getLatestRelease();
const installed = validator.getInstalledVersion();
console.log(`Latest: ${latest.version}`);
console.log(`Installed: ${installed || 'not installed'}`);
// Download/update without starting service
const result = await validator.ensureValidator();
// Force re-download
await validator.ensureValidator({ force: true });# Use the example script to just download/update the JAR
node example.js --download-onlyImplementation guides can be loaded in two ways:
- At startup (recommended for known dependencies):
await validator.start({
version: '5.0.0',
txServer: 'http://tx.fhir.org/r5',
txLog: './txlog.txt',
igs: [
'hl7.fhir.us.core#6.0.0',
'hl7.fhir.uv.sdc#3.0.0'
]
});- At runtime (for dynamic loading):
await validator.loadIG('hl7.fhir.uv.ips', '1.1.0');For IG package format documentation, see: Using the FHIR Validator - Loading Implementation Guides
The library throws descriptive errors for various failure scenarios:
try {
await validator.validate(invalidResource);
} catch (error) {
if (error.message.includes('Validation failed')) {
// Handle validation errors
console.log('Resource is invalid:', error.message);
} else if (error.message.includes('not ready')) {
// Handle service not ready
console.log('Service not started:', error.message);
} else {
// Handle other errors
console.log('Unexpected error:', error.message);
}
}- Resource Management: Always call
stop()when done to clean up the Java process:
try {
await validator.start(config);
// ... validation operations
} finally {
await validator.stop();
}- Process Termination Handling: Handle graceful shutdown:
process.on('SIGINT', async () => {
await validator.stop();
process.exit(0);
});- Reuse Validator Instance: Start the validator once and reuse for multiple validations:
const validator = new FhirValidator('./validator_cli.jar');
await validator.start(config);
// Validate multiple resources
const result1 = await validator.validate(resource1);
const result2 = await validator.validate(resource2);
const result3 = await validator.validate(resource3);
await validator.stop();- Timeout Configuration: Set appropriate timeouts for startup in production:
await validator.start({
// ... other config
timeout: 120000 // 2 minutes for production environments
});- Skip Update Checks in CI/CD: For faster builds, skip update checks:
await validator.start({
// ... other config
skipUpdateCheck: true
});The library includes comprehensive tests. You can run them in different ways depending on your setup:
npm run test:unitGITHUB_API_TESTS=1 npm testDOWNLOAD_TESTS=1 npm test# With auto-download
INTEGRATION_TESTS=1 npm test
# With specific JAR path
FHIR_VALIDATOR_JAR_PATH=./your-validator.jar INTEGRATION_TESTS=1 npm test# Quick manual test (auto-downloads JAR if needed)
INTEGRATION_TESTS=1 npm run test:manual- Java not found: Ensure Java is installed and available in PATH
- JAR download fails: Check internet connection and GitHub accessibility
- Port conflicts: Change the port if 8080 is already in use
- Memory issues: Add JVM options by modifying the spawn command if needed
- Network timeouts: Increase timeout values for slow networks
- GitHub rate limits: Use
skipUpdateCheck: trueto avoid repeated API calls
The library logs validator stdout/stderr for debugging. You can provide a Winston logger for custom logging:
const winston = require('winston');
const logger = winston.createLogger({
level: 'debug',
transports: [new winston.transports.Console()]
});
const validator = new FhirValidator('./validator_cli.jar', logger);
// Or set later:
validator.setLogger(logger);For issues with this wrapper, please file a GitHub issue. For FHIR validator issues, see the official FHIR validator documentation.
Releases are cut from main. The bump, the commit and the tag all come from a single npm version
command, so the steps are shorter than they look.
Do not edit version in package.json by hand. npm version derives the new version from whatever
is in package.json already, so a hand-edited version makes it overshoot — leave package.json at the
last released version and let step 2 move it.
-
Land everything, including the changelog. Add the entry for the version about to be released to CHANGELOG.md, and commit it with the rest of the release.
npm versionrefuses to run against a dirty working tree. -
Bump, commit and tag — one command, pick the one that matches the change:
npm version minor # new features, e.g. 1.2.2 -> 1.3.0 npm version patch # fixes only, e.g. 1.2.2 -> 1.2.3This is the step that does the work. It rewrites
versionin package.json, makes a commit whose message is the bare version number (1.3.0), and creates the matching git tag (v1.3.0). It prints the new version when it's done. -
Push the commit and the tag —
git pushalone does not push tags:git push && git push --tags -
Publish to npm:
npm whoami # if this errors, run `npm login` first - it is not needed every release npm publishnpm versiondoes not publish. Skip this and the tag exists but nothing reaches npm. Confirm with:npm view fhir-validator-wrapper version