Validation
Define JSON Schema rules on HTTP nodes to validate data at runtime. The TypeScript SDK accepts Zod schemas and converts them to JSON Schema automatically.
How validation works
Each HTTP node can define an optional validation object with two fields:
outputvalidates the response body (result.data) after the node executes.inputvalidates the accumulatedresultsobject from prior nodes before the node executes.
Both fields accept a JSON Schema object. The TypeScript SDK lets you pass Zod schemas instead, and converts them to JSON Schema at build time using zod-to-json-schema.
Validation is optional. If you omit the validation field, the node runs without any schema checks.
This page covers runtime data validation (input and output schemas on nodes). For pre-submission structural validation (dependencies, cycles, node IDs, expressions), see Graph Validation.
Output validation
Output validation checks the response body returned by the node's HTTP call. The schema is validated against result.data, not the full result envelope ({ data, statusCode, headers }).
import { z } from 'zod'
import { GraphCompose } from '@graph-compose/client'
const graph = new GraphCompose({
token: process.env.GRAPH_COMPOSE_TOKEN
})
const UserSchema = z.object({
id: z.string(),
email: z.string().email(),
age: z.number().min(0)
})
graph
.node('get_user')
.get('https://api.example.com/users/{{ context.userId }}')
.withValidation({
output: UserSchema
})
.end()
If get_user returns { id: "u_1", email: "alice@example.com", age: 30 }, the schema passes and the workflow continues. If the response is missing email or age is a string, the node fails with a schema validation error.
Input validation
Input validation checks the accumulated results from prior nodes before the current node executes. The schema is validated against the results object, which has the shape { nodeId: { data, statusCode, headers }, ... }.
This lets you verify that upstream nodes produced the data your node expects.
import { z } from 'zod'
import { GraphCompose } from '@graph-compose/client'
const graph = new GraphCompose({
token: process.env.GRAPH_COMPOSE_TOKEN
})
graph
.node('get_user')
.get('https://api.example.com/users/{{ context.userId }}')
.end()
.node('create_order')
.post('https://api.example.com/orders')
.withDependencies(['get_user'])
.withBody({
userId: '{{ results.get_user.data.id }}',
email: '{{ results.get_user.data.email }}'
})
.withValidation({
input: z.object({
get_user: z.object({
data: z.object({
id: z.string(),
email: z.string()
})
})
})
})
.end()
Before create_order runs, Graph Compose validates that results.get_user.data contains a string id and a string email. If the upstream node returned a different shape, create_order fails immediately without making its HTTP call.
The input schema validates against the full results object, not just the direct dependency. The schema shape mirrors what you access in template expressions: results.nodeId.data.field.
What happens when validation fails
Schema validation failures (input or output) at runtime are non-retryable. The node fails immediately without retrying, even if a retry policy is configured.
If the failed node is protected by an error boundary, the boundary fires. If not, the workflow fails and downstream nodes are skipped.
| Validation type | When it runs | What it checks | Retryable |
|---|---|---|---|
| Output schema | After the node's HTTP call completes | result.data against the output schema | No |
| Input schema | Before the node's HTTP call runs | results object against the input schema | No |
Best practices
- Start with output validation on nodes whose response shape matters to downstream nodes. If a payment API changes its response format, output validation catches it before the next node tries to use a missing field.
- Use input validation when a node depends on specific fields from an upstream response. This fails the node early with a clear schema error instead of letting it make an HTTP call with missing data.
- Keep schemas focused on the fields you actually use. Avoid validating the entire response body if you only need two fields from it.
- Combine validation with error boundaries to handle validation failures gracefully. Protect nodes with validation schemas so that a schema mismatch triggers your cleanup endpoint instead of failing the workflow.