Skip to main content
Madhukar
All Engineering Articles
Language:
AI & GenAI•12 min read•Sep 30, 2026•By Mr Madhukar

LangChain vs. LangGraph: A Practical JavaScript Developer’s Guide

The Challenge of Building LLM-Powered Applications Calling a model provider’s API directly — openai.chat.completions.create(...) — works fine for a single question and a single ans...

Cover illustration for LangChain vs. LangGraph: A Practical JavaScript Developer’s Guide
Share Article

The Challenge of Building LLM-Powered Applications

Calling a model provider’s API directly — openai.chat.completions.create(...) — works fine for a single question and a single answer. The moment an application needs more than that — reusable prompt templates, retrieving relevant documents before answering, calling external tools, remembering earlier steps in a multi-turn task, or branching based on what the model decided — that raw API call stops being enough, and developers end up rebuilding the same scaffolding: prompt formatting, retries, state tracking, conditional logic.

LangChain and LangGraph, both available as JavaScript/TypeScript libraries built by LangChain Inc., exist specifically to remove that repeated scaffolding — as two related, complementary frameworks addressing two different layers of the same problem.

LangChain Fundamentals

LangChain provides high-level, composable building blocks for LLM applications: models, prompts, tools, retrieval, and agents.

Models

A consistent interface across model providers (OpenAI, Anthropic, Google, and others), so switching providers means changing an import and a config object, not rewriting application logic.

import { ChatAnthropic } from "@langchain/anthropic";

const model = new ChatAnthropic({ model: "claude-sonnet-4-5", temperature: 0 });
const response = await model.invoke("Explain event loops in one sentence.");

Prompts

Prompt templates let you define reusable, parameterized prompts instead of hardcoding strings everywhere:

import { ChatPromptTemplate } from "@langchain/core/prompts";

const prompt = ChatPromptTemplate.fromMessages([
["system", "You are a {role} expert."],
["human", "{question}"],
]);

const chain = prompt.pipe(model);
const response = await chain.invoke({ role: "Node.js", question: "What is the event loop?" });

Tools

Tools let a model request that a specific function be called — the same tool-calling concept covered conceptually in this series’s LLM vs JEV article:

import { tool } from "@langchain/core/tools";
import { z } from "zod";

const getWeather = tool(
async ({ city }) => `Sunny in ${city}`,
{ name: "getWeather", description: "Get the weather for a city", schema: z.object({ city: z.string() }) }
);

Retrieval

Retrieval-augmented generation (RAG) lets a model answer using your own documents: loading content, splitting it into chunks, embedding those chunks, storing them in a vector store, and retrieving the most relevant ones for a given question before generating an answer.

Agents

LangChain also provides prebuilt agent abstractions — components that let a model decide, in a loop, which tools to call and when. Worth stating clearly: modern LangChain agents are built on top of LangGraph underneath — they aren’t a separate, competing execution model.

LangGraph Fundamentals

LangGraph provides explicit, low-level control over stateful, multi-step workflows — built around three core ideas: state, nodes, and edges.

State

A shared, typed data structure passed between every step of the workflow, with each node able to read and update it.

import { Annotation } from "@langchain/langgraph";

const StateAnnotation = Annotation.Root({
messages: Annotation(),
step: Annotation(),
});

Nodes

Individual processing steps — plain functions that receive the current state and return updates to it.

function agentNode(state) {
// run the model, decide what to do next
return { step: "decided" };
}

Edges

Connections defining execution order — including conditional edges, which route execution differently depending on the current state.

import { StateGraph, START, END } from "@langchain/langgraph";

const graph = new StateGraph(StateAnnotation)
.addNode("agent", agentNode)
.addNode("tool", toolNode)
.addEdge(START, "agent")
.addConditionalEdges("agent", (state) => (state.step === "needs_tool" ? "tool" : END))
.addEdge("tool", "agent")
.compile();

const result = await graph.invoke({ messages: [], step: "start" });

Deterministic vs. Model-Driven Branching

Not every branching decision needs to be made by a model. If a workflow has a fixed requirement — for example, every request must retrieve documents before an answer is generated — that routing can remain deterministic in the graph or application code.

The model becomes useful when the next step depends on the content or meaning of what was retrieved. For example, after retrieving documents, a model might determine whether the evidence is sufficient to answer, whether more information is needed, or which narrower workflow should handle the request.

A practical rule is: put predictable workflow requirements in code, and use model judgment where the decision genuinely depends on unstructured or semantic information.

Graph execution

Unlike a straightforward, mostly linear LangChain pipeline, a LangGraph workflow can cycle — looping an agent back through a decision repeatedly — which is exactly the pattern most genuine agentic behavior needs: call the model, check if it wants a tool, run the tool, hand the result back, repeat until it’s actually done.

LangChain vs. LangGraph: Architecture Comparison

