Last Updated: 3/11/2026
Core Concepts
Understand the foundational concepts that power LangGraph: state, nodes, edges, and graphs.
Graphs
A graph in LangGraph models your agent workflow as a directed graph where:
- Nodes are functions that do work
- Edges define the flow between nodes
- State is shared data that flows through the graph
LangGraph uses a message-passing algorithm inspired by Google’s Pregel. When a node completes, it sends messages (state updates) along edges to other nodes. The graph proceeds in discrete “super-steps” where nodes run in parallel or sequence.
State
State is the shared data structure that flows through your graph. Every node reads from and writes to the state.
Defining State
Python - Use TypedDict:
from typing_extensions import TypedDict
class State(TypedDict):
messages: list[str]
count: intJavaScript - Use StateSchema:
import { StateSchema } from "@langchain/langgraph";
import { z } from "zod";
const State = new StateSchema({
messages: z.array(z.string()),
count: z.number(),
});Reducers
Reducers control how state updates are applied. By default, updates overwrite the previous value. Use reducers to accumulate values instead.
Python - Use Annotated with operator.add:
from typing import Annotated
import operator
class State(TypedDict):
# Default: overwrites
count: int
# Reducer: appends to list
messages: Annotated[list[str], operator.add]JavaScript - Use ReducedValue:
import { StateSchema, ReducedValue } from "@langchain/langgraph";
import { z } from "zod";
const State = new StateSchema({
count: z.number(), // Default: overwrites
messages: new ReducedValue(
z.array(z.string()).default(() => []),
{ reducer: (x, y) => x.concat(y) } // Appends to list
),
});Working with Messages
LangGraph provides built-in support for chat message lists:
Python:
from langgraph.graph import MessagesState
class State(MessagesState):
# Inherits: messages: Annotated[list[AnyMessage], add_messages]
custom_field: strJavaScript:
import { StateSchema, MessagesValue } from "@langchain/langgraph";
import { z } from "zod";
const State = new StateSchema({
messages: MessagesValue, // Built-in message reducer
customField: z.string(),
});The MessagesValue / add_messages reducer:
- Appends new messages to the list
- Updates existing messages by ID
- Handles message deletions
Nodes
Nodes are functions that read state, perform work, and return state updates.
Node Function Signature
Python:
def my_node(state: State) -> dict:
# Read from state
current_count = state["count"]
# Do work
new_count = current_count + 1
# Return updates (partial state)
return {"count": new_count}JavaScript:
const myNode = (state: typeof State.State) => {
// Read from state
const currentCount = state.count;
// Do work
const newCount = currentCount + 1;
// Return updates (partial state)
return { count: newCount };
};Adding Nodes
Python:
from langgraph.graph import StateGraph
builder = StateGraph(State)
builder.add_node("my_node", my_node)JavaScript:
import { StateGraph } from "@langchain/langgraph";
const builder = new StateGraph(State)
.addNode("myNode", myNode);Special Nodes
- START: Virtual node representing graph entry
- END: Virtual node representing graph completion
Edges
Edges define how the graph flows from one node to another.
Normal Edges
Static connections that always execute:
Python:
from langgraph.graph import START, END
builder.add_edge(START, "node_a") # Entry point
builder.add_edge("node_a", "node_b") # Always go to node_b
builder.add_edge("node_b", END) # Exit pointJavaScript:
import { START, END } from "@langchain/langgraph";
builder
.addEdge(START, "nodeA") // Entry point
.addEdge("nodeA", "nodeB") // Always go to nodeB
.addEdge("nodeB", END); // Exit pointConditional Edges
Dynamic routing based on state:
Python:
from typing import Literal
def route_logic(state: State) -> Literal["node_b", "node_c"]:
if state["count"] > 10:
return "node_b"
return "node_c"
builder.add_conditional_edges(
"node_a",
route_logic,
["node_b", "node_c"]
)JavaScript:
const routeLogic = (state: typeof State.State): "nodeB" | "nodeC" => {
if (state.count > 10) {
return "nodeB";
}
return "nodeC";
};
builder.addConditionalEdges("nodeA", routeLogic);Compiling and Running
Compile the Graph
Python:
app = builder.compile()JavaScript:
const app = builder.compile();Compilation validates the graph structure and prepares it for execution.
Run the Graph
Python:
# Synchronous
result = app.invoke({"count": 0, "messages": []})
# Streaming
for chunk in app.stream({"count": 0, "messages": []}):
print(chunk)JavaScript:
// Invoke
const result = await app.invoke({ count: 0, messages: [] });
// Streaming
for await (const chunk of await app.stream({ count: 0, messages: [] })) {
console.log(chunk);
}Putting It Together
Here’s a complete example:
Python:
from langgraph.graph import StateGraph, START, END
from typing_extensions import TypedDict
class State(TypedDict):
value: int
def add_one(state: State) -> dict:
return {"value": state["value"] + 1}
def multiply_two(state: State) -> dict:
return {"value": state["value"] * 2}
builder = StateGraph(State)
builder.add_node("add_one", add_one)
builder.add_node("multiply_two", multiply_two)
builder.add_edge(START, "add_one")
builder.add_edge("add_one", "multiply_two")
builder.add_edge("multiply_two", END)
app = builder.compile()
result = app.invoke({"value": 5})
print(result) # {"value": 12} -> (5 + 1) * 2JavaScript:
import { StateGraph, StateSchema, START, END } from "@langchain/langgraph";
import { z } from "zod";
const State = new StateSchema({ value: z.number() });
const app = new StateGraph(State)
.addNode("addOne", (state) => ({ value: state.value + 1 }))
.addNode("multiplyTwo", (state) => ({ value: state.value * 2 }))
.addEdge(START, "addOne")
.addEdge("addOne", "multiplyTwo")
.addEdge("multiplyTwo", END)
.compile();
const result = await app.invoke({ value: 5 });
console.log(result); // { value: 12 } -> (5 + 1) * 2Key Takeaways
- State: Shared data that flows through the graph
- Nodes: Functions that read state, do work, and return updates
- Edges: Define the flow (static or conditional)
- Reducers: Control how state updates are applied (overwrite vs accumulate)
- Compile: Validate and prepare the graph for execution
Next Steps
- Building Graphs: Learn advanced patterns and state management
- Persistence & Memory: Add checkpoints and long-term memory
- Streaming: Stream real-time updates to users
- Human-in-the-Loop: Add approval workflows