<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>JavaScript on RockB</title><link>https://baeseokjae.github.io/tags/javascript/</link><description>Recent content in JavaScript on RockB</description><image><title>RockB</title><url>https://baeseokjae.github.io/images/og-default.png</url><link>https://baeseokjae.github.io/images/og-default.png</link></image><generator>Hugo</generator><language>en-us</language><lastBuildDate>Tue, 05 May 2026 21:03:58 +0000</lastBuildDate><atom:link href="https://baeseokjae.github.io/tags/javascript/index.xml" rel="self" type="application/rss+xml"/><item><title>LangGraph TypeScript Guide: Build AI Agents in 2026</title><link>https://baeseokjae.github.io/posts/langgraph-typescript-guide-2026/</link><pubDate>Tue, 05 May 2026 21:03:58 +0000</pubDate><guid>https://baeseokjae.github.io/posts/langgraph-typescript-guide-2026/</guid><description>Complete LangGraph TypeScript guide for 2026: StateGraph, streaming, checkpointing, HITL, and multi-agent systems with real code examples.</description><content:encoded><![CDATA[<p>LangGraph TypeScript (<code>@langchain/langgraph</code>) lets you build stateful, graph-based AI agents in Node.js with full type safety. As of 2026, it handles StateGraph, conditional edges, checkpointing, streaming, and human-in-the-loop — feature-parity with the Python version — and sees over 42,000 weekly npm downloads.</p>
<h2 id="what-is-langgraph-typescript-and-why-it-matters-in-2026">What Is LangGraph TypeScript (and Why It Matters in 2026)</h2>
<p>LangGraph TypeScript is a production-ready library for building stateful AI agent systems using a directed graph model, where nodes represent actions and edges represent transitions between states. Unlike simple chain-based frameworks, LangGraph lets agents loop, branch, pause for human input, and recover from failures without losing context. It reached full production stability in mid-2025, with feature parity to the Python version including StateGraph, conditional edges, checkpointing, streaming, and human-in-the-loop (HITL). The <code>@langchain/langgraph</code> npm package now records over 42,000 weekly downloads as of April 2026, making it the most-used graph-based agent framework in the JavaScript ecosystem.</p>
<p>Why does the graph model matter? Traditional LLM chains run top-to-bottom: prompt → LLM → output. That works for one-shot tasks but breaks down when agents need to retry, use tools, wait for approval, or coordinate with other agents. LangGraph models those workflows as graphs — each node is a function, each edge is a routing decision — giving you explicit control over execution flow. Companies like Klarna (85M active users), Replit, Uber, LinkedIn, GitLab, and Elastic all run LangGraph in production, which signals that the framework handles real scale. For TypeScript teams specifically, the type system catches entire categories of state-shape bugs at compile time that Python users only discover at runtime.</p>
<h2 id="prerequisites-and-installation-langchainlanggraph-setup">Prerequisites and Installation (@langchain/langgraph Setup)</h2>
<p>Setting up <code>@langchain/langgraph</code> requires Node.js 20 or later and TypeScript 5.4 or later — older versions lack the ES2022 features (structuredClone, native top-level await, improved class field semantics) that the library depends on. You also need an LLM provider package (typically <code>@langchain/openai</code> or <code>@langchain/anthropic</code>) and optionally <code>@langchain/community</code> for tool integrations like Tavily search or Wikipedia. The <code>zod</code> package is strongly recommended for defining tool input schemas with TypeScript type inference. For persistence, install <code>@langchain/langgraph-checkpoint-postgres</code> or <code>@langchain/langgraph-checkpoint-redis</code> depending on your production infrastructure — the in-process <code>MemorySaver</code> that ships with the core package is only suitable for local development. The full install for a production project looks like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>npm install @langchain/langgraph @langchain/openai @langchain/core zod
</span></span></code></pre></div><p>Configure TypeScript with strict mode and module resolution set to <code>bundler</code> or <code>node16</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;compilerOptions&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;target&#34;</span>: <span style="color:#e6db74">&#34;ES2022&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;module&#34;</span>: <span style="color:#e6db74">&#34;NodeNext&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;moduleResolution&#34;</span>: <span style="color:#e6db74">&#34;NodeNext&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;strict&#34;</span>: <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;outDir&#34;</span>: <span style="color:#e6db74">&#34;dist&#34;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Set your API key in the environment:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>export OPENAI_API_KEY<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;sk-...&#34;</span>
</span></span></code></pre></div><p>For production projects, the <code>@langgraphjs/toolkit</code> companion package adds observability hooks, retry utilities, and structured error types. Run <code>npm install @langgraphjs/toolkit</code> and import it alongside the core library.</p>
<h3 id="verifying-the-install">Verifying the Install</h3>
<p>Create <code>src/hello.ts</code> and confirm the import resolves:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">StateGraph</span>, <span style="color:#a6e22e">END</span>, <span style="color:#a6e22e">START</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@langchain/langgraph&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#34;LangGraph ready&#34;</span>);
</span></span></code></pre></div><p>Run with <code>npx ts-node src/hello.ts</code>. If it prints without error, your environment is correctly set up.</p>
<h2 id="core-concepts-stategraph-nodes-and-edges-explained">Core Concepts: StateGraph, Nodes, and Edges Explained</h2>
<p>A <code>StateGraph</code> in LangGraph TypeScript is the central data structure that defines what your agent remembers (state), what it can do (nodes), and how it decides what to do next (edges). State is a typed object — typically defined with Zod or TypeScript interfaces — that flows through every node in the graph. Each node receives the current state, performs some action (calling an LLM, running a tool, making an API request), and returns a partial state update. The graph merges that update into the running state before routing to the next node. This model is how LangGraph agents maintain context across dozens of tool calls without losing information or re-prompting for context already established.</p>
<p>Here is the minimal structure of a LangGraph TypeScript app:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">StateGraph</span>, <span style="color:#a6e22e">END</span>, <span style="color:#a6e22e">START</span>, <span style="color:#a6e22e">Annotation</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@langchain/langgraph&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// 1. Define state schema with Annotation
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">AgentState</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">Annotation</span>.<span style="color:#a6e22e">Root</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">messages</span>: <span style="color:#66d9ef">Annotation</span>&lt;<span style="color:#f92672">string</span><span style="color:#960050;background-color:#1e0010">[]</span>&gt;({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">reducer</span><span style="color:#f92672">:</span> (<span style="color:#a6e22e">prev</span>, <span style="color:#a6e22e">next</span>) <span style="color:#f92672">=&gt;</span> [...<span style="color:#a6e22e">prev</span>, ...<span style="color:#a6e22e">next</span>],
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">default</span><span style="color:#f92672">:</span> () <span style="color:#f92672">=&gt;</span> [],
</span></span><span style="display:flex;"><span>  }),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">status</span>: <span style="color:#66d9ef">Annotation</span><span style="color:#f92672">&lt;</span><span style="color:#e6db74">&#34;running&#34;</span> <span style="color:#f92672">|</span> <span style="color:#e6db74">&#34;done&#34;</span><span style="color:#f92672">&gt;</span>({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">reducer</span><span style="color:#f92672">:</span> (<span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">next</span>) <span style="color:#f92672">=&gt;</span> <span style="color:#a6e22e">next</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">default</span><span style="color:#f92672">:</span> () <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#34;running&#34;</span>,
</span></span><span style="display:flex;"><span>  }),
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// 2. Define a node
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">async</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">myNode</span>(<span style="color:#a6e22e">state</span>: <span style="color:#66d9ef">typeof</span> <span style="color:#a6e22e">AgentState</span>.<span style="color:#a6e22e">State</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> { <span style="color:#a6e22e">messages</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#34;Hello from node&#34;</span>], <span style="color:#a6e22e">status</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;done&#34;</span> <span style="color:#66d9ef">as</span> <span style="color:#66d9ef">const</span> };
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// 3. Build the graph
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">graph</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">StateGraph</span>(<span style="color:#a6e22e">AgentState</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">addNode</span>(<span style="color:#e6db74">&#34;myNode&#34;</span>, <span style="color:#a6e22e">myNode</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">addEdge</span>(<span style="color:#a6e22e">START</span>, <span style="color:#e6db74">&#34;myNode&#34;</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">addEdge</span>(<span style="color:#e6db74">&#34;myNode&#34;</span>, <span style="color:#a6e22e">END</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">compile</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// 4. Run
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">result</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">graph</span>.<span style="color:#a6e22e">invoke</span>({ <span style="color:#a6e22e">messages</span><span style="color:#f92672">:</span> [], <span style="color:#a6e22e">status</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;running&#34;</span> });
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#a6e22e">result</span>);
</span></span></code></pre></div><h3 id="reducers-how-state-merges">Reducers: How State Merges</h3>
<p>The <code>reducer</code> function in each <code>Annotation</code> field controls how partial updates merge into state. For message history, you almost always want <code>(prev, next) =&gt; [...prev, ...next]</code> — append semantics. For a status field, you want <code>(_, next) =&gt; next</code> — last-write-wins. Getting reducers right is the most common source of bugs in early LangGraph projects.</p>
<h3 id="conditional-edges">Conditional Edges</h3>
<p>Edges can be static (<code>addEdge</code>) or conditional (<code>addConditionalEdges</code>). Conditional edges take a routing function that inspects state and returns the name of the next node:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#a6e22e">graph</span>.<span style="color:#a6e22e">addConditionalEdges</span>(<span style="color:#e6db74">&#34;router&#34;</span>, (<span style="color:#a6e22e">state</span>) <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">state</span>.<span style="color:#a6e22e">status</span> <span style="color:#f92672">===</span> <span style="color:#e6db74">&#34;done&#34;</span> <span style="color:#f92672">?</span> <span style="color:#a6e22e">END</span> <span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;worker&#34;</span>;
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><h2 id="building-your-first-react-agent-in-typescript">Building Your First ReAct Agent in TypeScript</h2>
<p>A ReAct (Reasoning + Acting) agent is the most common LangGraph pattern: the LLM reasons about what to do, calls a tool, observes the result, and repeats until it has a final answer. LangGraph TypeScript implements this as a two-node loop — one node calls the LLM, one node executes tools — connected by a conditional edge that routes to END when the LLM stops requesting tools. This pattern is behind the majority of production LangGraph deployments at companies like Klarna and Replit, where the agent must search databases, call APIs, and synthesize multi-step results before responding to users.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">StateGraph</span>, <span style="color:#a6e22e">END</span>, <span style="color:#a6e22e">START</span>, <span style="color:#a6e22e">Annotation</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@langchain/langgraph&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">ChatOpenAI</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@langchain/openai&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">ToolNode</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@langchain/langgraph/prebuilt&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">tool</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@langchain/core/tools&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">z</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;zod&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">BaseMessage</span>, <span style="color:#a6e22e">HumanMessage</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@langchain/core/messages&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Define a tool
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">searchTool</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">tool</span>(
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">async</span> ({ <span style="color:#a6e22e">query</span> }<span style="color:#f92672">:</span> { <span style="color:#a6e22e">query</span>: <span style="color:#66d9ef">string</span> }) <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#e6db74">`Search results for: </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">query</span><span style="color:#e6db74">}</span><span style="color:#e6db74"> — found 3 relevant documents.`</span>;
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">name</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;search&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">description</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;Search the web for information&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">schema</span>: <span style="color:#66d9ef">z.object</span>({ <span style="color:#a6e22e">query</span>: <span style="color:#66d9ef">z.string</span>().<span style="color:#a6e22e">describe</span>(<span style="color:#e6db74">&#34;The search query&#34;</span>) }),
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">tools</span> <span style="color:#f92672">=</span> [<span style="color:#a6e22e">searchTool</span>];
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">model</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">ChatOpenAI</span>({ <span style="color:#a6e22e">model</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;gpt-4o&#34;</span> }).<span style="color:#a6e22e">bindTools</span>(<span style="color:#a6e22e">tools</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// State uses BaseMessage array for LangChain message history
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">AgentState</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">Annotation</span>.<span style="color:#a6e22e">Root</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">messages</span>: <span style="color:#66d9ef">Annotation</span>&lt;<span style="color:#f92672">BaseMessage</span><span style="color:#960050;background-color:#1e0010">[]</span>&gt;({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">reducer</span><span style="color:#f92672">:</span> (<span style="color:#a6e22e">prev</span>, <span style="color:#a6e22e">next</span>) <span style="color:#f92672">=&gt;</span> [...<span style="color:#a6e22e">prev</span>, ...<span style="color:#a6e22e">next</span>],
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">default</span><span style="color:#f92672">:</span> () <span style="color:#f92672">=&gt;</span> [],
</span></span><span style="display:flex;"><span>  }),
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Node 1: call the LLM
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">async</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">callModel</span>(<span style="color:#a6e22e">state</span>: <span style="color:#66d9ef">typeof</span> <span style="color:#a6e22e">AgentState</span>.<span style="color:#a6e22e">State</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">response</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">model</span>.<span style="color:#a6e22e">invoke</span>(<span style="color:#a6e22e">state</span>.<span style="color:#a6e22e">messages</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> { <span style="color:#a6e22e">messages</span><span style="color:#f92672">:</span> [<span style="color:#a6e22e">response</span>] };
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Routing: did the LLM request a tool call?
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">shouldContinue</span>(<span style="color:#a6e22e">state</span>: <span style="color:#66d9ef">typeof</span> <span style="color:#a6e22e">AgentState</span>.<span style="color:#a6e22e">State</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">lastMessage</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">state</span>.<span style="color:#a6e22e">messages</span>[<span style="color:#a6e22e">state</span>.<span style="color:#a6e22e">messages</span>.<span style="color:#a6e22e">length</span> <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>];
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> (<span style="color:#e6db74">&#34;tool_calls&#34;</span> <span style="color:#66d9ef">in</span> <span style="color:#a6e22e">lastMessage</span> <span style="color:#f92672">&amp;&amp;</span> (<span style="color:#a6e22e">lastMessage</span> <span style="color:#66d9ef">as</span> <span style="color:#66d9ef">any</span>).<span style="color:#a6e22e">tool_calls</span><span style="color:#f92672">?</span>.<span style="color:#a6e22e">length</span> <span style="color:#f92672">&gt;</span> <span style="color:#ae81ff">0</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#34;tools&#34;</span>;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">END</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Node 2: execute tools
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">toolNode</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">ToolNode</span>(<span style="color:#a6e22e">tools</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">reactGraph</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">StateGraph</span>(<span style="color:#a6e22e">AgentState</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">addNode</span>(<span style="color:#e6db74">&#34;agent&#34;</span>, <span style="color:#a6e22e">callModel</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">addNode</span>(<span style="color:#e6db74">&#34;tools&#34;</span>, <span style="color:#a6e22e">toolNode</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">addEdge</span>(<span style="color:#a6e22e">START</span>, <span style="color:#e6db74">&#34;agent&#34;</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">addConditionalEdges</span>(<span style="color:#e6db74">&#34;agent&#34;</span>, <span style="color:#a6e22e">shouldContinue</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">addEdge</span>(<span style="color:#e6db74">&#34;tools&#34;</span>, <span style="color:#e6db74">&#34;agent&#34;</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">compile</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Run the agent
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">result</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">reactGraph</span>.<span style="color:#a6e22e">invoke</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">messages</span><span style="color:#f92672">:</span> [<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">HumanMessage</span>(<span style="color:#e6db74">&#34;What are the latest TypeScript 5.5 features?&#34;</span>)],
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#a6e22e">result</span>.<span style="color:#a6e22e">messages</span>.<span style="color:#a6e22e">at</span>(<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>)<span style="color:#f92672">?</span>.<span style="color:#a6e22e">content</span>);
</span></span></code></pre></div><p>The key detail: <code>ToolNode</code> from <code>@langchain/langgraph/prebuilt</code> handles serialization, error catching, and message formatting automatically. Don&rsquo;t implement tool execution by hand.</p>
<h2 id="adding-real-time-streaming-to-your-langgraph-agent">Adding Real-Time Streaming to Your LangGraph Agent</h2>
<p>Streaming in LangGraph TypeScript lets users see the agent&rsquo;s reasoning and partial outputs as they happen, instead of waiting for a complete response. This matters enormously for UX: research from Vercel and Anthropic both show that users tolerate 3-5x longer wait times when they see progress. LangGraph supports two streaming modes — <code>&quot;values&quot;</code> streams the full state after each node, and <code>&quot;messages&quot;</code> streams individual LLM tokens as they generate. For chat interfaces, you almost always want <code>&quot;messages&quot;</code> mode. For debugging pipelines, <code>&quot;values&quot;</code> gives you a snapshot of state at each step.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#75715e">// Stream tokens from the agent
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">stream</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">reactGraph</span>.<span style="color:#a6e22e">stream</span>(
</span></span><span style="display:flex;"><span>  { <span style="color:#a6e22e">messages</span><span style="color:#f92672">:</span> [<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">HumanMessage</span>(<span style="color:#e6db74">&#34;Explain LangGraph checkpointing&#34;</span>)] },
</span></span><span style="display:flex;"><span>  { <span style="color:#a6e22e">streamingMode</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;messages&#34;</span> }
</span></span><span style="display:flex;"><span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> <span style="color:#66d9ef">await</span> (<span style="color:#66d9ef">const</span> [<span style="color:#a6e22e">message</span>, <span style="color:#a6e22e">metadata</span>] <span style="color:#66d9ef">of</span> <span style="color:#a6e22e">stream</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">message</span>.<span style="color:#a6e22e">_getType</span>() <span style="color:#f92672">===</span> <span style="color:#e6db74">&#34;AIMessageChunk&#34;</span> <span style="color:#f92672">&amp;&amp;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">metadata</span>.<span style="color:#a6e22e">langgraph_node</span> <span style="color:#f92672">===</span> <span style="color:#e6db74">&#34;agent&#34;</span>
</span></span><span style="display:flex;"><span>  ) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">process</span>.<span style="color:#a6e22e">stdout</span>.<span style="color:#a6e22e">write</span>(<span style="color:#a6e22e">message</span>.<span style="color:#a6e22e">content</span> <span style="color:#66d9ef">as</span> <span style="color:#66d9ef">string</span>);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="streaming-in-nextjs-with-the-vercel-ai-sdk">Streaming in Next.js with the Vercel AI SDK</h3>
<p>For Next.js apps, pipe LangGraph stream output through the Vercel AI SDK&rsquo;s <code>LangChainAdapter</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">LangChainAdapter</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;ai&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">async</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">POST</span>(<span style="color:#a6e22e">req</span>: <span style="color:#66d9ef">Request</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> { <span style="color:#a6e22e">messages</span> } <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">req</span>.<span style="color:#a6e22e">json</span>();
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">stream</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">reactGraph</span>.<span style="color:#a6e22e">stream</span>({ <span style="color:#a6e22e">messages</span> });
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">LangChainAdapter</span>.<span style="color:#a6e22e">toDataStreamResponse</span>(<span style="color:#a6e22e">stream</span>);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>This gives you <code>useChat()</code> hook compatibility on the frontend with zero extra plumbing.</p>
<h2 id="persistence-and-checkpointing-from-memorysaver-to-postgresql">Persistence and Checkpointing: From MemorySaver to PostgreSQL</h2>
<p>LangGraph checkpointing saves the full graph state after each node execution, enabling agents to pause, resume across restarts, and support human-in-the-loop interrupts. Without checkpointing, every agent run starts from scratch — no memory of previous interactions, no ability to pause for user input mid-execution. With checkpointing, your agent becomes stateful across sessions. LangGraph TypeScript ships with <code>MemorySaver</code> (in-process, for development) and supports PostgreSQL, Redis, and custom backends for production. The key design decision: all checkpointers implement the same interface, so you switch from <code>MemorySaver</code> to <code>PostgresSaver</code> with one line change — no agent code modifications required.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">MemorySaver</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@langchain/langgraph&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Development: in-memory checkpointing
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">memorySaver</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">MemorySaver</span>();
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">graphWithMemory</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">StateGraph</span>(<span style="color:#a6e22e">AgentState</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">addNode</span>(<span style="color:#e6db74">&#34;agent&#34;</span>, <span style="color:#a6e22e">callModel</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">addNode</span>(<span style="color:#e6db74">&#34;tools&#34;</span>, <span style="color:#a6e22e">toolNode</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">addEdge</span>(<span style="color:#a6e22e">START</span>, <span style="color:#e6db74">&#34;agent&#34;</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">addConditionalEdges</span>(<span style="color:#e6db74">&#34;agent&#34;</span>, <span style="color:#a6e22e">shouldContinue</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">addEdge</span>(<span style="color:#e6db74">&#34;tools&#34;</span>, <span style="color:#e6db74">&#34;agent&#34;</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">compile</span>({ <span style="color:#a6e22e">checkpointer</span>: <span style="color:#66d9ef">memorySaver</span> });
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Each thread_id is an isolated conversation
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">config</span> <span style="color:#f92672">=</span> { <span style="color:#a6e22e">configurable</span><span style="color:#f92672">:</span> { <span style="color:#a6e22e">thread_id</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;user-123-session-456&#34;</span> } };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">result1</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">graphWithMemory</span>.<span style="color:#a6e22e">invoke</span>(
</span></span><span style="display:flex;"><span>  { <span style="color:#a6e22e">messages</span><span style="color:#f92672">:</span> [<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">HumanMessage</span>(<span style="color:#e6db74">&#34;My name is Alex&#34;</span>)] },
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">config</span>
</span></span><span style="display:flex;"><span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">result2</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">graphWithMemory</span>.<span style="color:#a6e22e">invoke</span>(
</span></span><span style="display:flex;"><span>  { <span style="color:#a6e22e">messages</span><span style="color:#f92672">:</span> [<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">HumanMessage</span>(<span style="color:#e6db74">&#34;What is my name?&#34;</span>)] },
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">config</span> <span style="color:#75715e">// same thread_id = agent remembers &#34;Alex&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>);
</span></span></code></pre></div><h3 id="switching-to-postgresql-in-production">Switching to PostgreSQL in Production</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">PostgresSaver</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@langchain/langgraph-checkpoint-postgres&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#a6e22e">pg</span> <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;pg&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">pool</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">pg</span>.<span style="color:#a6e22e">Pool</span>({ <span style="color:#a6e22e">connectionString</span>: <span style="color:#66d9ef">process.env.DATABASE_URL</span> });
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">postgresSaver</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">PostgresSaver</span>.<span style="color:#a6e22e">fromConnString</span>(<span style="color:#a6e22e">process</span>.<span style="color:#a6e22e">env</span>.<span style="color:#a6e22e">DATABASE_URL</span><span style="color:#f92672">!</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">await</span> <span style="color:#a6e22e">postgresSaver</span>.<span style="color:#a6e22e">setup</span>(); <span style="color:#75715e">// creates checkpoint tables
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">productionGraph</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">reactGraph</span>.<span style="color:#a6e22e">compile</span>({ <span style="color:#a6e22e">checkpointer</span>: <span style="color:#66d9ef">postgresSaver</span> });
</span></span></code></pre></div><p>The <code>thread_id</code> pattern maps naturally to user sessions, document IDs, or any entity that needs isolated state.</p>
<h2 id="human-in-the-loop-pause-review-and-resume-agent-execution">Human-in-the-Loop: Pause, Review, and Resume Agent Execution</h2>
<p>Human-in-the-loop (HITL) in LangGraph TypeScript lets you pause agent execution at a specific node, surface the pending action to a human for review, and resume only after approval. This is critical for high-stakes operations: financial transactions, sending emails, writing to production databases, or deleting records. Without HITL, autonomous agents operating on real systems create unacceptable risk. LangGraph implements HITL through <code>interrupt_before</code> — a compile-time option that tells the graph to pause before executing a named node and save state to the checkpointer. The human reviews the pending state, approves or modifies it, and calls <code>graph.invoke()</code> again with the same <code>thread_id</code> to resume. No state is lost during the pause.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#75715e">// Compile with interrupt_before the &#34;tools&#34; node
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">hitlGraph</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">StateGraph</span>(<span style="color:#a6e22e">AgentState</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">addNode</span>(<span style="color:#e6db74">&#34;agent&#34;</span>, <span style="color:#a6e22e">callModel</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">addNode</span>(<span style="color:#e6db74">&#34;tools&#34;</span>, <span style="color:#a6e22e">toolNode</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">addEdge</span>(<span style="color:#a6e22e">START</span>, <span style="color:#e6db74">&#34;agent&#34;</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">addConditionalEdges</span>(<span style="color:#e6db74">&#34;agent&#34;</span>, <span style="color:#a6e22e">shouldContinue</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">addEdge</span>(<span style="color:#e6db74">&#34;tools&#34;</span>, <span style="color:#e6db74">&#34;agent&#34;</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">compile</span>({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">checkpointer</span>: <span style="color:#66d9ef">memorySaver</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">interruptBefore</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#34;tools&#34;</span>], <span style="color:#75715e">// pause before tool execution
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  });
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">config</span> <span style="color:#f92672">=</span> { <span style="color:#a6e22e">configurable</span><span style="color:#f92672">:</span> { <span style="color:#a6e22e">thread_id</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;approval-flow-001&#34;</span> } };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// First run: agent reasons, then pauses before tool call
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">step1</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">hitlGraph</span>.<span style="color:#a6e22e">invoke</span>(
</span></span><span style="display:flex;"><span>  { <span style="color:#a6e22e">messages</span><span style="color:#f92672">:</span> [<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">HumanMessage</span>(<span style="color:#e6db74">&#34;Delete all records from the test table&#34;</span>)] },
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">config</span>
</span></span><span style="display:flex;"><span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Inspect what the agent wants to do
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">pendingState</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">hitlGraph</span>.<span style="color:#a6e22e">getState</span>(<span style="color:#a6e22e">config</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#34;Agent wants to call:&#34;</span>, <span style="color:#a6e22e">pendingState</span>.<span style="color:#a6e22e">values</span>.<span style="color:#a6e22e">messages</span>.<span style="color:#a6e22e">at</span>(<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>));
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Human approves: resume by calling invoke again with same config
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// (no new messages needed — state is persisted)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">finalResult</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">hitlGraph</span>.<span style="color:#a6e22e">invoke</span>(<span style="color:#66d9ef">null</span>, <span style="color:#a6e22e">config</span>);
</span></span></code></pre></div><h3 id="modifying-state-before-resume">Modifying State Before Resume</h3>
<p>To reject or modify the pending action, use <code>updateState</code> before resuming:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">await</span> <span style="color:#a6e22e">hitlGraph</span>.<span style="color:#a6e22e">updateState</span>(<span style="color:#a6e22e">config</span>, {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">messages</span><span style="color:#f92672">:</span> [<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">HumanMessage</span>(<span style="color:#e6db74">&#34;Actually, only delete records from last week&#34;</span>)],
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">revised</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">hitlGraph</span>.<span style="color:#a6e22e">invoke</span>(<span style="color:#66d9ef">null</span>, <span style="color:#a6e22e">config</span>);
</span></span></code></pre></div><h2 id="building-multi-agent-systems-with-langgraph-typescript">Building Multi-Agent Systems with LangGraph TypeScript</h2>
<p>Multi-agent systems in LangGraph TypeScript use a supervisor/worker pattern where a coordinator agent routes tasks to specialized sub-agents based on the task requirements. Each sub-agent is a compiled LangGraph graph with its own state, tools, and logic — the supervisor treats sub-agents as nodes in its own graph. This architecture separates concerns: a coding agent handles code generation, a research agent handles web search, a writer agent handles document synthesis. In 2026, LangGraph&rsquo;s <code>@langchain/langgraph</code> supports two coordination patterns: supervisor (central router decides) and swarm (agents hand off directly to each other using <code>Command</code> returns).</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">Command</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@langchain/langgraph&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Worker agents as nodes that return Command for routing
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">async</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">researchWorker</span>(<span style="color:#a6e22e">state</span>: <span style="color:#66d9ef">typeof</span> <span style="color:#a6e22e">AgentState</span>.<span style="color:#a6e22e">State</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">result</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">researchGraph</span>.<span style="color:#a6e22e">invoke</span>({ <span style="color:#a6e22e">messages</span>: <span style="color:#66d9ef">state.messages</span> });
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Command</span>({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">update</span><span style="color:#f92672">:</span> { <span style="color:#a6e22e">messages</span>: <span style="color:#66d9ef">result.messages</span> },
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">goto</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;supervisor&#34;</span>, <span style="color:#75715e">// hand back to supervisor
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  });
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">async</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">codeWorker</span>(<span style="color:#a6e22e">state</span>: <span style="color:#66d9ef">typeof</span> <span style="color:#a6e22e">AgentState</span>.<span style="color:#a6e22e">State</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">result</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">codeGraph</span>.<span style="color:#a6e22e">invoke</span>({ <span style="color:#a6e22e">messages</span>: <span style="color:#66d9ef">state.messages</span> });
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Command</span>({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">update</span><span style="color:#f92672">:</span> { <span style="color:#a6e22e">messages</span>: <span style="color:#66d9ef">result.messages</span> },
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">goto</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;supervisor&#34;</span>,
</span></span><span style="display:flex;"><span>  });
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Supervisor routes to workers
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">async</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">supervisor</span>(<span style="color:#a6e22e">state</span>: <span style="color:#66d9ef">typeof</span> <span style="color:#a6e22e">AgentState</span>.<span style="color:#a6e22e">State</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">lastMessage</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">state</span>.<span style="color:#a6e22e">messages</span>.<span style="color:#a6e22e">at</span>(<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>)<span style="color:#f92672">?</span>.<span style="color:#a6e22e">content</span> <span style="color:#66d9ef">as</span> <span style="color:#66d9ef">string</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">lastMessage</span>.<span style="color:#a6e22e">includes</span>(<span style="color:#e6db74">&#34;search&#34;</span>) <span style="color:#f92672">||</span> <span style="color:#a6e22e">lastMessage</span>.<span style="color:#a6e22e">includes</span>(<span style="color:#e6db74">&#34;research&#34;</span>)) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Command</span>({ <span style="color:#66d9ef">goto</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;research_worker&#34;</span> });
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">lastMessage</span>.<span style="color:#a6e22e">includes</span>(<span style="color:#e6db74">&#34;code&#34;</span>) <span style="color:#f92672">||</span> <span style="color:#a6e22e">lastMessage</span>.<span style="color:#a6e22e">includes</span>(<span style="color:#e6db74">&#34;implement&#34;</span>)) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Command</span>({ <span style="color:#66d9ef">goto</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;code_worker&#34;</span> });
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Command</span>({ <span style="color:#66d9ef">goto</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">END</span> });
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">multiAgentGraph</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">StateGraph</span>(<span style="color:#a6e22e">AgentState</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">addNode</span>(<span style="color:#e6db74">&#34;supervisor&#34;</span>, <span style="color:#a6e22e">supervisor</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">addNode</span>(<span style="color:#e6db74">&#34;research_worker&#34;</span>, <span style="color:#a6e22e">researchWorker</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">addNode</span>(<span style="color:#e6db74">&#34;code_worker&#34;</span>, <span style="color:#a6e22e">codeWorker</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">addEdge</span>(<span style="color:#a6e22e">START</span>, <span style="color:#e6db74">&#34;supervisor&#34;</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">compile</span>({ <span style="color:#a6e22e">checkpointer</span>: <span style="color:#66d9ef">memorySaver</span> });
</span></span></code></pre></div><h3 id="when-to-use-supervisor-vs-swarm">When to Use Supervisor vs Swarm</h3>
<p>Use <strong>supervisor</strong> when tasks have clear categories and the coordinator needs to track overall progress. Use <strong>swarm</strong> (direct <code>Command</code> handoffs) when agents need to chain dynamically based on intermediate results — like a research agent discovering it needs code generated and handing off directly without returning to supervisor.</p>
<h2 id="langgraph-typescript-vs-mastra-vs-vercel-ai-sdk-which-should-you-choose">LangGraph TypeScript vs Mastra vs Vercel AI SDK: Which Should You Choose?</h2>
<p>LangGraph TypeScript is the best choice for stateful, long-running agent workflows that require explicit control over execution flow, checkpointing, and human-in-the-loop interrupts. As of 2026, four frameworks dominate the TypeScript AI agent space: LangGraph (graph-based, stateful), Mastra (TypeScript-first, Vercel-native), Vercel AI SDK (streaming-first, serverless-optimized), and OpenAI Agents SDK (provider-locked, simple handoffs). Each occupies a different niche — TypeScript teams switching from LangGraph to Mastra report setup time reduction from ~41 hours to ~18 hours for simple deployments, but Mastra lacks LangGraph&rsquo;s granular checkpointing and conditional routing for complex workflows. LangGraph still leads for durable, stateful, graph-based workflows.</p>
<table>
  <thead>
      <tr>
          <th>Feature</th>
          <th>LangGraph TS</th>
          <th>Mastra</th>
          <th>Vercel AI SDK</th>
          <th>OpenAI Agents SDK</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>State persistence</td>
          <td>PostgreSQL, Redis, custom</td>
          <td>Built-in (Postgres)</td>
          <td>Stateless by default</td>
          <td>None</td>
      </tr>
      <tr>
          <td>Human-in-the-loop</td>
          <td>Native <code>interruptBefore</code></td>
          <td>Plugin-based</td>
          <td>Not supported</td>
          <td>Not supported</td>
      </tr>
      <tr>
          <td>Streaming</td>
          <td><code>&quot;messages&quot;</code> + <code>&quot;values&quot;</code></td>
          <td>Supported</td>
          <td>Core feature</td>
          <td>Basic</td>
      </tr>
      <tr>
          <td>Multi-agent</td>
          <td>Supervisor + Swarm</td>
          <td>Workflows</td>
          <td>Not built-in</td>
          <td>Handoffs</td>
      </tr>
      <tr>
          <td>TypeScript safety</td>
          <td>Strong (Annotation API)</td>
          <td>Strong</td>
          <td>Strong</td>
          <td>Basic</td>
      </tr>
      <tr>
          <td>Learning curve</td>
          <td>High</td>
          <td>Medium</td>
          <td>Low</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Best for</td>
          <td>Complex stateful agents</td>
          <td>Next.js/Vercel teams</td>
          <td>Chat UIs, edge</td>
          <td>OpenAI-only projects</td>
      </tr>
  </tbody>
</table>
<h3 id="when-to-pick-each-framework">When to Pick Each Framework</h3>
<p><strong>LangGraph TypeScript</strong>: Stateful agents with complex routing, HITL for high-stakes ops, multi-agent systems, long-running workflows across sessions.</p>
<p><strong>Mastra</strong>: TypeScript-first teams on Vercel/Next.js infrastructure who need agents integrated into existing serverless deployments with faster initial setup.</p>
<p><strong>Vercel AI SDK</strong>: Chat interfaces and streaming UIs where agents are relatively simple and serverless execution is the constraint.</p>
<p><strong>OpenAI Agents SDK</strong>: Prototypes or teams fully committed to OpenAI models who want minimal configuration and are comfortable with provider lock-in.</p>
<h2 id="production-deployment-best-practices-and-observability">Production Deployment Best Practices and Observability</h2>
<p>Deploying LangGraph TypeScript agents to production requires attention to four areas: checkpointer durability, error handling, observability, and resource limits. The most common production failure mode is using <code>MemorySaver</code> in production — it doesn&rsquo;t persist across process restarts, so agent state is lost on every deploy. Switch to <code>PostgresSaver</code> or <code>RedisSaver</code> before going live. For observability, LangSmith (LangChain&rsquo;s tracing platform) integrates natively with LangGraph TypeScript: set <code>LANGCHAIN_TRACING_V2=true</code> and <code>LANGCHAIN_API_KEY</code> and every node execution, LLM call, and tool invocation appears in the LangSmith dashboard with full token counts and latency. Companies like LinkedIn and GitLab use this for production monitoring of their LangGraph deployments.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#75715e">// Production-ready graph configuration
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">PostgresSaver</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;@langchain/langgraph-checkpoint-postgres&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">productionSaver</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">PostgresSaver</span>.<span style="color:#a6e22e">fromConnString</span>(<span style="color:#a6e22e">process</span>.<span style="color:#a6e22e">env</span>.<span style="color:#a6e22e">DATABASE_URL</span><span style="color:#f92672">!</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">await</span> <span style="color:#a6e22e">productionSaver</span>.<span style="color:#a6e22e">setup</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">productionGraph</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">StateGraph</span>(<span style="color:#a6e22e">AgentState</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">addNode</span>(<span style="color:#e6db74">&#34;agent&#34;</span>, <span style="color:#a6e22e">callModel</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">addNode</span>(<span style="color:#e6db74">&#34;tools&#34;</span>, <span style="color:#a6e22e">toolNode</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">addEdge</span>(<span style="color:#a6e22e">START</span>, <span style="color:#e6db74">&#34;agent&#34;</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">addConditionalEdges</span>(<span style="color:#e6db74">&#34;agent&#34;</span>, <span style="color:#a6e22e">shouldContinue</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">addEdge</span>(<span style="color:#e6db74">&#34;tools&#34;</span>, <span style="color:#e6db74">&#34;agent&#34;</span>)
</span></span><span style="display:flex;"><span>  .<span style="color:#a6e22e">compile</span>({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">checkpointer</span>: <span style="color:#66d9ef">productionSaver</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">interruptBefore</span>: <span style="color:#66d9ef">process.env.REQUIRE_APPROVAL</span> <span style="color:#f92672">===</span> <span style="color:#e6db74">&#34;true&#34;</span> <span style="color:#f92672">?</span> [<span style="color:#e6db74">&#34;tools&#34;</span>] <span style="color:#f92672">:</span> [],
</span></span><span style="display:flex;"><span>  });
</span></span></code></pre></div><h3 id="setting-langsmith-tracing">Setting LangSmith Tracing</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>export LANGCHAIN_TRACING_V2<span style="color:#f92672">=</span>true
</span></span><span style="display:flex;"><span>export LANGCHAIN_API_KEY<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;ls__...&#34;</span>
</span></span><span style="display:flex;"><span>export LANGCHAIN_PROJECT<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;my-production-agent&#34;</span>
</span></span></code></pre></div><p>Every graph invocation automatically sends traces to LangSmith — no code changes needed.</p>
<h3 id="resource-limits-and-timeouts">Resource Limits and Timeouts</h3>
<p>Agents in production loops can run indefinitely if the routing logic has a bug. Set recursion limits to catch runaway loops:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">result</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">productionGraph</span>.<span style="color:#a6e22e">invoke</span>(
</span></span><span style="display:flex;"><span>  { <span style="color:#a6e22e">messages</span><span style="color:#f92672">:</span> [<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">HumanMessage</span>(<span style="color:#a6e22e">userInput</span>)] },
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">configurable</span><span style="color:#f92672">:</span> { <span style="color:#a6e22e">thread_id</span>: <span style="color:#66d9ef">sessionId</span> },
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">recursionLimit</span>: <span style="color:#66d9ef">25</span>, <span style="color:#75715e">// max 25 node executions per invoke
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  }
</span></span><span style="display:flex;"><span>);
</span></span></code></pre></div><p>Also implement timeout wrappers for tool calls — external APIs can hang, and a hanging tool call blocks the entire agent thread.</p>
<h3 id="deployment-options">Deployment Options</h3>
<p><strong>LangGraph Cloud</strong>: Managed hosting with built-in persistence, scaling, and the LangGraph Studio debugger. Best for teams that want zero infrastructure management.</p>
<p><strong>Self-hosted on Railway/Fly.io</strong>: Deploy as a Node.js service with <code>DATABASE_URL</code> pointing to a managed PostgreSQL instance. More control, similar simplicity.</p>
<p><strong>Vercel Edge Functions</strong>: Works with Vercel AI SDK adapter for streaming, but <code>MemorySaver</code> only — no persistent checkpointing. Good for stateless chat agents.</p>
<hr>
<h2 id="faq">FAQ</h2>
<p><strong>Q: Can I use LangGraph TypeScript without LangChain?</strong>
Yes. <code>@langchain/langgraph</code> has minimal coupling to LangChain — you can use any LLM provider by wrapping it in a function that returns <code>BaseMessage</code> objects. The <code>@langchain/openai</code> and <code>@langchain/anthropic</code> packages are optional convenience wrappers.</p>
<p><strong>Q: What is the difference between <code>MemorySaver</code> and <code>PostgresSaver</code>?</strong>
<code>MemorySaver</code> stores checkpoint state in process memory — it&rsquo;s fast, zero-config, and ideal for development and testing, but loses all state on process restart. <code>PostgresSaver</code> persists state to a PostgreSQL database, surviving restarts and scaling across multiple instances. Always use <code>PostgresSaver</code> (or <code>RedisSaver</code>) in production.</p>
<p><strong>Q: How do I debug a LangGraph agent that is looping indefinitely?</strong>
First, set <code>recursionLimit</code> in your invoke config (e.g., <code>{ recursionLimit: 25 }</code>). Then enable LangSmith tracing to visualize the node execution sequence. Common causes: the conditional edge routing function never returns <code>END</code>, a tool call always fails and the agent retries infinitely, or state mutation produces a different result on each pass.</p>
<p><strong>Q: Is LangGraph TypeScript production-ready in 2026?</strong>
Yes. LangGraph TypeScript reached production stability in mid-2025 with full feature parity to the Python version. Companies including Klarna (85M users), LinkedIn, Replit, Uber, and GitLab run LangGraph in production. The <code>@langchain/langgraph</code> package sees over 42,000 weekly npm downloads as of April 2026.</p>
<p><strong>Q: How does LangGraph TypeScript handle concurrent agent runs?</strong>
Each run is scoped to a <code>thread_id</code> in the checkpointer. Multiple concurrent <code>thread_id</code> values execute independently — there is no shared mutable state across threads. For true parallel execution within a single agent run, LangGraph supports fan-out edges where multiple nodes execute concurrently and fan back in through a merge node.</p>
]]></content:encoded></item></channel></rss>