Important clarification, stated directly: this comparison doesn’t mean LangChain “can’t” support agents or branching logic — its prebuilt agents genuinely handle tool-calling loops. LangGraph’s distinction is giving you explicit, fine-grained control over that same execution, which matters once a workflow’s logic grows complex enough that you need to see and control every step yourself.

How LangChain and LangGraph Work Together

In practice, LangGraph nodes very commonly use LangChain components internally — a node might call a LangChain-wrapped model, or run a LangChain retrieval step — while LangGraph handles the surrounding orchestration: what runs next, what state is shared, and whether execution can pause and resume. They aren’t competing choices; they’re two layers of the same stack.

Building AI Applications with JavaScript and Node.js: Practical Examples

Example 1: Basic LangChain Chatbot

npm install @langchain/anthropic @langchain/core
import { ChatAnthropic } from "@langchain/anthropic";
import { ChatPromptTemplate } from "@langchain/core/prompts";

const model = new ChatAnthropic({ model: "claude-sonnet-4-5" });
const prompt = ChatPromptTemplate.fromMessages([
["system", "You are a helpful assistant."],
["human", "{input}"],
]);

const chain = prompt.pipe(model);
const response = await chain.invoke({ input: "What does npm stand for?" });
console.log(response.content);

Example 2: Document Q&A with LangChain (RAG)

import { MemoryVectorStore } from "langchain/vectorstores/memory";
import { OpenAIEmbeddings } from "@langchain/openai";

const vectorStore = await MemoryVectorStore.fromDocuments(chunks, new OpenAIEmbeddings());
const relevantDocs = await vectorStore.similaritySearch("How does the checkpointer work?", 3);

const context = relevantDocs.map(d => d.pageContent).join("\n");
const answer = await chain.invoke({ input: `Context: ${context}\n\nQuestion: How does the checkpointer work?` });

Example 3: Stateful LangGraph Workflow

import { StateGraph, Annotation, START, END } from "@langchain/langgraph";

const State = Annotation.Root({ count: Annotation() });

const increment = (state) => ({ count: state.count + 1 });

const graph = new StateGraph(State)
.addNode("increment", increment)
.addEdge(START, "increment")
.addEdge("increment", END)
.compile();

const result = await graph.invoke({ count: 0 }); // { count: 1 }

Example 4: Tool-Calling AI Agent

import { StateGraph, Annotation, START, END } from "@langchain/langgraph";

const model = new ChatAnthropic({ model: "claude-sonnet-4-5" }).bindTools([getWeather]);

async function agentNode(state) {
const response = await model.invoke(state.messages);
return { messages: [...state.messages, response] };
}

function shouldCallTool(state) {
const last = state.messages[state.messages.length - 1];
return last.tool_calls?.length ? "tool" : END;
}

const graph = new StateGraph(Annotation.Root({ messages: Annotation() }))
.addNode("agent", agentNode)
.addNode("tool", toolNode) // runs the requested tool and appends its result
.addEdge(START, "agent")
.addConditionalEdges("agent", shouldCallTool)
.addEdge("tool", "agent")
.compile();

Example 5: Human-in-the-Loop Agent

import { MemorySaver, interrupt } from "@langchain/langgraph";

const checkpointer = new MemorySaver();

function approvalNode(state) {
const approved = interrupt({ question: "Approve this action?", action: state.pendingAction });
return { approved };
}

const graph = new StateGraph(State)
.addNode("approval", approvalNode)
// ...other nodes
.compile({ checkpointer });

// Execution pauses at the interrupt; resume later with the human's decision

interrupt() pauses graph execution at that exact point, persisting state via the configured checkpointer — execution resumes later, from precisely where it paused, once a human decision is supplied.

Full-Stack Integration: Express.js, React, and External APIs

A typical production shape: the frontend (React/Next.js) sends a user’s message to an Express.js endpoint; that endpoint invokes a LangGraph workflow, which internally uses LangChain components for the actual model calls and retrieval; results (and, where relevant, streamed tokens) flow back to the frontend, while checkpoint and application data persist separately.

// Express route exposing the workflow
app.post("/api/chat", async (req, res) => {
const result = await graph.invoke({ messages: [new HumanMessage(req.body.message)] });
res.json({ reply: result.messages.at(-1).content });
});

Real-World Examples

  • AI Customer Support Chatbot — LangChain handles model and tool integration; LangGraph manages the conversation’s persistent, multi-turn state
  • AI Research Assistant — LangChain retrieval and search tools, orchestrated through LangGraph’s conditional execution based on what’s actually been found so far
  • AI Travel Planning Assistant — a multi-step workflow (search flights → check dates → confirm budget → book) naturally modeled as a LangGraph graph, calling external APIs via LangChain tools at each step
  • GitHub Issue Assistant — classifies an incoming issue, routes it through different handling paths, and pauses for human approval before taking a sensitive action (like closing or labeling) — a direct application of the human-in-the-loop pattern above

