LangGraph Tutorial (TypeScript): building conditional routing for beginners
This tutorial shows how to build a LangGraph workflow in TypeScript that routes requests conditionally based on simple runtime logic. You need this when one agent flow is not enough and you want different paths for different inputs, like routing urgent cases to one node and normal cases to another.
What You'll Need
- •Node.js 18+
- •TypeScript 5+
- •
@langchain/langgraph - •
@langchain/core - •A package manager like npm, pnpm, or yarn
- •No API key required for this example
- •Basic familiarity with async/await and TypeScript interfaces
Step-by-Step
- •Start by installing the packages and setting up a TypeScript project. This example uses only local graph logic, so there is no model call yet.
npm init -y
npm install @langchain/langgraph @langchain/core
npm install -D typescript tsx @types/node
npx tsc --init
- •Define your state shape and create the nodes. The graph will inspect the input text and decide whether to send it to a priority path or a normal path.
import { Annotation, END, START, StateGraph } from "@langchain/langgraph";
const GraphState = Annotation.Root({
input: Annotation<string>(),
route: Annotation<string>(),
output: Annotation<string>(),
});
type GraphStateType = typeof GraphState.State;
function classifyUrgency(state: GraphStateType): Partial<GraphStateType> {
const text = state.input.toLowerCase();
const route = text.includes("urgent") || text.includes("asap") ? "priority" : "normal";
return { route };
}
function priorityHandler(_: GraphStateType): Partial<GraphStateType> {
return { output: "Priority queue: escalate to human review." };
}
function normalHandler(_: GraphStateType): Partial<GraphStateType> {
return { output: "Normal queue: continue standard processing." };
}
- •Add the conditional edge function. This is the key part of the tutorial: LangGraph reads the state after classification and picks the next node based on the returned route value.
function routeByUrgency(state: GraphStateType) {
if (state.route === "priority") return "priorityHandler";
return "normalHandler";
}
const graph = new StateGraph(GraphState)
.addNode("classifyUrgency", classifyUrgency)
.addNode("priorityHandler", priorityHandler)
.addNode("normalHandler", normalHandler)
.addEdge(START, "classifyUrgency")
.addConditionalEdges("classifyUrgency", routeByUrgency, {
priorityHandler: "priorityHandler",
normalHandler: "normalHandler",
})
.addEdge("priorityHandler", END)
.addEdge("normalHandler", END);
- •Compile the graph and run it with two different inputs. You should see different outputs depending on whether the message contains urgent language.
const app = graph.compile();
async function main() {
const urgentResult = await app.invoke({ input: "This is urgent, please help ASAP." });
const normalResult = await app.invoke({ input: "Please review my account statement." });
console.log("Urgent:", urgentResult.output);
console.log("Normal:", normalResult.output);
}
main().catch(console.error);
- •Put everything into one file and run it with
tsx. This keeps the example easy to test without extra build steps.
// src/index.ts
import { Annotation, END, START, StateGraph } from "@langchain/langgraph";
const GraphState = Annotation.Root({
input: Annotation<string>(),
route: Annotation<string>(),
output: Annotation<string>(),
});
type GraphStateType = typeof GraphState.State;
function classifyUrgency(state: GraphStateType): Partial<GraphStateType> {
const text = state.input.toLowerCase();
const route = text.includes("urgent") || text.includes("asap") ? "priority" : "normal";
return { route };
}
function priorityHandler(_: GraphStateType): Partial<GraphStateType> {
return { output: "Priority queue: escalate to human review." };
}
function normalHandler(_: GraphStateType): Partial<GraphStateType> {
return { output: "Normal queue: continue standard processing." };
}
function routeByUrgency(state: GraphStateType) {
if (state.route === "priority") return "priorityHandler";
return "normalHandler";
}
const graph = new StateGraph(GraphState)
.addNode("classifyUrgency", classifyUrgency)
.addNode("priorityHandler", priorityHandler)
.addNode("normalHandler", normalHandler)
.addEdge(START, "classifyUrgency")
.addConditionalEdges("classifyUrgency", routeByUrgency, {
priorityHandler: "priorityHandler",
normalHandler: "normalHandler",
})
.addEdge("priorityHandler", END)
.addEdge("normalHandler", END);
const app = graph.compile();
async function main() {
const result = await app.invoke({ input: process.argv[2] ?? "Hello there" });
console.log(result.output);
}
main().catch(console.error);
Testing It
Run the file with an urgent prompt and confirm it routes to the priority handler. Then run it again with a neutral prompt and confirm it routes to the normal handler.
npx tsx src/index.ts "urgent payment issue"
npx tsx src/index.ts "general question about my policy"
If both paths print different outputs, your conditional routing is working correctly. If not, check that your routeByUrgency function returns keys that match the mapping in addConditionalEdges.
Next Steps
- •Replace the keyword router with an LLM-based classifier using
ChatOpenAI - •Add more branches for categories like billing, fraud, and onboarding
- •Pass structured state through nodes so each branch can enrich the same case record
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