Skip to content

Nola vs LangGraph

LangGraph is an orchestration framework for stateful workflows and agents. This page uses its JavaScript package, @langchain/langgraph. You define state, add functions as nodes, connect them with edges, and invoke the compiled graph.

Its main features include persistence, resumable execution, streaming, and human-in-the-loop interrupts. It can use LangChain’s model integrations, but does not require them. See the LangGraph overview.

Nola focuses on typed model calls inside TypeScript programs. The two can be used together when a workflow needs both.

Both examples classify a support message, then write a reply using the category. This short sequence illustrates how each organizes the steps; it does not exercise LangGraph’s persistence or interrupt features.

LangGraph JS: two nodes share the message and results through graph state. The classifier uses LangChain’s withStructuredOutput with a Zod schema.

// not-checked — graph.ts
import { ChatOpenAI } from "@langchain/openai";
import { END, START, StateGraph, StateSchema } from "@langchain/langgraph";
import * as z from "zod";
const llm = new ChatOpenAI({ model: "gpt-5-mini" });
const Triage = z.object({
category: z.enum(["billing", "refund", "fraud", "other"]),
orderIds: z.array(z.string()),
});
const classifier = llm.withStructuredOutput(Triage);
const State = new StateSchema({
message: z.string(),
category: z.string(),
orderIds: z.array(z.string()),
reply: z.string(),
});
const graph = new StateGraph(State)
.addNode("classify", async (state) => {
const triage = await classifier.invoke([
{ role: "system", content: "You are a support classifier." },
{ role: "user", content: state.message },
]);
return { category: triage.category, orderIds: triage.orderIds };
})
.addNode("reply", async (state) => {
const res = await llm.invoke([
{ role: "system", content: "You are a support agent." },
{ role: "user", content: `Reply to this ${state.category} case: ${state.message}` },
]);
return { reply: String(res.content) }; // message content is not typed as string
})
.addEdge(START, "classify")
.addEdge("classify", "reply")
.addEdge("reply", END)
.compile();
const result = await graph.invoke({ message });

Nola: one infer function contains the two asks.

classify.tsi
export type Triage = {
category: "billing" | "refund" | "fraud" | "other";
orderIds: string[];
};
export infer function classifyMessage(.message: string) {
const triage = ask `triage the customer message`: Triage;
const reply = ask `a reply for a ${triage.category} case`: string;
return { ...triage, reply };
}

A TypeScript caller can import the function and await its result:

main.ts
import { classifyMessage } from "./classify.tsi";
const result = await classifyMessage("I was charged twice for order #4711.");
console.log(result.category, result.reply);
  • Durable execution. Checkpoints let a workflow resume after an interruption. Nola’s infer functions do not provide a persistent workflow engine.
  • Human review. Interrupts support pausing a workflow for input before continuing.
  • Inspecting a running workflow. State streaming and graph visualization help with workflows that have many steps. See the LangGraph overview for these capabilities and their setup.
  • Reusing TypeScript types. The schema for Triage comes from the TypeScript type. In the LangGraph example, the classifier’s output type comes from its Zod schema. Both approaches provide typed results.
  • Sharing context within a function. Both asks see .message. The graph example carries the same data in state so either node can read it.
  • Short sequences. A pair of asks can be written directly in a Nola function. TypeScript if, for, and try statements handle local control flow. A graph becomes more useful when its persistence and execution features are needed.

Nola derives schemas during compilation and validates model responses at runtime. Compilation cannot determine whether the model chose the correct category for a message.

A LangGraph node can await an exported Nola infer function and return its result as a state update. LangGraph then manages the workflow around the Nola call.

The Nola step must run on the server under a supported Node version. See Restrictions and NOLA4001.

Use LangGraph when a workflow must persist, pause for input, resume, or expose its intermediate state. Nola can handle extraction and classification inside its nodes.

For a short sequence within a TypeScript application, a Nola function may be enough. The deciding factor is whether you need a workflow engine around those calls.

Next: Nola vs Vercel AI SDK