The runtime type system & structural API boundary layer for LangGraphJS agents.
While TypeScript provides compile-time safety, LLMs operate in the untyped world of unstructured text and JSON strings. langgraph-zod bridges that gap by deriving production-ready runtime boundary schemas directly from your LangGraph state channel definitions.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Incoming HTTP / REST Payloadโ
โโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโผโโโโโโโโโโโโโ
โ inputSchema.parse() โ (Validates & Coerces)
โโโโโโโโโโโโโโฌโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโผโโโโโโโโโโโโโ
โ LangGraph Execution Loopโ
โโโโโโโโโโโโโโฌโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโผโโโโโโโโโโโโโ
โ outputSchema.parse() โ (Strips Internal Keys)
โโโโโโโโโโโโโโฌโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโ
โ Clean Public REST API Output โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- ๐ก๏ธ Boundary Validation (
createGraphSchemas): DerivesinputSchema(partial with optional required fields) andoutputSchema(omitting internal scratchpads/keys) from state definitions. - ๐ก Streaming-First SSE Validation (v0.2.0): Incremental SSE parser +
TransformStreamthat validatesgraph.streamEvents()payloads on the fly, drops malformed chunks, and handles backpressure โ with <1ms per-chunk validation latency. - ๐ Framework-Agnostic
AgentBoundary(v0.2.0): Wrap any async function or async generator โ LangGraph, Vercel AI SDK, AutoGen TS, or plain custom agents โ with the same input/output validation boundary. - ๐งฐ Vercel AI SDK Adapter (v0.2.0): Validate
tool()arguments/results andstreamText()params/fullStreamparts without taking a hard dependency on theaipackage. - ๐ฎ Zod v3 & v4 Resilient JSON Schema (v0.2.0): All internal AST inspection flows through a safe introspection layer (
zod-ast) โ with support for discriminated unions, intersections, recursiveZodLazy, tuples, records, and more. - ๐ Production Telemetry (v0.2.0): Structural OpenTelemetry hooks (
validation.error_count,channel.failed_key, latency histograms) plus Pino/Winston formatters forSchemaValidationError. - ๐ฆ Zod-Native Channel Reducers (
zodChannel): Type-safe channel reducers for LangGraphAnnotation.Rootstate (arrays, sums, latest value, custom accumulators). - ๐ LLM Output Parsing & Self-Healing (
safeParseLLMOutput): Extracts JSON from markdown blocks, validates against Zod schemas, and retries with repair functions. - โก HTTP Framework Adapters: Turn any LangGraph agent into a validated API endpoint for Next.js App Router, Hono, or Express/Fastify in 3 lines โ now with SSE streaming variants (
createNextStreamHandler,createHonoStreamHandler). - ๐ OpenAPI 3.1 & JSON Schema Generator: Automatically output interactive OpenAPI specifications with
/invokeand/batchendpoints. - ๐ Schema Registry & Breaking Change Diffing (
GraphSchemaRegistry): Version your agent schemas and detect breaking API changes in CI before deployment. - โ๏ธ Composable Middleware Pipeline: Hook node-level logging, retries, and strict schema validation before and after node execution.
# pnpm
pnpm add langgraph-zod zod @langchain/langgraph
# npm
npm install langgraph-zod zod @langchain/langgraph
# yarn
yarn add langgraph-zod zod @langchain/langgraphimport { z } from "zod";
import { createGraphSchemas } from "langgraph-zod";
// Define your agent channels with Zod
const AgentChannels = {
userQuery: z.string().describe("The primary prompt from the user"),
chatHistory: z.array(z.string()).default([]),
internalScratchpad: z.string().optional().describe("Internal agent thoughts"),
retryCount: z.number().default(0),
};
// Derive runtime input, output, and full state boundary schemas
export const { inputSchema, outputSchema, stateSchema, validateInput } =
createGraphSchemas(AgentChannels, {
omitOutputKeys: ["internalScratchpad", "retryCount"],
requiredInputKeys: ["userQuery"],
});import { createValidatedGraph } from "langgraph-zod";
import { myLangGraphAgent } from "./agent";
export const agentApi = createValidatedGraph(
async (input) => {
return await myLangGraphAgent.invoke(input);
},
{
inputSchema,
outputSchema,
strict: true,
}
);
const response = await agentApi.invoke({ userQuery: "Analyze revenue" });
// response.data contains validated query and chatHistory, with internalScratchpad stripped!// app/api/agent/route.ts
import { createNextRouteHandler } from "langgraph-zod/adapters";
import { agentApi } from "@/lib/agent";
export const { POST, GET } = createNextRouteHandler({ graph: agentApi });// app/api/agent/stream/route.ts
import { createNextStreamHandler } from "langgraph-zod/adapters";
import { z } from "zod";
export const { POST } = createNextStreamHandler({
graph: compiledGraph, // any graph exposing streamEvents()
schemas, // optional: validates the inbound POST payload
eventSchemas: { // per-event data-payload schemas
on_chat_model_stream: z.object({ chunk: z.any() }).passthrough(),
on_tool_end: z.object({ output: z.string() }).passthrough(),
},
onInvalidEvent: (info) => console.warn("dropped event", info),
});Invalid or malformed events are dropped before they reach the client; the
pull-based ReadableStream applies natural backpressure to slow consumers.
For lower-level control, use createSSEStreamTransformer() (SSE wire format)
or validateEventStream() (object-mode event streams) from
langgraph-zod/streaming.
import { AgentBoundary } from "langgraph-zod";
const boundary = new AgentBoundary({
inputSchema: z.object({ prompt: z.string() }),
outputSchema: z.object({ text: z.string() }),
});
// Any async function โ Vercel AI SDK, AutoGen TS, custom agents:
export const run = boundary.wrapAsyncFn(async (input) => myAgent(input));
// Any async generator โ every yielded chunk is validated:
export const stream = boundary.wrapGenerator(async function* (input) {
yield* myAgent.stream(input);
});import { createValidationTelemetry, pinoValidationErrorSerializer } from "langgraph-zod";
import { trace, metrics } from "@opentelemetry/api";
const telemetry = createValidationTelemetry({
tracer: trace.getTracer("my-agent"),
meter: metrics.getMeter("my-agent"),
});
// Emits span + counters + duration histogram with
// validation.error_count / channel.failed_key attributes on failure:
const result = telemetry.traceValidation("validateInput", () =>
schemas.validateInput(payload)
);
// Pino: pino({ serializers: { err: pinoValidationErrorSerializer } })
// Winston: winston.format(winstonValidationErrorFormat)# Export OpenAPI 3.1 Specification
npx langgraph-zod openapi ./src/schemas.ts --out openapi.json
# Check for breaking schema changes in CI
npx langgraph-zod diff ./schemas.v1.ts ./schemas.v2.tshttps://github.com/shahrryyar/langgraph-zod
ISC ยฉ 2026
