LangGraph Tutorial (TypeScript): adding memory to agents for advanced developers
This tutorial shows how to give a LangGraph agent durable memory in TypeScript using checkpointing, so conversations can survive across turns and process restarts. You need this when the agent must remember prior user inputs, maintain workflow state, or continue a multi-step task without re-asking for everything.
What You'll Need
- •Node.js 18+
- •TypeScript 5+
- •
@langchain/langgraph - •
@langchain/openai - •
dotenv - •An OpenAI API key in
OPENAI_API_KEY - •A LangGraph-compatible checkpointer; for local development, use the in-memory saver
- •A project configured for ESM or TypeScript transpilation
Step-by-Step
- •Start with a graph state that can hold messages.
For memory, the key detail is that your state must be serializable and include the chat history you want persisted between runs.
import "dotenv/config";
import { Annotation, MessagesAnnotation, StateGraph, START, END } from "@langchain/langgraph";
import { ChatOpenAI } from "@langchain/openai";
import { AIMessage } from "@langchain/core/messages";
const llm = new ChatOpenAI({
model: "gpt-4o-mini",
temperature: 0,
});
const GraphState = Annotation.Root({
messages: MessagesAnnotation,
});
type GraphStateType = typeof GraphState.State;
- •Build a node that reads the current messages and appends the model response.
The important part is returning only the new message; LangGraph merges it into the persisted state for you.
async function assistantNode(state: GraphStateType) {
const response = await llm.invoke(state.messages);
return {
messages: [new AIMessage(response.content as string)],
};
}
- •Wire the node into a graph and compile it with a checkpointer.
Without a checkpointer, each invocation starts fresh. With one, LangGraph stores the thread state keyed bythread_id.
import { MemorySaver } from "@langchain/langgraph";
const graph = new StateGraph(GraphState)
.addNode("assistant", assistantNode)
.addEdge(START, "assistant")
.addEdge("assistant", END);
const checkpointer = new MemorySaver();
const app = graph.compile({ checkpointer });
- •Invoke the graph with a stable thread ID.
This is what turns a stateless call into a memory-backed conversation. Use the samethread_idfor every turn you want to belong to the same session.
async function main() {
const config = {
configurable: {
thread_id: "customer-123",
},
};
const firstTurn = await app.invoke(
{ messages: [{ role: "user", content: "My policy number is POL-7781." }] },
config
);
console.log("First turn:", firstTurn.messages.at(-1));
const secondTurn = await app.invoke(
{ messages: [{ role: "user", content: "What policy number did I give you?" }] },
config
);
console.log("Second turn:", secondTurn.messages.at(-1));
}
main();
- •If you want multiple independent conversations, use different thread IDs.
That gives you per-user or per-case isolation, which is what you want in production systems handling separate customers.
async function runSeparateThreads() {
const aliceConfig = { configurable: { thread_id: "alice-session" } };
const bobConfig = { configurable: { thread_id: "bob-session" } };
await app.invoke(
{ messages: [{ role: "user", content: "Remember that my deductible is $500." }] },
aliceConfig
);
await app.invoke(
{ messages: [{ role: "user", content: "Remember that my deductible is $1000." }] },
bobConfig
);
}
Testing It
Run the script twice with the same thread_id and confirm the second call can answer using prior context. Then change only the thread_id and verify the agent no longer sees earlier conversation state. If you want to inspect persistence more directly, log the full returned state after each invoke and compare how messages grows over time.
For production-style testing, restart the process between calls and confirm memory still works when using a persistent checkpointer instead of MemorySaver. That is the difference between session memory and actual durable memory.
Next Steps
- •Swap
MemorySaverfor a persistent store like Postgres-backed checkpointing - •Add structured state fields alongside
messages, such ascustomerId,caseStatus, orriskFlags - •Learn how to branch graphs with conditional edges so memory drives workflow decisions
Keep learning
- •The complete AI Agents Roadmap — my full 8-step breakdown
- •Free: The AI Agent Starter Kit — PDF checklist + starter code
- •Work with me — I build AI for banks and insurance companies
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