@synanetics/fhirstore-integrations
v0.1.0
Published
> TODO: description
Downloads
102
Maintainers
Keywords
Readme
@synanetics/fhirstore-integrations
Moleculer service and functionality to add gateways and integrations to a FHIR Store.
Quick Start
npm i @synanetics/fhirstore-integrations
Purpose
Please see FHIR Store Gateways and Integrations for full details.
A FHIR Store gateway acts as an ingress to a FHIR Store (such as HTTP). A FHIR Store integration is registered against a gateway and can interact with a FHIR Store via the Moleculer transporter to apply some additional functionality to the FHIR Store without altering the FHIR Store codebase.
Usage
The package includes services and functions to be used both in the gateway and the integration itself.
The gateway may be constructed as follows e.g. with moleculer and moleculer-web
import {
httpRequestContextFromRequest,
getIntegration,
} from '@synanetics/fhirstore-integrations';
// construct a moleculer-web api gateway, we will listen to GET and POST HTTP requests on PORT 8080
// all requests for GET/POST /fhir/r4 will be passed to the request handler.
// based on what integrations have been registered with the integration service the gateway will detemine
// which Moleculer Service and Action that should be called to handle the request
const gatewayService: ServiceSchema = {
name: 'gateway',
mixins: [ApiGateway],
settings: {
port: 8080,
routes: [{
path: '/',
aliases: {
'POST /fhir/r4/(.*)': requestHandler,
'GET /fhir/r4/(.*)': requestHandler,
},
}],
},
};
const requestHandler = async (req: IncomingRequest, res: ServerResponse) => {
try {
const baseUrl = req.url?.replace(req.$params[0], '');
// as we are handling HTTP requests, we will use httpRequestContextFromRequest
// to extract the request context. The response has the structure
// {
// type: 'http' // indicate the request context is from an HTTP request
// route: string // the route the HTTP request came from
// method: string // the HTTP method of the request
// headers: Record<string, string> // the HTTP request headers
// body?: any // the body of the request, if any
// query?: URLSearchParams // the query parameters, if any
// gatewayName: string // the name of the gateway the request was receieved from
// }
//
// the gatewayName parameter allows for multiple gateways to service a FHIR Store
// and have different integrations attached to them
const requestContext = await httpRequestContextFromRequest(
req,
{
gatewayName: 'gateway',
baseUrl,
},
);
// getIntegration will query the integration service for the
// known integrations attached to this gateway
// where the requestContext is an HTTP context it will be matched
// on the HTTP route and HTTP method of the request only at present
// the result of the function call is the matched target for the request
// e.g. for the HTTP request context
// {
// target: {
// serviceName: string // the name of the Moleculer service to call
// actionName: string // the Moleculer service action to call
// gatewayName: string // the name of the gateway
// pattern: { // the http request pattern that matched the request
// type: 'http',
// route: string // the http route that was matched to the request
// method: string // the http method that was matched to the request
// }
// }
// }
const { target } = await getIntegration(
requestContext,
req.$ctx,
);
// call the service and action of the integration that matches the request
const result = await req.$ctx.call(`${target.serviceName}.${target.actionName}`, { requestContext });
// return the result
res.setHeader('content-type', 'application/json').writeHead(200).end(JSON.stringify(result));
} catch (err) {
const error = err as unknown as Error;
req.$ctx.broker.logger.error('error', error);
res.setHeader('content-type', 'application/json').writeHead(500).end(JSON.stringify({ error: error.message }));
}
};
A FHIR Integration may be constructed similar to the below
import {
registerIntegrations,
integrationService,
} from '@synanetics/fhirstore-integrations';
// the integration service(s) are Moleculer services to perform some
// additional processing for the FHIR Store
// the key part of an Integreation is that it will register the requests
// that a gateway should match on to be forwarded to the Integration
// this is done with the registerIntegrations method
const integrationOne: ServiceSchema = {
name: 'integrationOne',
// as we are registering integrations on start
// here we need to wait for the integrationService
// before starting, so add it as a dependency
dependencies: [integrationService.name],
// the actions of the Integration can be anything, and
// perform any action they need to. As they will be attached
// to the same transporter as the FHIR Store they may call
// FHIR Store Service and Actions directly
actions: {
extract: {
async handler() {
return {
service: 'integrationOne',
};
},
},
},
async started(this: Service) {
// registerIntegrations is called to register
// an Integration with a Gateway so the Gateway can
// match and forward requests to it.
// the function takes two parameters
//
// integration: string - the name of the integration that is making the registration
// multiple calls to registerIntegrations with the same integration will be assumed to
// be updates, and any existing registrations with the same key will be overwritten
//
// registrations: Array<{
// serviceName: string // when matched this is the service that will be called with the request
// actionName: string // when matched this the action on the service that will be called
// gatewayName: string // the name of the gateway the request must originate from to match the request
// pattern: { // the request pattern that must be matched
// type: 'http' // the request must be a HTTP request to match the integration
// method: string // the HTTP method that must be matched
// route: string // the HTTP route that must be matched - uses path-to-regexp to match routes
// }
//}>
await registerIntegrations(
'integrationone',
[
{
serviceName: 'integrationOne',
actionName: 'extract',
gatewayName: 'gateway',
pattern: {
type: 'http',
method: 'POST',
route: '/Questionnaire/:id/$extract',
},
},
],
this.broker,
);
},
};
Putting all this together in Moleculer
import { integrationService } from '@synanetics/fhirstore-integrations';
import { ServiceBroker } from 'moleculer';
// make the ServiceBroker
const broker = new ServiceBroker({ logger: false });
// add the gateway
broker.createService(gatewayService);
// add the integration service
broker.createService(integrationService);
// add the integration
broker.createService(integrationOne);
// start the broker
await broker.start();