Command: update and route in one step
A Command lets a node do two things at once: change the state and choose the next node. Return Command(update={...}, goto="...") instead of a plain dict.
Last updated: 27 Sep, 2026 · LangGraph 1.2
With a conditional edge, one function decides the state and a separate function decides the route. When the node already knows both, a Command carries them together and you skip the router.
The Command return
from langgraph.types import Command
from typing import Literal
def node_a(state) -> Command[Literal["node_b"]]:
return Command(update={"key": "value"}, goto="node_b")The state
Start with the imports and a one-key state. Command comes from langgraph.types.
from typing import Literal
from typing_extensions import TypedDict
from langgraph.types import Command
from langgraph.graph import StateGraph, START, END
class State(TypedDict):
foo: strThe Command node
Here is the new idea. node_a returns a Command instead of a plain dict: update changes the state and goto names the next node. The Command[Literal["node_b"]] annotation tells LangGraph where this node can go.
def node_a(state) -> Command[Literal["node_b"]]:
# update the state AND choose the next node in one return
return Command(update={"foo": "bar"}, goto="node_b")Wiring and running
Wire the graph. There is no router after node_a, because its goto already carries the route. node_b reads the updated foo and adds an exclamation mark.
builder = StateGraph(State)
builder.add_node("node_a", node_a)
builder.add_node("node_b", lambda s: {"foo": s["foo"] + "!"}) # reads foo, adds "!"
builder.add_edge(START, "node_a") # no conditional edge after node_a
builder.add_edge("node_b", END)
print(builder.compile().invoke({"foo": ""}))Update and route in one node
The same pieces in one file, ready to run.
from typing import Literal
from typing_extensions import TypedDict
from langgraph.types import Command
from langgraph.graph import StateGraph, START, END
class State(TypedDict):
foo: str
def node_a(state) -> Command[Literal["node_b"]]:
return Command(update={"foo": "bar"}, goto="node_b")
builder = StateGraph(State)
builder.add_node("node_a", node_a)
builder.add_node("node_b", lambda s: {"foo": s["foo"] + "!"})
builder.add_edge(START, "node_a")
builder.add_edge("node_b", END)
print(builder.compile().invoke({"foo": ""}))What node_a did in one return
node_asetfooto "bar" and sent the run tonode_bin one return, with no conditional edge after it.node_bread the updatedfooand added "!", giving "bar!".- The
Command[Literal["node_b"]]annotation tells LangGraph where this node can go, which also lets it draw the graph.
Command vs a conditional edge
| Conditional edge | Command | |
|---|---|---|
| Who decides the route | A separate router function | The node itself |
| Who updates the state | The node | The same node, in the Command |
| Reach for it when | Routing logic is shared or complex | The node already knows the update and the next step |
When a node knows both
- A classifier that both records the category and jumps to the matching handler.
- Any node whose result also decides where the run goes next.
goto must name a real node or END. Import Command from langgraph.types, not langgraph.graph.Related
- Previous: Parallel nodes: fan-out with edges
- Next: Loops and the recursion limit
- Reference: Graph API: Command
- Add a
node_cand makenode_achoose betweennode_bandnode_cfrom the state. - Return a plain dict from
node_ainstead and wire a normal edge. Same result?
Every expert started right here.