Writing Runners
Guide to creating custom runners for HTTP API
Overview
This guide covers everything you need to know about writing effective runners.
Basic Runner Structure
A runner is an async function that:
- Has the
"use runner"directive - Accepts
RunnerContextand optional input - Returns a
RunnerResult
The "use runner" Directive
The directive tells the discovery system that this function is a runner. It can be:
Function-Level (Recommended)
Module-Level
Best Practice: Use function-level directives for clarity and to avoid accidentally marking helper functions as runners.
Input and Output Validation
Runners support validation for both input and output using Zod or Standard Schema. Input schemas are validated at runtime, while output schemas provide TypeScript typing for the details field.
Input Validation
Use Zod schemas to validate and type your input:
The runner harness will automatically validate input against your schema before execution.
Output Validation
Define output schemas to type the details field in your results:
Using Playwright
For browser automation, use the withPlaywright() helper:
The withPlaywright() helper:
- Launches a browser instance
- Provides a
pageobject - Handles browser lifecycle
- Manages browser contexts
Runner Context
The RunnerContext provides:
Logging
Use ctx.log() for structured logging:
Region and Run ID
Access region and run ID from context:
Error Handling
Runners can throw errors, which are automatically caught and converted to error results:
Errors result in:
Best Practices
1. Use Descriptive Names
2. Validate Input
Always define input schemas:
3. Return Meaningful Details
4. Use Logging
Log important events:
5. Handle Edge Cases
HTTP API-Specific Notes
When writing runners for standalone HTTP API:
- Import runners from your
runners/directory - Use
createHttpRunner()to create the handler - Runners are executed via HTTP requests
See Also
- Deployment - Deploy HTTP API servers
- API Reference - Complete API documentation