Package Exports
- @ruvos/handler-sdk
Readme
@ruvos/handler-sdk
SDK for building custom pipeline handlers for the Ruvos healthcare data exchange platform.
Handlers let you intercept and process FHIR resources at three stages of the Ruvos data pipeline:
post_downstream_fetch— after data is fetched from an external sourcepre_store— before data is encrypted and storedpost_store— after data is persisted
Installation
npm install @ruvos/handler-sdkQuick Start
Scaffold a new handler project
npx @ruvos/handler-sdk create-handler my-handler
cd ruvos-handler-my-handler
npm installThis generates a complete project with TypeScript config, esbuild bundling, Terraform infrastructure, and a GitLab CI pipeline.
Write a handler
import { createHandler, type HandlerInput, type HandlerOutcome } from "@ruvos/handler-sdk";
async function handle(input: HandlerInput): Promise<HandlerOutcome> {
const { stage, payload, tenantId, connectorType } = input;
// Transform: modify resources before storage
if (stage === "pre_store" && payload.resources) {
const enriched = payload.resources.map((r) => ({
...r,
meta: { ...r.meta, tag: [{ code: "reviewed", display: "Reviewed by handler" }] },
}));
return { action: "transform", resources: enriched };
}
// Passthrough: continue normal pipeline
return { action: "passthrough" };
}
export const handler = createHandler({ handlerId: "my-handler" }, handle);Handler Outcomes
Every handler must return one of five outcomes:
| Outcome | Description |
|---|---|
passthrough |
Continue normal pipeline processing |
transform |
Replace resources with a modified set |
route_to |
Forward the payload to an HTTPS URL (skip normal storage) |
requeue |
Retry processing later with an optional delay |
fail |
Abort the job with a reason |
// passthrough
return { action: "passthrough" };
// transform
return { action: "transform", resources: modifiedResources, metadata: { source: "enriched" } };
// route_to
return { action: "route_to", url: "https://my-service.example.com/ingest", body: payload };
// requeue
return { action: "requeue", delaySeconds: 30 };
// fail
return { action: "fail", reason: "Missing required patient identifier" };API Reference
createHandler(options, fn)
Wraps a handler function with logging, timeout, and outcome validation.
import { createHandler } from "@ruvos/handler-sdk";
export const handler = createHandler(
{ handlerId: "my-handler", timeoutMs: 60_000 },
async (input) => { /* ... */ }
);Options:
handlerId— unique identifier for this handlertimeoutMs— execution timeout (default: 120,000ms)
parseCcda(xml)
Parses a C-CDA XML document into FHIR resources (Patient + Composition).
import { parseCcda } from "@ruvos/handler-sdk";
const resources = parseCcda(xmlString);
// [{ resourceType: "Patient", ... }, { resourceType: "Composition", ... }]validateOutcome(value)
Validates that a handler outcome has the correct structure. Called automatically by createHandler, but available for testing.
import { validateOutcome, HandlerValidationError } from "@ruvos/handler-sdk";
try {
const validated = validateOutcome(result);
} catch (err) {
if (err instanceof HandlerValidationError) {
console.error("Invalid outcome:", err.message);
}
}createHandlerLogger(context)
Creates a structured JSON logger for use within handlers.
import { createHandlerLogger } from "@ruvos/handler-sdk";
const logger = createHandlerLogger({
handlerId: "my-handler",
correlationId: input.correlationId,
tenantId: input.tenantId,
stage: input.stage,
});
logger.info("processing_resources", { count: input.payload.resources?.length });Types
type HookStage = "post_downstream_fetch" | "pre_store" | "post_store";
interface HandlerInput {
stage: HookStage;
tenantId: string;
jobId: string;
correlationId: string;
connectorType: string;
config?: Record<string, unknown>;
payload: {
resources?: FHIRResource[];
metadata?: Record<string, unknown>;
s3Key?: string;
};
}
type HandlerOutcome =
| { action: "passthrough" }
| { action: "transform"; resources: FHIRResource[]; metadata?: Record<string, unknown> }
| { action: "route_to"; url: string; method?: string; headers?: Record<string, string>; body: unknown }
| { action: "requeue"; delaySeconds?: number }
| { action: "fail"; reason: string };
interface FHIRResource {
resourceType: string;
id?: string;
[key: string]: unknown;
}License
MIT