Skip to content

Commit 120ae06

Browse files
authored
Improvements to docs emphasizing the 'schema' property usage (#257)
Signed-off-by: Nathan Rajlich <n@n8.io>
1 parent 49aa624 commit 120ae06

File tree

3 files changed

+55
-25
lines changed

3 files changed

+55
-25
lines changed

.changeset/young-humans-create.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
---

docs/content/docs/api-reference/workflow/define-hook.mdx

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import { generateDefinition } from "@/lib/tsdoc"
88

99
Creates a type-safe hook helper that ensures the payload type is consistent between hook creation and resumption.
1010

11-
This is a lightweight wrapper around [`createHook()`](/docs/api-reference/workflow/create-hook) and [`resumeHook()`](/docs/api-reference/workflow-api/resume-hook) to avoid type mismatches.
11+
This is a lightweight wrapper around [`createHook()`](/docs/api-reference/workflow/create-hook) and [`resumeHook()`](/docs/api-reference/workflow-api/resume-hook) to avoid type mismatches. It also supports optional runtime validation and transformation of payloads using any [Standard Schema v1](https://standardschema.dev) compliant validator like Zod or Valibot.
1212

1313
<Callout>
14-
We recommend using `defineHook()` over `createHook()` in production codebases for better type safety.
14+
We recommend using `defineHook()` over `createHook()` in production codebases for better type safety and optional runtime validation.
1515
</Callout>
1616

1717
```ts lineNumbers
@@ -67,7 +67,7 @@ export default DefineHook;`
6767

6868
## Examples
6969

70-
### Type-Safe Hook Definition
70+
### Basic Type-Safe Hook Definition
7171

7272
By defining the hook once with a specific payload type, you can reuse it in multiple workflows and API routes with automatic type safety.
7373

@@ -117,16 +117,21 @@ export async function POST(request: Request) {
117117

118118
### Validate and Transform with Schema
119119

120-
The optional `schema` accepts any validator that conforms to [Standard Schema v1](https://standardschema.dev).
120+
You can provide runtime validation and transformation of hook payloads using the `schema` option. This option accepts any validator that conforms to the [Standard Schema v1](https://standardschema.dev) specification.
121121

122-
Zod is shown below as one example, but libraries like Valibot, ArkType, Effect Schema, or your own custom validator work as well.
122+
<Callout type="info">
123+
Standard Schema is a standardized specification for schema validation libraries. Most popular validation libraries support it, including Zod, Valibot, ArkType, and Effect Schema. You can also write custom validators.
124+
</Callout>
125+
126+
#### Using Zod with defineHook
127+
128+
Here's an example using [Zod](https://zod.dev) to validate and transform hook payloads:
123129

124130
```typescript lineNumbers
125131
import { defineHook } from "workflow";
126132
import { z } from "zod";
127133

128134
export const approvalHook = defineHook({
129-
// Provide a schema to validate/transform payloads.
130135
schema: z.object({ // [!code highlight]
131136
approved: z.boolean(), // [!code highlight]
132137
comment: z.string().min(1).transform((value) => value.trim()), // [!code highlight]
@@ -140,29 +145,50 @@ export async function approvalWorkflow(approvalId: string) {
140145
token: `approval:${approvalId}`,
141146
});
142147

148+
// Payload is automatically typed based on the schema
143149
const { approved, comment } = await hook;
144150
console.log('Approved:', approved);
145-
console.log('Comment:', comment);
151+
console.log('Comment (trimmed):', comment);
146152
}
147153
```
148154

149-
In your route handler, resume the hook with the same definition; the schema validates and transforms the payload before the workflow continues.
155+
When resuming the hook from an API route, the schema validates and transforms the incoming payload before the workflow resumes:
150156

151157
```typescript lineNumbers
152158
export async function POST(request: Request) {
153-
// comment is " Ready! " here
159+
// Incoming payload: { token: "...", approved: true, comment: " Ready! " }
154160
const { token, approved, comment } = await request.json();
155161

156-
// If validation fails, Zod throws and the hook is not resumed.
162+
// The schema validates and transforms the payload:
163+
// - Checks that `approved` is a boolean
164+
// - Checks that `comment` is a non-empty string
165+
// - Trims whitespace from the comment
166+
// If validation fails, an error is thrown and the hook is not resumed
157167
await approvalHook.resume(token, { // [!code highlight]
158168
approved, // [!code highlight]
159-
comment, // transformed to "Ready!" [!code highlight]
169+
comment, // Automatically trimmed to "Ready!" // [!code highlight]
160170
}); // [!code highlight]
161171

162172
return Response.json({ success: true });
163173
}
164174
```
165175

176+
#### Using Other Standard Schema Libraries
177+
178+
The same pattern works with any Standard Schema v1 compliant library. Here's an example with [Valibot](https://valibot.dev):
179+
180+
```typescript lineNumbers
181+
import { defineHook } from "workflow";
182+
import * as v from "valibot";
183+
184+
export const approvalHook = defineHook({
185+
schema: v.object({ // [!code highlight]
186+
approved: v.boolean(), // [!code highlight]
187+
comment: v.pipe(v.string(), v.minLength(1), v.trim()), // [!code highlight]
188+
}), // [!code highlight]
189+
});
190+
```
191+
166192
### Customizing Tokens
167193

168194
Tokens are used to identify a specific hook and for resuming a hook. You can customize the token to be more specific to a use case.

docs/content/docs/foundations/hooks.mdx

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -362,20 +362,21 @@ async function postToSlack(channelId: string, message: string) {
362362

363363
### Type-Safe Hooks with `defineHook()`
364364

365-
The [`defineHook()`](/docs/api-reference/workflow/define-hook) helper provides type safety between creating and resuming hooks:
365+
The [`defineHook()`](/docs/api-reference/workflow/define-hook) helper provides type safety and runtime validation between creating and resuming hooks using [Standard Schema v1](https://standardschema.dev). Use any compliant validator like Zod or Valibot:
366366

367367
```typescript lineNumbers
368368
import { defineHook } from "workflow";
369-
370-
// Define the hook type once
371-
type ApprovalRequest = {
372-
requestId: string;
373-
approved: boolean;
374-
approvedBy: string;
375-
comment: string;
376-
};
377-
378-
const approvalHook = defineHook<ApprovalRequest>();
369+
import { z } from "zod";
370+
371+
// Define the hook with schema for type safety and runtime validation
372+
const approvalHook = defineHook({ // [!code highlight]
373+
schema: z.object({ // [!code highlight]
374+
requestId: z.string(), // [!code highlight]
375+
approved: z.boolean(), // [!code highlight]
376+
approvedBy: z.string(), // [!code highlight]
377+
comment: z.string().transform((value) => value.trim()), // [!code highlight]
378+
}), // [!code highlight]
379+
}); // [!code highlight]
379380

380381
// In your workflow
381382
export async function documentApprovalWorkflow(documentId: string) {
@@ -385,24 +386,25 @@ export async function documentApprovalWorkflow(documentId: string) {
385386
token: `approval:${documentId}`
386387
});
387388

389+
// Payload is type-safe and validated
388390
const approval = await hook;
389391

390392
console.log(`Document ${approval.requestId} ${approval.approved ? "approved" : "rejected"}`);
391393
console.log(`By: ${approval.approvedBy}, Comment: ${approval.comment}`);
392394
}
393395

394-
// In your API route - TypeScript ensures the payload matches!
396+
// In your API route - both type-safe and runtime-validated!
395397
export async function POST(request: Request) {
396398
const { documentId, ...approvalData } = await request.json();
397399

398-
// This is type-safe - TypeScript knows the exact shape required
400+
// The schema validates the payload before resuming the workflow
399401
await approvalHook.resume(`approval:${documentId}`, approvalData);
400402

401403
return new Response("OK");
402404
}
403405
```
404406

405-
This pattern is especially valuable in larger applications where the workflow and API code are in separate files.
407+
This pattern is especially valuable in larger applications where the workflow and API code are in separate files, providing both compile-time type safety and runtime validation.
406408

407409
## Best Practices
408410

0 commit comments

Comments
 (0)