Important Comparisons

  • LangChain vs. LangGraph — high-level application components versus low-level, explicit stateful orchestration
  • Chain vs. Graph — pipeline-style execution versus explicit graph-based execution with cycles and branches
  • LangChain Agent vs. Custom LangGraph — a prebuilt agent abstraction versus a fully customized workflow you design node by node
  • Node vs. Edge — a processing step versus the connection defining what runs next
  • State vs. Memory — the current workflow’s data at this point in execution, versus retained conversational or application context carried across separate interactions
  • Conditional Routing vs. Fixed Execution — paths chosen based on current state, versus a single, predetermined sequence
  • Checkpointing vs. Database Storage — snapshots of workflow execution state for pausing/resuming, versus general-purpose application data storage

Developer Connection

  • JavaScript/TypeScript — build AI applications using familiar tooling and type safety
  • Node.js — execute workflows and integrate model provider SDKs
  • Express.js — expose chatbot and agent functionality as REST endpoints
  • React/Next.js — build interactive interfaces, including streamed token responses
  • Databases — manage application data and conversation records
  • Vector Stores — enable retrieval and semantic search for RAG
  • System Design — deliberately separate frontend, backend, orchestration, and model layers

Production Considerations

  • Secure model API keys and external service credentials via environment variables, never hardcoded
  • Add authentication and authorization to any exposed AI endpoint
  • Use persistent checkpointing (a real database-backed checkpointer, not just in-memory) for genuinely resumable workflows in production
  • Handle retries, timeouts, and tool execution failures explicitly — external calls fail, and workflows need to handle that gracefully
  • Monitor latency, token usage, and model costs, which can scale unpredictably with usage
  • Add tracing and logging across workflow steps for debugging multi-step execution
  • Apply rate limiting and request validation on any public-facing AI endpoint
  • Deploy Node.js services via containers or cloud platforms, as covered in this series’s deployment-adjacent articles
  • Scale stateless API workers independently from persistent workflow state, which typically lives in a shared store
  • Add human approval checkpoints before any sensitive external action, exactly as shown in Example 5

Common Mistakes and Misconceptions

  • LangChain and LangGraph are not competing language models — both are frameworks for building LLM-powered applications
  • LangGraph does not completely replace LangChain — they work together, and LangChain’s own agents are built on LangGraph
  • LangChain is not limited to linear chains only — modern LangChain also provides agents with genuine tool-calling capabilities
  • Not every chatbot requires LangGraph — simple applications may only need direct model calls or basic LangChain components
  • LangGraph is not exclusively for multi-agent systems — it supports single-agent and general stateful workflows just as well
  • Graph state is not automatically persistent — durable persistence requires a properly configured checkpointer and storage backend
  • Not every workflow needs an autonomous agent — deterministic, explicitly-defined workflows handle many predictable tasks perfectly well
  • Adding tools does not automatically make an agent production-ready — security, validation, monitoring, and failure handling all remain necessary

Conclusion

LangChain and LangGraph address two related but distinct layers of building LLM-powered applications: LangChain supplies the high-level components — models, prompts, tools, retrieval — while LangGraph supplies explicit, stateful orchestration for anything more complex than a straightforward pipeline. They’re built to complement each other, not compete — LangChain’s own agents run on top of LangGraph underneath. For JavaScript developers, the practical choice usually isn’t “one or the other,” but recognizing when a workflow’s branching, state, or persistence needs genuinely call for LangGraph’s explicit control, versus when LangChain’s higher-level components are already enough — a decision worth making deliberately, the same way any architectural choice in this series has been, rather than defaulting to the most powerful tool available.

Frequently Asked Questions

Should I start with LangChain or LangGraph for my first AI project?

> Start with LangChain’s basic model and prompt integration for a simple use case (Example 1 above) — reach for LangGraph once your workflow genuinely needs branching, cycles, shared state across steps, or pause/resume behavior that a straightforward pipeline can’t express cleanly.

Do I need LangGraph if I’m only building a simple Q&A chatbot?

> Probably not — a simple chatbot without multi-step branching or persistent state typically works fine with LangChain’s model and prompt components alone, without the added complexity of explicit graph orchestration.

Can I use LangGraph without LangChain at all?

> Yes — LangGraph is built by the same team but designed to be usable independently. In practice, most JavaScript projects still use LangChain components (models, tools) inside LangGraph nodes, since it avoids reimplementing that integration work yourself.

How do I decide between a LangChain prebuilt agent and a custom LangGraph workflow?

> Reach for a prebuilt LangChain agent when your use case fits its standard tool-calling loop pattern well. Build a custom LangGraph workflow when you need precise control over specific branches, custom state, human-in-the-loop approval steps, or execution patterns a prebuilt agent doesn’t directly support.

LangChain vs. LangGraph: A Practical JavaScript Developer’s Guide was originally published in Towards Dev on Medium, where people are continuing the conversation by highlighting and responding to this story.

Share Article

Originally published by Mr Madhukar

Read the complete article on Medium with full formatting & reader responses.