Validators

Validate incoming messages before transformation

Overview

Validators are TypeScript functions that run before the transformer in a channel's processing pipeline. They inspect the structure and content of incoming messages and either accept them silently (by returning void) or reject them (by throwing an Error).

When a validator throws, the message is immediately rejected — it never reaches the transformer or any destination. If error handling is configured for the channel (e.g. a dead-letter queue), the rejected message and the error details are forwarded there for later inspection.

Configuration

Validators are declared in a channel's channel.yaml. There are two equivalent ways to wire a validator into the pipeline.

Explicit validator block

yaml
validator:
  runtime: node
  entrypoint: validator.ts

Pipeline shorthand

yaml
pipeline:
  validator: validator.ts
Note Both forms are functionally identical. The pipeline shorthand is more concise when you have multiple pipeline stages declared together.

Writing a Validator

A validator is a named export called validate. It receives the parsed message and an optional context object. If the function returns normally, the message is accepted. If the function throws an Error, the message is rejected.

Function signature

typescript
export function validate(msg: unknown, ctx?: { channelId: string; correlationId: string }): void {
  // Throw an error to reject the message
  // Return void to accept
}
Parameter Type Description
msg unknown The incoming message, automatically parsed according to data_types.inbound
ctx.channelId string The name of the channel processing this message
ctx.correlationId string Unique correlation ID for distributed tracing

Examples

Basic Object Validation

Reject anything that is not a non-null object:

typescript
export function validate(msg: unknown): void {
  if (!msg || typeof msg !== "object") {
    throw new Error("Message must be a non-null object");
  }
}

HL7 ADT Message Validation

Ensure required segments and fields are present on an HL7v2 ADT message:

typescript
export function validate(msg: unknown): void {
  const m = msg as Record<string, any>;

  if (!m.MSH) throw new Error("Missing MSH segment");
  if (!m.PID) throw new Error("Missing PID segment");
  if (!m.PID["3.1"]) throw new Error("Missing patient ID (PID-3.1)");
  if (!m.EVN) throw new Error("Missing EVN segment");
}

FHIR Resource Validation

Validate that a FHIR resource has the required type and mandatory fields:

typescript
export function validate(msg: unknown): void {
  const resource = msg as Record<string, any>;

  if (!resource.resourceType) {
    throw new Error("Missing resourceType");
  }

  if (resource.resourceType === "Patient") {
    if (!resource.identifier?.length) {
      throw new Error("Patient must have at least one identifier");
    }
    if (!resource.name?.length) {
      throw new Error("Patient must have at least one name");
    }
  }
}

Lab Result Validation

Check that a lab result message has all the required fields and a well-formed results array:

typescript
export function validate(msg: unknown): void {
  const lab = msg as Record<string, any>;

  if (!lab.orderId) throw new Error("Missing orderId");
  if (!lab.patientId) throw new Error("Missing patientId");
  if (!lab.results || !Array.isArray(lab.results)) {
    throw new Error("results must be an array");
  }

  for (const result of lab.results) {
    if (!result.code) throw new Error("Each result must have a code");
    if (result.value === undefined) throw new Error("Each result must have a value");
  }
}

Error Handling

When a validator throws an Error, intu handles the rejection as follows:

  1. The message is rejected and does not proceed to the transformer or any destination.
  2. The error is logged with the original message payload and the thrown error message.
  3. If error_handling.dlq is configured in the channel's YAML, the message is forwarded to the dead-letter queue along with error metadata.
  4. The message property of the thrown Error is included in the error log entry, so use descriptive messages to make debugging easier.
yaml
error_handling:
  dlq:
    ref: kafka-dlq
    include_original: true
    include_error: true
Tip Always include clear, specific messages in your thrown errors. Messages like "Missing patient ID (PID-3.1)" are far easier to triage than generic "Validation failed" strings.

Data Types

When data_types.inbound is set in channel.yaml, intu automatically parses the raw incoming payload before it reaches the validator. The msg argument your validator receives is already the parsed representation.

Data Type Parsed As
raw Raw string — no parsing applied
json Parsed JSON object
xml Parsed XML represented as a JSON structure
csv Parsed CSV as an array of objects (header row becomes keys)
hl7v2 Parsed HL7v2 message as a JSON structure with segment/field accessors
fhir_r4 Parsed FHIR R4 resource (JSON)
x12 Parsed X12 transaction as a JSON structure
yaml
data_types:
  inbound: hl7v2
  outbound: fhir_r4
Note If data_types.inbound is not set, the message is passed to the validator as a raw string. Your validator is then responsible for any parsing it needs.