Integration Tutorial
If you're building a Node.JS application like Next.JS or a GraphQL Server and need to integrate directly with HL7 interfaces, we've included messengers to make creating and sending HL7 simple once and for all.
Setup
To integrate Gofer Engine into an existing application, it seems silly to say, but you will need an existing application. Perhaps you are researching before building a new application or extending functionality of an existing one. Either way, this short tutorial purposely doesn't cover the setup of an existing project. But it does require that the project is a Node.JS environment, and you have access to the npm repository.
Another point to clarify is that integrating messenger services into your application should be implemented on the server side.
For example, in your Next.JS application, the messenger should be implemented in an API Page or Server Side function and not in a front-end page directly.
Install
Within your project, run:
npm i @gofer-engine/engine @gofer-engine/hl7 @gofer-engine/tools
Alternatively, use your package manager of choice and follow its syntax for adding new packages from the npm repository.
You may need additional/fewer packages from @gofer-engine/
depending on what
functionality you need.
Create a Messenger
The gofer
class exposes a method called messenger
that accepts a
MessengerRoute
function. This method returns a tuple that contains the
messenger callback and an id to identify the messenger class to observe events
or control later in your application.
The MessengerRoute
function follows the below simplified type. Also including the simplified ORoute
interface for quick reference.
type MessengerRoute = (R: ORoute) => ORoute;
export interface ORoute extends OBase<ORoute> {
name: (name: string) => ORoute;
id: (id: string | number) => ORoute;
filter: (f: FilterFunc) => ORoute;
transform: (t: TransformFunc) => ORoute;
store: (s: StoreConfig) => ORoute;
setVar: (scope: varTypes, varName: string, varValue: MsgVar) => ORoute;
getVar: (scope: varTypes, varName: string, getVal: WithVarDo) => ORoute;
setMsgVar: (varName: string, varValue: MsgVar) => ORoute;
getMsgVar: (varName: string, getVal: WithVarDo) => ORoute;
setChannelVar: (varName: string, varValue: MsgVar) => ORoute;
getChannelVar: (varName: string, getVal: WithVarDo) => ORoute;
setGlobalVar: (varName: string, varValue: MsgVar) => ORoute;
getGlobalVar: (varName: string, getVal: WithVarDo) => ORoute;
setRouteVar: (varName: string, varValue: MsgVar) => ORoute;
getRouteVar: (varName: string, getVal: WithVarDo) => ORoute;
send(method: 'tcp', host: FunctProp<string>, port: FunctProp<number>): ORoute;
send(method: 'http', options: IHTTPConfig<true>): ORoute;
send(method: 'https', options: IHTTPSConfig<true>): ORoute;
}
For this tutorial, we will just use a single flow in the route to send to a tcp
HL7 listener. The full example will show that the host
and port
options can
come from the environment variables.
import gofer from '@gofer-engine/engine';
const [messenger] = gofer.messenger((route) =>
route.send('tcp', EHR_HL7_IP, EHR_HL7_A20_PORT),
);
Use the Callback
The messenger callback function accepts a function that is run to build the
message to send to the route defined in the previous steps. This inline function
is expected to return any one of the classes that implement the IMsg
interface.
In this example, we will be using the HL7v2Msg
class.
import HL7v2Msg from '@gofer-engine/hl7';
// ...
messenger((msg: HL7v2Msg) => {
return msg
.set('MSH-3', 'Gofer Engine')
.set('MSH-4', 'Bed Board App')
.set('MSH-5', 'ACME Hospital')
.set('MSH-7', now)
.set('MSH-9', 'ADT^A20^ADT_A20')
.set('MSH-10', genId('UID'))
.set('MSH-11-1', 'T')
.set('MSH-11-2', 'T')
.set('MSH-12', '2.4')
.addSegment('EVN')
.set('EVN-1', now)
.set('EVN-5', operator)
.addSegment(['NPU', bed, status.toString()]);
});
Alternatively, we could create and return a new instance of the HL7v2Msg
class
instead of using the argument of this callback function, which provides the
interface IMsg
. We asserted that msg
is the HL7v2Msg
default class. If
we did not need custom methods such as the addSegment
method, then we would
not need to assert the class and could use any of the interface methods exposed
by every message type class.
Full Example
Here is the full detailed example of using Gofer Engine to send ADT_A20
messages within a Next.JS API page.
import gofer from '@gofer-engine/engine';
import HL7v2Msg from '@gofer-engine/hl7';
import { genId } from '@gofer-engine/tools';
const EHR_HL7_IP = process.env['EHR_HL7_IP'];
const EHR_HL7_A20_PORT = parseInt(process.env['EHR_HL7_A20_PORT']);
const [messenger] = gofer.messenger((route) =>
route.send('tcp', EHR_HL7_IP, EHR_HL7_A20_PORT),
);
const sendBedStatusUpdate = async (
bed: string,
status: 1 | 2,
operator: string,
) => {
const now = new Date().toISOString().replace(/-|:|T/g, '').slice(0, 12);
return messenger((msg: HL7v2Msg) => {
return msg
.set('MSH-3', 'Gofer Engine')
.set('MSH-4', 'Bed Board App')
.set('MSH-5', 'ACME Hospital')
.set('MSH-7', now)
.set('MSH-9', 'ADT^A20^ADT_A20')
.set('MSH-10', genId('UID'))
.set('MSH-11-1', 'T')
.set('MSH-11-2', 'T')
.set('MSH-12', '2.4')
.addSegment('EVN')
.set('EVN-1', now)
.set('EVN-5', operator)
.addSegment(['NPU', bed, status.toString()]);
});
};
export default sendBedStatusUpdate;
import sendBedStatusUpdate from './sample-messenger-function';
import type { NextApiRequest, NextApiResponse } from 'next';
const table0116_cust = {
Cleaning: 1,
Clean: 2,
} as const;
interface BedStatusUpdateRequest extends NextApiRequest {
body: {
bed: string;
status: keyof typeof table0116_cust;
operator: string;
};
}
const table0008 = {
AA: 'Accepted',
AE: 'Errored',
AR: 'Rejected',
CA: 'Commit Accepted',
CE: 'Commit Errored',
CR: 'Commit Rejected',
} as const;
type ResponseData = {
ack: 'Unknown' | (typeof table0008)[keyof typeof table0008];
};
const handler = async (
req: BedStatusUpdateRequest,
res: NextApiResponse<ResponseData>,
) => {
const { method } = req;
if (method !== 'POST') {
res.setHeader('Allow', ['POST']);
res.status(405).end(`Method ${method} Not Allowed`);
return;
}
const { bed, status, operator } = await req.json();
const mappedStatus = table0116_cust?.[status];
// TODO: validate input
const ackMsg = await sendBedStatusUpdate(bed, mappedStatus, operator);
const ack = table008?.[ackMsg.get('MSA-1')] || 'Unknown';
const message = ackMsg.get('MSA-3');
res.status(200).json({ ack, message });
};
export default handler;