LangGraph Tutorial (TypeScript): building a RAG pipeline for intermediate developers
This tutorial builds a working Retrieval-Augmented Generation (RAG) pipeline in LangGraph using TypeScript. You’ll wire together document retrieval, context assembly, and answer generation in a graph that is easy to extend for production use.
What You'll Need
- •Node.js 18+ installed
- •A TypeScript project with
ts-nodeortsx - •Packages:
- •
langgraph - •
@langchain/openai - •
@langchain/core - •
@langchain/community - •
zod
- •
- •An OpenAI API key in
OPENAI_API_KEY - •Basic familiarity with LangGraph nodes, edges, and state
- •A few sample documents to retrieve from
Step-by-Step
- •Start by installing dependencies and setting up a minimal TypeScript project. This tutorial uses OpenAI embeddings plus an in-memory vector store so you can run it locally without extra infrastructure.
npm init -y
npm install langgraph @langchain/openai @langchain/core @langchain/community zod
npm install -D typescript tsx @types/node
- •Define the graph state and create a small document index. The state carries the user question, retrieved documents, and final answer through the pipeline.
import { ChatOpenAI, OpenAIEmbeddings } from "@langchain/openai";
import { MemoryVectorStore } from "@langchain/community/vectorstores/memory";
import { Document } from "@langchain/core/documents";
import { Annotation, END, START, StateGraph } from "langgraph";
const State = Annotation.Root({
question: Annotation<string>(),
docs: Annotation<Document[]>(),
answer: Annotation<string>(),
});
const embeddings = new OpenAIEmbeddings({ model: "text-embedding-3-small" });
const docs = [
new Document({
pageContent: "LangGraph is useful for building stateful AI workflows.",
metadata: { source: "notes-1" },
}),
new Document({
pageContent: "RAG combines retrieval with generation to ground answers in documents.",
metadata: { source: "notes-2" },
}),
];
- •Build the retriever node and the answer-generation node. The retriever pulls the most relevant chunks, then the generator turns them into a grounded response.
const vectorStore = await MemoryVectorStore.fromDocuments(docs, embeddings);
const retriever = vectorStore.asRetriever(2);
const llm = new ChatOpenAI({ model: "gpt-4o-mini", temperature: 0 });
async function retrieve(state: typeof State.State) {
const docs = await retriever.invoke(state.question);
return { docs };
}
async function generate(state: typeof State.State) {
const context = state.docs.map((d) => d.pageContent).join("\n\n");
const prompt = [
"Answer the question using only the context below.",
`Question: ${state.question}`,
`Context:\n${context}`,
"If the context is insufficient, say you do not know.",
].join("\n\n");
const response = await llm.invoke(prompt);
return { answer: response.content.toString() };
}
- •Wire the nodes into a LangGraph workflow. This graph starts at retrieval, passes results into generation, then ends with the final answer.
const graph = new StateGraph(State)
.addNode("retrieve", retrieve)
.addNode("generate", generate)
.addEdge(START, "retrieve")
.addEdge("retrieve", "generate")
.addEdge("generate", END);
const app = graph.compile();
- •Run the pipeline with a real question. This is where you verify that retrieval happens first and generation only sees the retrieved context.
const result = await app.invoke({
question: "What is RAG?",
});
console.log("Answer:", result.answer);
console.log(
"Sources:",
result.docs.map((d) => d.metadata.source).join(", ")
);
Testing It
Run the file with npx tsx your-file.ts. If everything is wired correctly, you should see an answer grounded in the provided documents plus a list of sources returned by retrieval.
Try questions that are clearly covered by your sample docs and one that is not. For unsupported questions, the model should say it does not know instead of inventing an answer.
If retrieval returns irrelevant chunks, check your document content and confirm your embedding model matches your vector store setup. In production, this is where chunking strategy and metadata filters start to matter.
Next Steps
- •Add a real document loader such as PDF or HTML ingestion before indexing
- •Replace
MemoryVectorStorewith Pinecone, pgvector, or Weaviate for persistence - •Add a grading node to validate whether retrieved context is sufficient before generation
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