Skip to the content.

🏠 Gofer Engine > Channel Workflows > Routing

Routing

“Routing” is the term used for outbound processing of messages in the Gofer Engine. Developing a channel route(s), you can build a route with either the JSON config route following the Route type, or you can use the OOP style and follow the ORoute interface.

Simplified types:

type Route = {
  kind: "route";
  id?: string | number; // a unique id for this route flow. If not provided will use UUID to generate. if not defined it may not be the same between deployments/reboots
  name?: string; // a human readable name for this route flow. Preferrably unique
  tags?: Tag[]; // Tags to help organize/identify route flows
  queue?: QueueConfig;
  flows:
    | RequiredProperties<RouteFlowNamed, "id">[]
    | (RouteFlow | RouteFlowNamed)[];
};

interface ORoute {
  name: (name: string) => ORoute;
  id: (id: string | number) => ORoute;
  filter: (f: FilterFlow) => ORoute;
  transform: (t: TransformFlow) => ORoute;
  store: (s: StoreConfig) => ORoute;
  setMsgVar: (varName: string, varValue: MsgVar) => ORoute;
  setChannelVar: <V>(varName: string, varValue: MsgVar<V>) => ORoute;
  setGlobalVar: <V>(varName: string, varValue: MsgVar<V>) => ORoute;
  getMsgVar: <V>(varName: string, getVal: WithVarDo<V>) => ORoute;
  getChannelVar: <V>(varName: string, getVal: WithVarDo<V>) => ORoute;
  getGlobalVar: <V>(varName: string, getVal: WithVarDo<V>) => ORoute;
  setVar: <V>(scope: varTypes, varName: string, varValue: MsgVar<V>) => ORoute;
  getVar: <V>(scope: varTypes, varName: string, getVal: WithVarDo<V>) => ORoute;
  setRouteVar: <V>(varName: string, varValue: MsgVar<V>) => ORoute;
  getRouteVar: <V>(varName: string, getVal: WithVarDo<V>) => ORoute;
  send: (method: "tcp", host: string, port: number) => ORoute;
  send: (method: "http", options: HTTPConfig) => ORoute;
  send: (method: "https", options: HTTPSConfig) => ORoute;
  export: () => RequiredProperties<Route, "id" | "flows">;
}

export type RouteFlow =
  | FilterFlow
  | TransformFlow
  | TransformOrFilterFlow
  | ({ kind: "store" } & StoreConfig)
  | Connection;

type RouteFlowNamed = {
  kind: "flow";
  id?: string | number; // a unique id for this route flow. If not provided will use UUID to generate. if not defined it may not be the same between deployments/reboots
  name?: string; // a human readable name for this route flow. Preferrably unique
  tags?: Tag[]; // Tags to help organize/identify route flows
  queue?: QueueConfig;
  flow: RouteFlow;
};

type FunctProp<R> = R | ((msg: IMsg, context: IMessageContext) => R);

interface TcpConfig {
  host: FunctProp<string>;
  port: FunctProp<number>;
  SoM?: FunctProp<string>;
  EoM?: FunctProp<number>;
  CR?: FunctProp<number>;
  responseTimeout?: number | false;
}

interface HTTPConfig {
  host: FunctProp<string>;
  port: FunctProp<>;
  method?:
    | "GET"
    | "HEAD"
    | "POST"
    | "PUT"
    | "DELETE"
    | "CONNECT"
    | "OPTIONS"
    | "TRACE"
    | "PATCH";
  path?: string;
  basicAuth?: {
    username: FunctProp<string>;
    password: FunctProp<string>;
  };
  responseTimeout?: number | false;
}

interface HTTPSConfig extends HTTPConfig {
  ca?: FunctProp<string | string[] | Buffer | Buffer[]>;
  cert?: FunctProp<string | string[] | Buffer | Buffer[]>;
  ciphers?: FunctProp<string>;
  clientCertEngine?: FunctProp<string>;
  crl?: FunctProp<string | string[] | Buffer | Buffer[]>;
  dhparam?: FunctProp<string | Buffer>;
  ecdhCurve?: FunctProp<string>;
  honorCipherOrder?: FunctProp<boolean>;
  key?: FunctProp<string | string[] | Buffer | Buffer[]>;
  passphrase?: FunctProp<string>;
  pfx?: FunctProp<
    | string
    | string[]
    | Buffer
    | Buffer[]
    | { buf: string | Buffer; passphrase?: string }[]
  >;
  secureOptions?: FunctProp<number>;
  secureProtocol?: FunctProp<string>;
  sessionIdContext?: FunctProp<string>;
  rejectUnauthorized?: FunctProp<boolean>;
  severname?: FunctProp<string>;
}

