Documentation Index Fetch the complete documentation index at: https://mintlify.com/hey-api/openapi-ts/llms.txt
Use this file to discover all available pages before exploring further.
Watch Mode
Watch mode automatically regenerates your TypeScript client whenever your OpenAPI specification changes. This is particularly useful during development when your API is evolving.
Overview
Watch mode polls your OpenAPI specification at regular intervals and regenerates the client when changes are detected. It works with both local files and remote URLs.
Watch mode is designed for remote specifications accessed via HTTP/HTTPS. For local file watching, consider using a file system watcher like nodemon or chokidar instead.
Configuration
Watch mode is configured through the input.watch property. The implementation is in /home/daytona/workspace/source/packages/shared/src/config/input/types.ts:175:
export type Watch = {
/**
* Whether this feature is enabled.
*
* @default false
*/
enabled ?: boolean ;
/**
* How often should we attempt to detect the input file change? (in ms)
*
* @default 1000
*/
interval ?: number ;
/**
* How long will we wait before the request times out?
*
* @default 60_000
*/
timeout ?: number ;
};
Basic Usage
CLI
Enable watch mode with the -w or --watch flag:
# Enable with default 1000ms interval
openapi-ts -i https://api.example.com/openapi.json -o ./src/client -w
# Specify custom interval (2000ms)
openapi-ts -i https://api.example.com/openapi.json -o ./src/client -w 2000
Configuration File
import { defineConfig } from '@hey-api/openapi-ts' ;
export default defineConfig ({
input: {
path: 'https://api.example.com/openapi.json' ,
watch: true , // Enable with defaults
} ,
output: 'src/client' ,
}) ;
Configuration Options
enabled
Type: boolean
Default: false
Enables or disables watch mode.
watch : {
enabled : true ,
}
interval
Type: number (milliseconds)
Default: 1000
How frequently to check for changes. The watcher polls the specification URL at this interval.
watch : {
enabled : true ,
interval : 5000 , // Check every 5 seconds
}
Setting a very low interval (< 100ms) can cause excessive network requests and may trigger rate limiting on some APIs.
timeout
Type: number (milliseconds)
Default: 60_000 (60 seconds)
How long to wait for the specification to download before timing out.
watch : {
enabled : true ,
timeout : 30_000 , // 30 second timeout for slow APIs
}
Configure watch mode independently for each input:
import { defineConfig } from '@hey-api/openapi-ts' ;
export default defineConfig ({
input: [
{
path: 'https://api.example.com/v1/openapi.json' ,
watch: true , // Watch this one
},
{
path: 'https://api.example.com/v2/openapi.json' ,
watch: false , // Don't watch this one
},
] ,
output: 'src/client' ,
}) ;
Global Watch Configuration
Apply watch settings to all inputs:
import { defineConfig } from '@hey-api/openapi-ts' ;
export default defineConfig ({
input: [
'https://api.example.com/v1/openapi.json' ,
'https://api.example.com/v2/openapi.json' ,
] ,
output: 'src/client' ,
// Global watch configuration
watch: {
enabled: true ,
interval: 3000 ,
} ,
}) ;
How It Works
The watch mode implementation uses HTTP polling with conditional requests. From /home/daytona/workspace/source/packages/openapi-ts/src/createClient.ts:169:
const watchedInput = config . input . find (
( input , index ) =>
input . watch . enabled &&
typeof inputPaths [ index ] ! . path === 'string' ,
);
if ( watchedInput ) {
setTimeout (() => {
createClient ({
config ,
dependencies ,
jobIndex ,
logger ,
watches , // Passes headers for conditional requests
});
}, watchedInput . watch . interval );
}
Conditional Requests
Watch mode uses HTTP headers to avoid unnecessary regeneration:
ETag - Cached entity tags
Last-Modified - Cached modification times
If-None-Match - Conditional request headers
If-Modified-Since - Conditional request headers
If the server responds with 304 Not Modified, regeneration is skipped.
Authentication
For APIs requiring authentication, pass credentials via fetch options:
import { defineConfig } from '@hey-api/openapi-ts' ;
export default defineConfig ({
input: {
path: 'https://api.example.com/openapi.json' ,
watch: true ,
fetch: {
headers: {
'Authorization' : `Bearer ${ process . env . API_TOKEN } ` ,
},
},
} ,
output: 'src/client' ,
}) ;
Programmatic Usage
When using createClient programmatically, watch mode runs indefinitely:
import { createClient } from '@hey-api/openapi-ts' ;
// This will run forever in watch mode
await createClient ({
input: {
path: 'https://api.example.com/openapi.json' ,
watch: true ,
},
output: 'src/client' ,
});
// This code will never execute
console . log ( 'Done!' );
Detecting Watch Mode
Check if watch mode is active:
import { createClient } from '@hey-api/openapi-ts' ;
const contexts = await createClient ({
input: {
path: 'https://api.example.com/openapi.json' ,
watch: process . env . WATCH === 'true' ,
},
output: 'src/client' ,
});
const hasActiveWatch = contexts [ 0 ]?. config . input . some (
( input ) => input . watch ?. enabled
);
if ( ! hasActiveWatch ) {
console . log ( 'Generation complete!' );
process . exit ( 0 );
} else {
console . log ( 'Watching for changes...' );
}
Development Workflow
Package Scripts
Add watch mode to your development workflow:
{
"scripts" : {
"generate" : "openapi-ts -c openapi-ts.config.ts" ,
"generate:watch" : "openapi-ts -c openapi-ts.config.ts -w" ,
"dev" : "npm run generate:watch & next dev"
}
}
Docker Compose
Watch a specification served by Docker:
services :
api :
image : my-api:latest
ports :
- "3000:3000"
codegen :
image : node:20
working_dir : /app
volumes :
- ./:/app
command : npm run generate:watch
environment :
- OPENAPI_URL=http://api:3000/openapi.json
Error Handling
Watch mode handles errors gracefully to avoid crashing during development:
// From /home/daytona/workspace/source/packages/openapi-ts/src/createClient.ts:64
if ( error && ! _watches ) {
// Throw on first run
const text = await response . text (). catch (() => '' );
throw new Error (
`Request failed with status ${ response . status } : ${ text || response . statusText } ` ,
);
}
// Subsequent errors in watch mode are logged but don't throw
// This allows the watcher to recover when the server comes back online
Handling Transient Errors
If your API server restarts, watch mode will continue polling:
import { defineConfig } from '@hey-api/openapi-ts' ;
export default defineConfig ({
input: {
path: 'http://localhost:3000/openapi.json' ,
watch: {
enabled: true ,
interval: 2000 ,
timeout: 5000 , // Shorter timeout for local development
},
} ,
output: 'src/client' ,
logs: {
level: 'info' , // Log when regeneration occurs
} ,
}) ;
Choose Appropriate Intervals
Balance responsiveness with resource usage:
Development : 1000-3000ms is reasonable
CI/CD : Don’t use watch mode
Production : Never use watch mode
Ensure your API server supports ETag and Last-Modified headers to minimize unnecessary regeneration.
For large specifications, increase the interval:
watch : {
enabled : true ,
interval : 5000 , // Less frequent for large specs
}
Watch mode clears the console on regeneration (from /home/daytona/workspace/source/packages/openapi-ts/src/createClient.ts:101):
if ( config . logs . level !== 'silent' && _watches ) {
console . clear ();
// ... log input paths
}
Limitations
Watch mode has several limitations:
Remote URLs only - Designed for HTTP/HTTPS specifications
Polling-based - Not as efficient as file system watching
Single process - Runs in the foreground
No file system events - Won’t detect local file changes
Local File Watching
For local files, use a file system watcher instead:
# Using nodemon
nodemon --watch openapi.json --exec "openapi-ts -c openapi-ts.config.ts"
# Using chokidar-cli
chokidar 'openapi.json' -c "openapi-ts -c openapi-ts.config.ts"
Or create a custom watcher:
import { watch } from 'node:fs' ;
import { createClient } from '@hey-api/openapi-ts' ;
watch ( 'openapi.json' , async ( eventType ) => {
if ( eventType === 'change' ) {
console . log ( 'Regenerating client...' );
await createClient ({
input: 'openapi.json' ,
output: 'src/client' ,
});
}
});
Next Steps
Programmatic Usage Learn how to use the createClient API programmatically
Custom Plugins Build custom plugins to extend code generation