Security News
Opengrep Emerges as Open Source Alternative Amid Semgrep Licensing Controversy
Opengrep forks Semgrep to preserve open source SAST in response to controversial licensing changes.
@icanbwell/fhirproof
Advanced tools
FHIRProof is an ODM for mongo that provides object document mapper to FHIR with all R4 resources and validation by default. It's meant to be heavily extensinble. It provides you the following mongo specific operations:
It also exposes the collection on the constructor and object, so you can do anything via mongo you would normally do.
From your terminal, run
npm install --save @icanbwell/fhirproof
Pass in your connection string and any options you would normally pass to Mongodb.
const { mongoconnect } = require('@icanbwell/fhirproof');
await mongoconnect(
('mongodb://localhost/my_database',
{
useNewUrlParser: true,
useUnifiedTopology: true,
})
);
You will want to inherit from the BaseFHIRModel
for all FHIR Resources. Below is an example of creating an Organization resource. This format is required. You can do more, but this is the minimum.
// organization.model.js
const { BaseFHIRModel, CONSTANTS } = require('@icanbwell/fhirproof');
const resourceName = 'Organization';
const collectionName = CONSTANTS.COLLECTION.ORGANIZATION;
class Organization extends BaseFHIRModel {
constructor(resource, _id) {
super(collectionName, collectionName);
this.resource = new this.Resource(resource);
this._id = _id;
}
}
Organization.resourceName = resourceName;
Organization.collectionName = collectionName;
module.exports = Organization;
And using it in a router.
//organization.route.js
const express = require('express');
const Organization = require('./organization.model');
const router = express.Router();
router
.get('/Organization/:id', async (req, res, next) => {
const organization = await Organization.initialize().findById(req.param.id);
res.status(200).json(organization.toJSON());
})
.get('/Organization/:id/history', async (req, res, next) => {
const organizationHistory = await Organization.initialize().history(
req.param.id
);
res.status(200).json(organizationHistory);
})
.post('/Organization', async (req, res, next) => {
try {
const organization = await Organization.initialize().create(req.body);
//try catch logic to handle 400s if desired. Create validates by default
res.status(201).json(organization.toJSON());
} catch (error) {
if (error.statusCode && error.operationOutcome) {
// we handle status 400, 404 as extended error objects
return res.status(error.statusCode).json(error.operationOutcome);
}
next(error);
}
});
module.exports = router;
Of course, everyone needs to add more capabilities to support their use case. Below are two examples. The first is adding a completely new method. The second is adding more functionality ontop of an existing method.
// organization.model.js from above
const { BaseFHIRModel, CONSTANTS } = require('@icanbwell/fhirproof');
const resourceName = 'Organization';
const collectionName = CONSTANTS.COLLECTION.ORGANIZATION;
class Organization extends BaseFHIRModel {
constructor(resource, _id) {
super(collectionName, collectionName);
this.resource = new this.Resource(resource);
this._id = _id;
}
//new search method
static async search(queries, options) {
// NOTICE: you have access to mongodb collection if you find yourself wanting to get closer to the metal
// console.log(this.collection)
// console.log(this.historyCollection)
// console.log(this.db)
// console.log(this.validator)
const cursor = await this.collection.find(queries, options);
return cursor.toArray();
}
//new instance method
addExtension({ valueUrl, value }) {
this.resource.extension.push({ valueUrl: value });
return this.save();
}
//override create method to make sure organization.name is present
static async create(payload) {
if (!payload.name) {
throw new Error('organization.name is required');
}
return await super.create(payload);
}
}
Organization.resourceName = resourceName;
Organization.collectionName = collectionName;
We try to handle errors as http status code errors. When certain situations arise, we return these errors extending from the base Error object. See ./src/http-errors
for more information.
For bad request (status code 400), we have the BadRequestError with statusCode
and operationOutcome
property. For not found, (status code 404), we have the NotFoundError with statusCode
and operationOutcome
.
# load configuration files to connect to mongo
node -r dotenv/config
> const { mongoconnect, BaseFHIRModel } = require('@icanbwell/fhirproof')
undefined
> mongoconnect('mongodb://localhost:27017')
Promise { <pending> }
> const Organization = require('./example/organization/organization.model')
undefined
> Organization
[class Organization extends BaseFHIRModel] {
resourceName: 'Organization',
collectionName: 'Organization'
}
Organization.initialize().create({resourceType: 'Organizatin', name: "This is a test"}).then(data => org = data).catch(err => error = err)
Promise { <pending> }
> org
Uncaught ReferenceError: org is not defined
> error
BadRequestError: Invalid resourceType 'Organizatin'
at /Users/nathanhall/bwell/marketplace-vendor/marketplace-vendor-onboarding/src/server/fhir/base-fhir.model.js:54:23
at new Promise (<anonymous>)
at Function.create (/Users/nathanhall/bwell/marketplace-vendor/marketplace-vendor-onboarding/src/server/fhir/base-fhir.model.js:51:12)
at REPL48:1:27
at Script.runInThisContext (vm.js:133:18)
at REPLServer.defaultEval (repl.js:484:29)
at bound (domain.js:413:15)
at REPLServer.runBound [as eval] (domain.js:424:12)
at REPLServer.onLine (repl.js:817:10)
at REPLServer.emit (events.js:327:22) {
operationOutcome: { resourceType: 'OperationOutcome', issue: [ [Object] ] },
statusCode: 400
}
> Organization.initialize().create({resourceType: 'Organization', name: "This is a test"}).then(data => org = data).catch(err => error = err)
Promise { <pending> }
org.toJSON()
{
resourceType: 'Organization',
id: '606c91947c00311223dfb08b',
meta: { versionId: '1', lastUpdated: '2021-04-06T16:51:32+00:00' },
name: 'This is a test'
}
> org.resource.name
'This is a test'
> org.resource.name = 'Update me'
'Update me'
> org.save()
Promise { <pending> }
> org.name
undefined
> org.resource.name
'Update me'
> org._id
606c91947c00311223dfb08b
> org.history().then(data => history = data)
Promise { <pending> }
> history
[
{
_id: 606c91947c00311223dfb08c,
resourceType: 'Organization',
meta: { versionId: '1', lastUpdated: '2021-04-06T16:51:32+00:00' },
name: 'This is a test',
id: 606c91947c00311223dfb08b
},
{
_id: 606c91fe7c00311223dfb08d,
resourceType: 'Organization',
id: 606c91947c00311223dfb08b,
meta: { versionId: '2', lastUpdated: '2021-04-06T16:53:18+00:00' },
name: 'Update me'
}
]
You'll probably want to add indexes to things you search for frequently. We tried to make it as easy as possible. Using mongoconnect
, when successful, fires an event call 'mongoconnect'. Listening to this event with fhirProofEvent
you can use our class method to create indexes, or operate on the collection directly.
//organization.model.js
const { fhirProofEvent } = require('@icanbwell/fhirproof');
// ...Other lines from above
fhirProofEvent.on('mongoconnect', async () => {
try {
await Organization.initialize().createIndex(
{ name: 1 },
{
unique: true,
sparse: true,
}
);
// collection directly
// await Organization.collection.createIndex(...args)
} catch (error) {
console.error(`[OrganizationModel] - Failed to create index ${error}`);
throw error;
}
});
The primary purpose of FHIRProof is to build a great data model for FHIR, mongo, and nodejs... making it faster and easier to use. To do this, we have FHIRProof on github for everyone to make it better.
Shout out to @asymmetrik/node-fhir-server-core and @asymmetrik/fhir-json-schema-validator for building the pieces to put this together.
Because I couldn't find all the generated mongoose schemas in FHIR, I like JSON Schemas, and using ObjectRef
is tricky with how FHIR does relations.
FHIRProof is MIT licensed.
FAQs
FHIR Mongo object document mapper with built in validation
We found that @icanbwell/fhirproof demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Opengrep forks Semgrep to preserve open source SAST in response to controversial licensing changes.
Security News
Critics call the Node.js EOL CVE a misuse of the system, sparking debate over CVE standards and the growing noise in vulnerability databases.
Security News
cURL and Go security teams are publicly rejecting CVSS as flawed for assessing vulnerabilities and are calling for more accurate, context-aware approaches.