type Connection =
  | { kind: "tcp"; tcp: TcpConfig }
  | { kind: "http"; http: HTTPConfig }
  | { kind: "https"; https: HTTPSConfig };

type varTypes = "Global" | "Channel" | "Route" | "Msg";

type MsgVar = any | ((msg: IMsg, context?: IMessageContext) => any);

type WithVarDo = (
  v: any,
  msg: IMsg,
  context: IMessageContext
) => void | IMsg | boolean;

Refer to the Message Class (Msg) below for more information on the IMsg interface and transforming the data in the message.

Refer to the Context Object below for more information on the context type.

Refer to the Filter Flows below for more information on the FilterFlow type.

Refer to the Transform Flows below for more information on the TransformFlow type.

Refer to the Store Configs below for more information on the StoreConfig type.

Refer to the Queuing below for more information on the QueueConfig type.

Developing Routes using JSON Style

Using object configs offer simplified strongly typed configuration and allows the configs to be easily stringified to JSON for exporting/importing

import { Route, ChannelConfig } from "@gofer-engine/engine";

const sendMsg: Route = {
  kind: "route", // required for strong typing
  id: "ID_1",
  name: "Example Route",
  tags: [{ name: "Examples" }],
  queue: {},
  flows: [
    {
      kind: "flow",
      id: "ID_2",
      name: "Example Flow",
      tags: [{ name: "Examples" }],
      queue: {},
      flow: {
        tcp: {
          host: "192.168.1.200",
          port: 5600,
        },
      },
    },
  ],
};

const storeACK: Route = {
  kind: "route", // required for strong typing
  id: "ID_3",
  name: "Example ACK Store Route",
  tags: [{ name: "Examples" }],
  queue: {},
  flows: [
    {
      kind: "flow",
      id: "ID_4",
      name: "Example ACK Store Flow",
      tags: [{ name: "Examples" }],
      queue: {},
      flow: {
        store: {
          file: {},
        },
      },
    },
  ],
};

const channel: ChannelConfig = {
  name: "Example Channel",
  source: {},
  ingestion: [
    {
      kind: "ack",
      ask: { organization: "Example Org" },
    },
  ],
  routes: [[sendMsg, storeACK]],
};

The routes property in the ChannelConfig type takes a multidimensional array of Routes. This allows routes to be processed syncronously or asyncronously. Routes within the same inner array will be called sequentially. If a route sends to a tcp server, then the next route will be using the ACK returned by that server.

Developing Routes with OOP Style

name

Call the name method to name the route

// example.ts (continued)
route.name("A Unique Channel Name");

Back to top

id

Call the id method to override the generated id given to the route. If not provided the id will be a UUID

// example.ts (continued)
route.id(42);

Back to top

filter

Call the filter method to filter the message. See Filter Flow for the function definition

// example.ts (continued)
route.filter((msg) => msg.get("MSH-9.1") === "ADT");

// alternatively you can use the long form get classes
route.filter(
  (msg) =>
    msg.getSegment("MSH").getField(9).getComponent(1).toString() === "ADT"
);

Back to top

transform

Call the transform method to transform/modify the message. See Transform Flow for the function definition

// example.ts (continued)
route.transform((msg) => msg.set("MSH-5"), "Gofer");

Back to top

store

Call the store method to persist the message to a data store. See Store Config for the config definition

// example.ts (continued)
route.store({ file: {} });

Back to top

setVar

Call the setVar method to set a variable value for the specific scope. The varValue can either be the value itself or a function to callback to retrieve the value from the message and context. See Variables for more information on using variables.

// example.ts (continued)
route.setVar("Msg", "name", "FirstName");

// you can extract the value from the message
route.setVar("Channel", "facility", (msg) => msg.get("MSH-3.1"));

// you can strictly specify the type of the value
route.setVar<{ bar: string }>("Global", "foo", { bar: "baz" });

Back to top

