Skip to content

ACK middleware

MLLP middleware that generates HL7v2 acknowledgments automatically from handler outcomes.

MLLP middleware that generates HL7v2 acknowledgments automatically from handler outcomes.

What it does

@glion/mllp-ack exports ackMiddleware(), a middleware for @glion/mllp that wraps your route handlers and produces an ACK/NAK response for every accepted message. When a handler returns without throwing, the middleware emits an AA (or a configurable success code such as CA for commit-level acknowledgments). When a handler throws an AckException from @glion/ack, the middleware translates it into the matching AE/AR/CE/CR response with an ERR segment. Plain Error values become ApplicationInternalError (AE, error code 207), so routes that forget to handle a failure still produce a valid NAK instead of silence.

Install

npm install @glion/mllp-ack

The package has a peer dependency on @glion/mllp.

Use

import { Mllp } from "@glion/mllp";
import { serve } from "@glion/mllp/node";
import { parseHL7v2 } from "@glion/hl7v2";
import { ackMiddleware } from "@glion/mllp-ack";

const app = new Mllp().parser(parseHL7v2);

app.use(ackMiddleware());

app.on("ADT^A01", async (ctx) => {
  // Process the message; no return needed — middleware sends AA automatically.
});

const server = serve(app, { port: 2575 });

Throw a typed exception to produce a NAK:

import {
  AckApplicationError,
  Hl7ErrorCode,
  Severity,
  UnsupportedMessageTypeReject,
} from "@glion/ack";

app.on("ADT^A01", async (ctx) => {
  const patient = await findPatient(ctx);
  if (!patient) {
    throw new AckApplicationError("Patient not found", {
      errorCode: Hl7ErrorCode.UnknownKeyIdentifier,
      severity: Severity.Error,
    });
  }
});

app.on("*", async (ctx) => {
  throw new UnsupportedMessageTypeReject(
    `Unsupported: ${ctx.messageType}^${ctx.triggerEvent}`
  );
});

For enhanced-mode processing, switch the success code to CA and use commit-level exceptions:

import { AckCode, AckCommitError, Hl7ErrorCode, Severity } from "@glion/ack";

app.use(ackMiddleware({ successCode: AckCode.CommitAccept }));

app.on("ADT^A01", async (ctx) => {
  try {
    await persistMessage(ctx);
  } catch (err) {
    throw new AckCommitError("Failed to persist message", {
      errorCode: Hl7ErrorCode.ApplicationInternalError,
      severity: Severity.Error,
      cause: err,
    });
  }
});

API

ackMiddleware(options?)

Returns a Middleware function for use with Mllp.use().

OptionTypeDescription
sendingSendingInfoMSH-3/MSH-4 of the ACK. Defaults to original message's MSH-5/MSH-6
generateId() => stringCustom ID generator for MSH-10. Uses uid() from @glion/ack
successCodeAckSuccessCodeMSA-1 code for success. Defaults to AckCode.ApplicationAccept (AA)

Exception classes

ClassMSA-1Description
AckApplicationErrorAEApplication-level error
AckApplicationRejectARApplication-level reject
AckCommitErrorCECommit-level error
AckCommitRejectCRCommit-level reject
ApplicationInternalErrorAEPre-configured: error code 207, severity E
UnsupportedMessageTypeRejectARPre-configured: error code 200, severity E
CommitInternalErrorCEPre-configured: error code 207, severity E

All exception classes are re-exported from @glion/ack.

Behavior

Handler outcome to MSA-1 mapping

Handler outcomeMSA-1ERR segment
returns (no throw)successCodeno
throws AckApplicationErrorAEyes
throws AckApplicationRejectARyes
throws AckCommitErrorCEyes
throws AckCommitRejectCRyes
throws a plain Error or non-ErrorAE (code 207)yes
handler set ctx.res and did not throwpassed through

If ctx.res was already set by a handler and nothing threw, the middleware leaves it untouched.

ctx.ast vs ctx.tree()

The middleware reads ctx.ast — the raw parsed tree — rather than await ctx.tree(). acknowledge() only needs the MSH header fields (MSH-3 through MSH-12), which are already present in the pre-transform tree. Avoiding ctx.tree() means the transform pipeline (escape decoding, annotations, lint) is not triggered for ACK construction, so the middleware adds zero async overhead. If you write custom ACK middleware, follow the same pattern.

Interaction with onError

The middleware catches handler errors and converts them into ACK responses. If ACK construction itself fails (for example, a malformed AST or a serialization error), the error propagates to the Mllp instance's onError handler as an infrastructure-level safety net.