Nola vs LangGraph
What LangGraph is
Section titled “What LangGraph is”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.
The same task, side by side
Section titled “The same task, side by side”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.tsimport { 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.
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:
import { classifyMessage } from "./classify.tsi";
const result = await classifyMessage("I was charged twice for order #4711.");console.log(result.category, result.reply);Where LangGraph is stronger
Section titled “Where LangGraph is stronger”- 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.
Where Nola is stronger
Section titled “Where Nola is stronger”- Reusing TypeScript types. The schema for
Triagecomes 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, andtrystatements 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.
Composing them
Section titled “Composing them”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.
When to pick which
Section titled “When to pick which”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