Skip to Content
getting-startedCore Concepts

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: int

JavaScript - 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: str

JavaScript:

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 point

JavaScript:

import { START, END } from "@langchain/langgraph"; builder .addEdge(START, "nodeA") // Entry point .addEdge("nodeA", "nodeB") // Always go to nodeB .addEdge("nodeB", END); // Exit point

Conditional 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) * 2

JavaScript:

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) * 2

Key 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