LangGraph Tutorial (TypeScript): streaming agent responses for intermediate developers

By Cyprian AaronsUpdated 2026-04-22
langgraphstreaming-agent-responses-for-intermediate-developerstypescript

This tutorial shows you how to stream agent responses from a LangGraph workflow in TypeScript, token by token, while still keeping the graph structure clean and debuggable. You need this when you want a chat UI to feel responsive, or when long-running agent steps should start producing output before the full run finishes.

What You'll Need

  • Node.js 18+
  • A TypeScript project with ts-node or a build step
  • langgraph
  • @langchain/openai
  • @langchain/core
  • An OpenAI API key in OPENAI_API_KEY
  • Basic familiarity with LangGraph nodes, edges, and state

Install the packages:

npm install langgraph @langchain/openai @langchain/core
npm install -D typescript ts-node @types/node

Step-by-Step

  1. Start with a minimal graph state that stores chat messages. For streaming, keep the state simple: one array of messages is enough.
import { Annotation, StateGraph, START, END } from "langgraph";
import { AIMessage, HumanMessage } from "@langchain/core/messages";

const GraphState = Annotation.Root({
  messages: Annotation<any[]>({
    reducer: (left, right) => left.concat(right),
    default: () => [],
  }),
});

type GraphStateType = typeof GraphState.State;
  1. Create an LLM node that returns an assistant message. Use a model that supports streaming so the graph can emit chunks as they are generated.
import { ChatOpenAI } from "@langchain/openai";

const llm = new ChatOpenAI({
  model: "gpt-4o-mini",
  temperature: 0,
});

async function agentNode(state: GraphStateType) {
  const response = await llm.invoke(state.messages);
  return { messages: [response] };
}
  1. Build the graph and compile it. This is standard LangGraph wiring; the important part is that your node returns a message object that can be streamed later.
const graph = new StateGraph(GraphState)
  .addNode("agent", agentNode)
  .addEdge(START, "agent")
  .addEdge("agent", END);

const app = graph.compile();
  1. Stream events from the compiled graph. In practice, you usually want streamMode: "messages" for token-level output in a chat app, or "updates" if you only care about node-level state changes.
async function main() {
  const input = {
    messages: [new HumanMessage("Write a short haiku about bank risk controls.")],
  };

  const stream = await app.stream(input, { streamMode: "messages" });

  for await (const [message, metadata] of stream) {
    if (message.content) {
      process.stdout.write(String(message.content));
    }
    if (metadata?.langgraph_node === "agent") {
      // useful for tracing which node emitted the chunk
    }
  }

  process.stdout.write("\n");
}

main().catch(console.error);
  1. If you want cleaner UI behavior, separate transport concerns from graph logic. The graph should emit chunks; your server or frontend should decide whether to buffer them, display them live, or convert them into SSE/WebSocket frames.
async function streamToConsole() {
  const result = await app.stream(
    { messages: [new HumanMessage("Explain why streaming matters in one sentence.")] },
    { streamMode: "messages" }
  );

  let fullText = "";
  for await (const [msg] of result) {
    if (typeof msg.content === "string") {
      fullText += msg.content;
      process.stdout.write(msg.content);
    }
  }

  console.log("\n\nFull text:", fullText);
}

Testing It

Run the script with OPENAI_API_KEY set in your environment. You should see text appear incrementally instead of waiting for one final response.

If nothing streams and you only get output at the end, check two things first: your model must support streaming, and your code must use app.stream(...), not app.invoke(...). Also verify that your terminal or server layer is not buffering output.

A good sanity test is to ask for a longer answer, like “list five reasons to stream agent responses.” If the implementation is correct, you will see partial text arrive before completion.

Next Steps

  • Add tool calling to the agent node and stream both tool progress and final assistant output.
  • Wrap app.stream(...) in an SSE endpoint for browser clients.
  • Learn streamMode: "updates" versus "messages" so you can choose the right level of granularity for each UI.

Keep learning

By Cyprian Aarons, AI Consultant at Topiax.

Want the complete 8-step roadmap?

Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.

Get the Starter Kit

Related Guides