setMsgVar

Call the setMsgVar method to set a variable value for the Message scope. Later in this message will be able to use this variable. The varValue can either be the value itself or a function to callback to retrieve the value from the message and context. See Variables for more information on using variables.

// example.ts (continued)
route.setMsgVar("name", "FirstName");

Back to top

setRouteVar

Call the setMsgVar method to set a variable value for the Message scope. Later in this message will be able to use this variable. The varValue can either be the value itself or a function to callback to retrieve the value from the message and context. See Variables for more information on using variables.

// example.ts (continued)
route.setMsgVar("name", "FirstName");

Back to top

setChannelVar

Call the setChannelVar method to set a variable value for the Channel scope. Later in this message or following messages within this same channel will be able to use this variable. The varValue can either be the value itself or a function to callback to retrieve the value from the message and context. See Variables for more information on using variables.

// example.ts (continued)
route.setChannelVar("facility", (msg) => msg.get("MSH-3.1"));

Back to top

setGlobalVar

Call the setGlobalVar method to set a variable value for the Global scope. Anywhere later in current or following messages within this same server will be able to use this variable. The varValue can either be the value itself or a function to callback to retrieve the value from the message and context. See Variables for more information on using variables.

// example.ts (continued)
route.setGlobalVar("foo", { bar: "baz" });

Back to top

getVar

Call the getVar method to get a previously set variable for the given scope by name. Define the callback function (cb) to do something with the value of the variable. You can use the value to filter or transform the message, or do something with the MessageContext.

// example.ts (continued)
route.getVar("Msg", "name", (name) => console.log(name));

Back to top

getMsgVar

Call the getMsgVar method to get a previously set variable for the Msg scope.

For example, you can use the value to transform the message

// example.ts (continued)
route.getMsgVar<string>("name", (name, msg) => msg.set("PID-5.2", name));

Back to top

getRouteVar

Call the getRouteVar method to get a previously set variable for the Route scope.

For example, you can use the value to transform the message

// example.ts (continued)
route.getRouteVar<string>("name", (name, msg) => msg.set("PID-5.2", name));

Back to top

getChannelVar

Call the getChannelVar method to get a previously set variable for the Channel scope.

For example, you can use the value and the context to log a message

// example.ts (continued)
route.getChannelVar<string>("facility", (facility, _, { logger }) => {
  logger(`Received message from ${facility}`, "info");
});

Back to top

getGlobalVar

Call the getGlobalVar method to get a previously set variable for the Channel scope

For example, you can use the value and filter the message by returning false

// example.ts (continued)
route.getGlobalVar<{ bar: string }>("foo", ({ bar }) => bar === "foo");

Back to top

send

Call the send method to configure a destination to send the message.

Currently only TCP (MLLP), HTTP, and HTTPS destinations are supported, but coming soon, Gofer Engine will also support ftp, sftp, and database direct queries/mutations.

For example, you can send the message via TCP to the IP and port provided by your EHR

// example.ts (continue)
route.send('tcp', "192.168.1.200", 5700);

You can also use a function to extract this information from the message or context

// example.ts (continue)
route.send(
  'tcp',
  (_msg, context) => context.getRouteVar<string>("host"),
  (_msg, context) => context.getRouteVar<number>("port"),
);

Another example, you can send the message to an HTTPS endpoint with a self-signed certificate, and provide basic authorization.

route.send(
  'https',
  {
    host: 'ehr.example.com',
    port: 443,
    basicAuth: {
      username: 'user',
      password: 'pass',
    },
    rejectUnauthorized: false,
  },
)

Back to top

export

Call the export method to save the configuration to a JSON object. This method is mainly used for testing purposes in Gofer Engine.

Back to top

Advanced Config Style Conformance

For advanced type control, you can pass through generics to the Route, RouteFlow, and RouteFlowNamed typed.

  1. The first generic seen in source as Filt controls the type of the filter style to either (F) Functional configs, (O) Ojectified configs, or (B) to allow Both config styles.
  2. The second generic seen in source as Tran controls the type of the transformer style to either (F) Functional configs, (O) Ojectified configs, or (B) to allow Both config styles.
  3. The third generic seen in source as Stct controls controls the strictness of the configs to either (S) Strictly require objectified configs with ids, or (L) then allow looser config without requirind ids.