Watch filesystem changes
This guide shows you how to monitor filesystem changes in real-time using the Sandbox SDK's file watching capabilities. File watching is essential for building development tools, automated workflows, and responsive applications that react to file system changes.
Start by watching a directory for any changes:
const watcher = await sandbox.watch("/workspace/src", { onEvent: (event) => { console.log(`${event.type} event: ${event.path}`); console.log(`Is directory: ${event.isDirectory}`); }, onError: (error) => { console.error("Watch failed:", error.message); },});
// Always clean up when doneprocess.on("exit", () => watcher.stop());const watcher = await sandbox.watch('/workspace/src', { onEvent: (event) => { console.log(`${event.type} event: ${event.path}`); console.log(`Is directory: ${event.isDirectory}`); }, onError: (error) => { console.error('Watch failed:', error.message); }});
// Always clean up when doneprocess.on('exit', () => watcher.stop());The watcher will detect four types of events:
- create - Files or directories created
- modify - File content or attributes changed
- delete - Files or directories removed
- rename - Files or directories moved/renamed
Use include patterns to watch only specific file types:
// Only watch TypeScript and JavaScript filesconst watcher = await sandbox.watch("/workspace/src", { include: ["*.ts", "*.tsx", "*.js", "*.jsx"], onEvent: (event) => { console.log(`${event.type}: ${event.path}`); },});// Only watch TypeScript and JavaScript filesconst watcher = await sandbox.watch('/workspace/src', { include: ['*.ts', '*.tsx', '*.js', '*.jsx'], onEvent: (event) => { console.log(`${event.type}: ${event.path}`); }});Common include patterns:
*.ts- TypeScript files*.js- JavaScript files*.json- JSON configuration files*.md- Markdown documentationpackage*.json- Package files specifically
Use exclude patterns to ignore certain directories or files:
const watcher = await sandbox.watch("/workspace", { exclude: [ "node_modules", // Dependencies "dist", // Build output "*.log", // Log files ".git", // Git metadata (excluded by default) "*.tmp", // Temporary files ], onEvent: (event) => { console.log(`Change detected: ${event.path}`); },});const watcher = await sandbox.watch('/workspace', { exclude: [ 'node_modules', // Dependencies 'dist', // Build output '*.log', // Log files '.git', // Git metadata (excluded by default) '*.tmp' // Temporary files ], onEvent: (event) => { console.log(`Change detected: ${event.path}`); }});Build a development server that automatically restarts when source files change:
let serverProcess = null;
async function startServer() { if (serverProcess) { await serverProcess.stop(); }
console.log("Starting development server..."); serverProcess = await sandbox.startProcess("npm run dev", { onOutput: (stream, data) => { console.log(`[server] ${data}`); }, });}
const watcher = await sandbox.watch("/workspace/src", { include: ["*.ts", "*.js", "*.json"], onEvent: async (event) => { if (event.type === "modify") { console.log(`Detected change in ${event.path}, restarting server...`); await startServer(); } },});
// Initial server startawait startServer();let serverProcess: { stop: () => Promise<void> } | null = null;
async function startServer() { if (serverProcess) { await serverProcess.stop(); }
console.log('Starting development server...'); serverProcess = await sandbox.startProcess('npm run dev', { onOutput: (stream, data) => { console.log(`[server] ${data}`); } });}
const watcher = await sandbox.watch('/workspace/src', { include: ['*.ts', '*.js', '*.json'], onEvent: async (event) => { if (event.type === 'modify') { console.log(`Detected change in ${event.path}, restarting server...`); await startServer(); } }});
// Initial server startawait startServer();Trigger builds automatically when source files are modified:
let buildInProgress = false;
const watcher = await sandbox.watch("/workspace/src", { include: ["*.ts", "*.tsx"], onEvent: async (event) => { if (event.type === "modify" && !buildInProgress) { buildInProgress = true; console.log("Building TypeScript project...");
try { const result = await sandbox.exec("npm run build"); if (result.success) { console.log("Build completed successfully"); } else { console.error("Build failed:", result.stderr); } } catch (error) { console.error("Build error:", error); } finally { buildInProgress = false; } } },});let buildInProgress = false;
const watcher = await sandbox.watch('/workspace/src', { include: ['*.ts', '*.tsx'], onEvent: async (event) => { if (event.type === 'modify' && !buildInProgress) { buildInProgress = true; console.log('Building TypeScript project...');
try { const result = await sandbox.exec('npm run build'); if (result.success) { console.log('Build completed successfully'); } else { console.error('Build failed:', result.stderr); } } catch (error) { console.error('Build error:', error); } finally { buildInProgress = false; } } }});Watch documentation files and rebuild docs when they change:
const watcher = await sandbox.watch("/workspace/docs", { include: ["*.md", "*.mdx"], onEvent: async (event) => { if (event.type === "modify") { console.log(`Documentation updated: ${event.path}`);
// Rebuild documentation site const result = await sandbox.exec("npm run build:docs"); if (result.success) { console.log("Documentation rebuilt"); } } },});const watcher = await sandbox.watch('/workspace/docs', { include: ['*.md', '*.mdx'], onEvent: async (event) => { if (event.type === 'modify') { console.log(`Documentation updated: ${event.path}`);
// Rebuild documentation site const result = await sandbox.exec('npm run build:docs'); if (result.success) { console.log('Documentation rebuilt'); } } }});Avoid excessive operations by debouncing rapid file changes:
let debounceTimeout = null;const changedFiles = new Set();
const watcher = await sandbox.watch("/workspace/src", { onEvent: (event) => { changedFiles.add(event.path);
// Clear existing timeout if (debounceTimeout) { clearTimeout(debounceTimeout); }
// Set new timeout to process changes debounceTimeout = setTimeout(async () => { console.log(`Processing ${changedFiles.size} changed files...`);
// Process all accumulated changes for (const filePath of changedFiles) { await processFile(filePath); }
changedFiles.clear(); debounceTimeout = null; }, 1000); // Wait 1 second after last change },});
async function processFile(filePath) { // Your file processing logic console.log(`Processing ${filePath}`);}let debounceTimeout: NodeJS.Timeout | null = null;const changedFiles = new Set<string>();
const watcher = await sandbox.watch('/workspace/src', { onEvent: (event) => { changedFiles.add(event.path);
// Clear existing timeout if (debounceTimeout) { clearTimeout(debounceTimeout); }
// Set new timeout to process changes debounceTimeout = setTimeout(async () => { console.log(`Processing ${changedFiles.size} changed files...`);
// Process all accumulated changes for (const filePath of changedFiles) { await processFile(filePath); }
changedFiles.clear(); debounceTimeout = null; }, 1000); // Wait 1 second after last change }});
async function processFile(filePath: string) { // Your file processing logic console.log(`Processing ${filePath}`);}Watch multiple directories with different configurations:
// Watch source code for buildsconst srcWatcher = await sandbox.watch("/workspace/src", { include: ["*.ts", "*.tsx"], onEvent: async (event) => { if (event.type === "modify") { await sandbox.exec("npm run build:src"); } },});
// Watch tests for test runsconst testWatcher = await sandbox.watch("/workspace/tests", { include: ["*.test.ts", "*.spec.ts"], onEvent: async (event) => { if (event.type === "modify") { await sandbox.exec(`npm test -- ${event.path}`); } },});
// Watch config files for full rebuildsconst configWatcher = await sandbox.watch("/workspace", { include: ["package.json", "tsconfig.json", "vite.config.ts"], recursive: false, // Only watch root level onEvent: async (event) => { console.log("Configuration changed, rebuilding project..."); await sandbox.exec("npm run build"); },});// Watch source code for buildsconst srcWatcher = await sandbox.watch('/workspace/src', { include: ['*.ts', '*.tsx'], onEvent: async (event) => { if (event.type === 'modify') { await sandbox.exec('npm run build:src'); } }});
// Watch tests for test runsconst testWatcher = await sandbox.watch('/workspace/tests', { include: ['*.test.ts', '*.spec.ts'], onEvent: async (event) => { if (event.type === 'modify') { await sandbox.exec(`npm test -- ${event.path}`); } }});
// Watch config files for full rebuildsconst configWatcher = await sandbox.watch('/workspace', { include: ['package.json', 'tsconfig.json', 'vite.config.ts'], recursive: false, // Only watch root level onEvent: async (event) => { console.log('Configuration changed, rebuilding project...'); await sandbox.exec('npm run build'); }});Use AbortSignal for clean shutdown handling:
const controller = new AbortController();
const watcher = await sandbox.watch("/workspace/src", { signal: controller.signal, onEvent: (event) => { console.log(`Event: ${event.type} - ${event.path}`); }, onError: (error) => { if (error.name === "AbortError") { console.log("Watch cancelled gracefully"); } else { console.error("Watch error:", error); } },});
// Handle shutdown signalsprocess.on("SIGINT", () => { console.log("Shutting down file watcher..."); controller.abort();});const controller = new AbortController();
const watcher = await sandbox.watch('/workspace/src', { signal: controller.signal, onEvent: (event) => { console.log(`Event: ${event.type} - ${event.path}`); }, onError: (error) => { if (error.name === 'AbortError') { console.log('Watch cancelled gracefully'); } else { console.error('Watch error:', error); } }});
// Handle shutdown signalsprocess.on('SIGINT', () => { console.log('Shutting down file watcher...'); controller.abort();});Always stop watchers to prevent resource leaks:
const watchers = [];
// Create watchersconst srcWatcher = await sandbox.watch("/workspace/src", options);const testWatcher = await sandbox.watch("/workspace/tests", options);watchers.push(srcWatcher, testWatcher);
// Clean shutdownasync function shutdown() { console.log("Stopping all watchers..."); await Promise.all(watchers.map((w) => w.stop())); console.log("All watchers stopped");}
process.on("exit", shutdown);process.on("SIGINT", shutdown);process.on("SIGTERM", shutdown);const watchers: Array<{ stop: () => Promise<void> }> = [];
// Create watchersconst srcWatcher = await sandbox.watch('/workspace/src', options);const testWatcher = await sandbox.watch('/workspace/tests', options);watchers.push(srcWatcher, testWatcher);
// Clean shutdownasync function shutdown() { console.log('Stopping all watchers...'); await Promise.all(watchers.map(w => w.stop())); console.log('All watchers stopped');}
process.on('exit', shutdown);process.on('SIGINT', shutdown);process.on('SIGTERM', shutdown);Implement robust error handling for production use:
const watcher = await sandbox.watch("/workspace/src", { onEvent: async (event) => { try { await handleFileChange(event); } catch (error) { console.error( `Failed to handle ${event.type} event for ${event.path}:`, error, ); // Don't let errors stop the watcher } }, onError: async (error) => { console.error("Watch system error:", error);
// Attempt to restart watcher on critical errors if (error.message.includes("inotify")) { console.log("Attempting to restart file watcher..."); await watcher.stop(); // Recreate watcher with same options } },});const watcher = await sandbox.watch('/workspace/src', { onEvent: async (event) => { try { await handleFileChange(event); } catch (error) { console.error(`Failed to handle ${event.type} event for ${event.path}:`, error); // Don't let errors stop the watcher } }, onError: async (error) => { console.error('Watch system error:', error);
// Attempt to restart watcher on critical errors if (error.message.includes('inotify')) { console.log('Attempting to restart file watcher...'); await watcher.stop(); // Recreate watcher with same options } }});For high-frequency changes, use server-side filtering:
// Efficient - filtering happens at kernel/inotify levelconst watcher = await sandbox.watch("/workspace", { include: ["*.ts"], // Only TypeScript files exclude: ["node_modules"], // Skip dependencies});
// Less efficient - all events sent to JavaScriptconst watcher2 = await sandbox.watch("/workspace", { onEvent: (event) => { if (!event.path.endsWith(".ts")) return; if (event.path.includes("node_modules")) return; // Handle event },});// Efficient - filtering happens at kernel/inotify levelconst watcher = await sandbox.watch('/workspace', { include: ['*.ts'], // Only TypeScript files exclude: ['node_modules'] // Skip dependencies});
// Less efficient - all events sent to JavaScriptconst watcher2 = await sandbox.watch('/workspace', { onEvent: (event) => { if (!event.path.endsWith('.ts')) return; if (event.path.includes('node_modules')) return; // Handle event }});Ensure directories exist before watching them:
const watchPath = "/workspace/src";
// Check if path exists firsttry { const exists = await sandbox.readDir(watchPath); const watcher = await sandbox.watch(watchPath, options);} catch (error) { if (error.message.includes("not found")) { console.log(`Creating directory ${watchPath}...`); await sandbox.exec(`mkdir -p ${watchPath}`); // Now start watching const watcher = await sandbox.watch(watchPath, options); }}const watchPath = '/workspace/src';
// Check if path exists firsttry { const exists = await sandbox.readDir(watchPath); const watcher = await sandbox.watch(watchPath, options);} catch (error) { if (error.message.includes('not found')) { console.log(`Creating directory ${watchPath}...`); await sandbox.exec(`mkdir -p ${watchPath}`); // Now start watching const watcher = await sandbox.watch(watchPath, options); }}If watching large directories causes performance issues:
- Use specific
includepatterns instead of watching everything - Exclude large directories like
node_modulesanddist - Watch specific subdirectories instead of the entire project
- Use non-recursive watching for shallow monitoring
- File Watching API reference - Complete API documentation
- Manage files guide - File operations
- Background processes guide - Long-running processes
Was this helpful?
- Resources
- API
- New to Cloudflare?
- Directory
- Sponsorships
- Open Source
- Support
- Help Center
- System Status
- Compliance
- GDPR
- Company
- cloudflare.com
- Our team
- Careers
- © 2026 Cloudflare, Inc.
- Privacy Policy
- Terms of Use
- Report Security Issues
- Trademark
-