Hot Reloading
Gati provides lightning-fast hot reloading for instant development feedback. Make changes to your handlers, modules, or configuration and see results immediately without server restarts.
How It Works
Hot reloading operates through the manifest system:
File Change → Manifest Update → Route Reload → Live Update
↓ ↓ ↓ ↓
50ms 100ms 150ms 200msTotal reload time: ~200ms ⚡
What Gets Hot Reloaded
✅ Handlers
- Route logic changes
- New handlers added
- Handlers deleted
- Method/route changes
✅ Modules
- Module logic updates
- New modules added
- Module exports changed
✅ Configuration
gati.config.tschanges- Route overrides
- Middleware updates
❌ Not Hot Reloaded
- Package.json changes (requires restart)
- TypeScript config changes (requires restart)
- Environment variables (requires restart)
Development Workflow
1. Start Development Server
pnpm devOutput:
🚀 Starting development server...
✅ Loaded 3 handlers, 1 module
🌐 Server running at http://localhost:3000
👁️ Watching for changes...2. Make Changes
Edit any handler file:
// src/handlers/hello.ts
export const handler: Handler = (req, res) => {
res.json({
message: 'Hello, Updated World!', // ← Change this
timestamp: new Date().toISOString()
});
};3. See Instant Updates
📝 File changed: hello.ts
✅ Updated manifest: hello.json
🔄 Reloaded GET /hello
⚡ Ready in 180msTest immediately:
curl http://localhost:3000/api/hello
# {"message":"Hello, Updated World!","timestamp":"..."}Hot Reload Examples
Adding New Handler
Create: src/handlers/users/profile.ts
export const METHOD = 'GET';
export const handler: Handler = (req, res) => {
res.json({ profile: 'User profile data' });
};Console Output:
📄 File added: users/profile.ts
✅ Created manifest: users_profile.json
🆕 Registered GET /users/profile
⚡ Ready in 120msTest Immediately:
curl http://localhost:3000/api/users/profile
# {"profile":"User profile data"}Modifying Route Method
Change: src/handlers/users/create.ts
// Before
export const METHOD = 'GET';
// After
export const METHOD = 'POST'; // ← Changed methodConsole Output:
📝 File changed: users/create.ts
❌ Unregistered GET /users/create
🆕 Registered POST /users/create
⚡ Ready in 150msDeleting Handler
Delete: src/handlers/old-endpoint.ts
Console Output:
🗑️ File removed: old-endpoint.ts
🗑️ Removed manifest: old-endpoint.json
❌ Unregistered GET /old-endpoint
⚡ Ready in 90msUpdating Module
Modify: src/modules/database.ts
export const database = {
// Add new method
findByEmail: (email: string) => { /* ... */ },
// Existing methods...
findById: (id: string) => { /* ... */ }
};Console Output:
📝 File changed: database.ts
✅ Updated manifest: database.json
🔄 Module reloaded: database
⚡ Ready in 110msConfiguration Hot Reload
Route Overrides
Modify: gati.config.ts
export default {
overrides: {
// Add new override
'GET /users/:id': {
middleware: [authMiddleware],
rateLimit: { requests: 100, window: '1m' }
}
}
};Console Output:
📝 Config changed: gati.config.ts
🔄 Reloading configuration...
✅ Applied overrides to 1 route
⚡ Ready in 200msMiddleware Updates
Add Global Middleware:
export default {
middleware: [
corsMiddleware,
loggerMiddleware,
newMiddleware // ← Added
]
};Console Output:
📝 Config changed: gati.config.ts
🔄 Reloading middleware stack...
✅ Applied middleware to all routes
⚡ Ready in 180msPerformance Optimizations
Incremental Updates
Only changed files are reprocessed:
✅ Fast: Single file change
src/handlers/users.ts → users.json → _app.json
Time: ~100ms
❌ Slow: Full rebuild (traditional)
Any change → Scan all files → Rebuild everything
Time: ~2-5 secondsParallel Processing
File watching and manifest aggregation run in parallel:
Thread 1: Watch src/ files
Thread 2: Watch .gati/manifests/
Thread 3: HTTP server
Thread 4: Route managementMemory Efficiency
- Individual manifests: ~1KB each
- Application manifest: ~10KB for 100 routes
- Total memory overhead: <5MB
Development Tips
1. Use TypeScript Watch Mode
For even faster feedback:
# Terminal 1: TypeScript compiler
pnpm tsc --watch
# Terminal 2: Gati dev server
pnpm dev2. Organize Files for Fast Reloads
✅ Good: Focused files
src/handlers/
├── users/
│ ├── list.ts # Single responsibility
│ ├── create.ts # Single responsibility
│ └── [id].ts # Single responsibility
❌ Avoid: Monolithic files
src/handlers/
└── users.ts # All user logic (slow reloads)3. Use Module Hot Reload
Extract shared logic to modules for faster iteration:
// src/modules/validation.ts
export const validation = {
validateEmail: (email: string) => { /* ... */ },
validateUser: (user: any) => { /* ... */ }
};
// src/handlers/users/create.ts
export const handler: Handler = (req, res, gctx) => {
const isValid = gctx.modules.validation.validateUser(req.body);
// ...
};When you update validation logic, all handlers using it get the updates instantly.
4. Test Changes Immediately
Keep a terminal open for quick testing:
# Watch for changes and test
watch -n 1 'curl -s http://localhost:3000/api/hello | jq'Debugging Hot Reload
Enable Verbose Logging
pnpm dev --verboseOutput:
🔍 Watching: src/**/*.{ts,js}
🔍 Watching: .gati/manifests/*.json
📝 File event: change → src/handlers/hello.ts
🔄 Processing: hello.ts
✅ Manifest updated: hello.json
🔄 Aggregating manifests...
✅ App manifest updated: _app.json
🔄 Reloading routes...
✅ Route reloaded: GET /hello
⚡ Total time: 156msCheck Manifest Contents
# View individual manifest
cat .gati/manifests/hello.json | jq
# View application manifest
cat .gati/manifests/_app.json | jq '.handlers'Monitor File Events
# Watch file system events
fswatch src/ | while read file; do
echo "Changed: $file"
doneTroubleshooting
Hot Reload Not Working
Check 1: File Watching Enabled
pnpm dev --watch # Ensure --no-watch is not setCheck 2: Valid TypeScript
pnpm tsc --noEmit # Check for syntax errorsCheck 3: Proper Exports
// ✅ Correct
export const handler: Handler = (req, res) => { /* ... */ };
// ❌ Missing export
const handler: Handler = (req, res) => { /* ... */ };Slow Hot Reload
Check 1: File Count
find src/ -name "*.ts" | wc -l
# If >1000 files, consider excluding someCheck 2: Disable Unnecessary Watching
// gati.config.ts
export default {
ignore: [
'src/**/*.test.ts',
'src/**/*.spec.ts',
'src/temp/**'
]
};Check 3: System Resources
# Check CPU/memory usage
top -p $(pgrep -f "gati dev")Routes Not Updating
Check 1: Manifest Generation
ls -la .gati/manifests/
# Should see .json files for each handlerCheck 2: Route Conflicts Look for warnings in console:
⚠️ Route conflict: GET /users
- handlers/users.ts (using this)
- handlers/users/index.tsCheck 3: Configuration Overrides
// Check if route is disabled in config
export default {
overrides: {
'GET /users': false // ← This disables the route
}
};Advanced Configuration
Custom Watch Patterns
// gati.config.ts
export default {
watch: {
patterns: [
'src/**/*.ts',
'lib/**/*.js',
'config/*.json'
],
ignore: [
'**/*.test.ts',
'**/node_modules/**'
]
}
};Hot Reload Hooks
// gati.config.ts
export default {
hooks: {
beforeReload: (changedFiles) => {
console.log('Reloading:', changedFiles);
},
afterReload: (loadedRoutes) => {
console.log('Loaded routes:', loadedRoutes.length);
}
}
};Conditional Hot Reload
// gati.config.ts
export default {
hotReload: {
enabled: process.env.NODE_ENV === 'development',
debounce: 100, // ms
batchUpdates: true
}
};Next Steps
- Development Server - Server features and configuration
- Manifest System - How hot reload works internally
- Configuration - Advanced configuration options