Skip to content

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:

plaintext
File Change → Manifest Update → Route Reload → Live Update
    ↓              ↓              ↓            ↓
  50ms           100ms          150ms       200ms

Total 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.ts changes
  • 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

bash
pnpm dev

Output:

plaintext
🚀 Starting development server...
✅ Loaded 3 handlers, 1 module
🌐 Server running at http://localhost:3000
👁️ Watching for changes...

2. Make Changes

Edit any handler file:

typescript
// 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

plaintext
📝 File changed: hello.ts
✅ Updated manifest: hello.json
🔄 Reloaded GET /hello
⚡ Ready in 180ms

Test immediately:

bash
curl http://localhost:3000/api/hello
# {"message":"Hello, Updated World!","timestamp":"..."}

Hot Reload Examples

Adding New Handler

Create: src/handlers/users/profile.ts

typescript
export const METHOD = 'GET';
export const handler: Handler = (req, res) => {
  res.json({ profile: 'User profile data' });
};

Console Output:

plaintext
📄 File added: users/profile.ts
✅ Created manifest: users_profile.json
🆕 Registered GET /users/profile
⚡ Ready in 120ms

Test Immediately:

bash
curl http://localhost:3000/api/users/profile
# {"profile":"User profile data"}

Modifying Route Method

Change: src/handlers/users/create.ts

typescript
// Before
export const METHOD = 'GET';

// After  
export const METHOD = 'POST'; // ← Changed method

Console Output:

plaintext
📝 File changed: users/create.ts
❌ Unregistered GET /users/create
🆕 Registered POST /users/create
⚡ Ready in 150ms

Deleting Handler

Delete: src/handlers/old-endpoint.ts

Console Output:

plaintext
🗑️ File removed: old-endpoint.ts
🗑️ Removed manifest: old-endpoint.json
❌ Unregistered GET /old-endpoint
⚡ Ready in 90ms

Updating Module

Modify: src/modules/database.ts

typescript
export const database = {
  // Add new method
  findByEmail: (email: string) => { /* ... */ },
  
  // Existing methods...
  findById: (id: string) => { /* ... */ }
};

Console Output:

plaintext
📝 File changed: database.ts
✅ Updated manifest: database.json
🔄 Module reloaded: database
⚡ Ready in 110ms

Configuration Hot Reload

Route Overrides

Modify: gati.config.ts

typescript
export default {
  overrides: {
    // Add new override
    'GET /users/:id': {
      middleware: [authMiddleware],
      rateLimit: { requests: 100, window: '1m' }
    }
  }
};

Console Output:

plaintext
📝 Config changed: gati.config.ts
🔄 Reloading configuration...
✅ Applied overrides to 1 route
⚡ Ready in 200ms

Middleware Updates

Add Global Middleware:

typescript
export default {
  middleware: [
    corsMiddleware,
    loggerMiddleware,
    newMiddleware // ← Added
  ]
};

Console Output:

plaintext
📝 Config changed: gati.config.ts
🔄 Reloading middleware stack...
✅ Applied middleware to all routes
⚡ Ready in 180ms

Performance Optimizations

Incremental Updates

Only changed files are reprocessed:

plaintext
✅ 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 seconds

Parallel Processing

File watching and manifest aggregation run in parallel:

plaintext
Thread 1: Watch src/ files
Thread 2: Watch .gati/manifests/
Thread 3: HTTP server
Thread 4: Route management

Memory 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:

bash
# Terminal 1: TypeScript compiler
pnpm tsc --watch

# Terminal 2: Gati dev server  
pnpm dev

2. Organize Files for Fast Reloads

plaintext
✅ 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:

typescript
// 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:

bash
# Watch for changes and test
watch -n 1 'curl -s http://localhost:3000/api/hello | jq'

Debugging Hot Reload

Enable Verbose Logging

bash
pnpm dev --verbose

Output:

plaintext
🔍 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: 156ms

Check Manifest Contents

bash
# View individual manifest
cat .gati/manifests/hello.json | jq

# View application manifest
cat .gati/manifests/_app.json | jq '.handlers'

Monitor File Events

bash
# Watch file system events
fswatch src/ | while read file; do
  echo "Changed: $file"
done

Troubleshooting

Hot Reload Not Working

Check 1: File Watching Enabled

bash
pnpm dev --watch  # Ensure --no-watch is not set

Check 2: Valid TypeScript

bash
pnpm tsc --noEmit  # Check for syntax errors

Check 3: Proper Exports

typescript
// ✅ Correct
export const handler: Handler = (req, res) => { /* ... */ };

// ❌ Missing export
const handler: Handler = (req, res) => { /* ... */ };

Slow Hot Reload

Check 1: File Count

bash
find src/ -name "*.ts" | wc -l
# If >1000 files, consider excluding some

Check 2: Disable Unnecessary Watching

typescript
// gati.config.ts
export default {
  ignore: [
    'src/**/*.test.ts',
    'src/**/*.spec.ts',
    'src/temp/**'
  ]
};

Check 3: System Resources

bash
# Check CPU/memory usage
top -p $(pgrep -f "gati dev")

Routes Not Updating

Check 1: Manifest Generation

bash
ls -la .gati/manifests/
# Should see .json files for each handler

Check 2: Route Conflicts Look for warnings in console:

plaintext
⚠️ Route conflict: GET /users
  - handlers/users.ts (using this)
  - handlers/users/index.ts

Check 3: Configuration Overrides

typescript
// Check if route is disabled in config
export default {
  overrides: {
    'GET /users': false  // ← This disables the route
  }
};

Advanced Configuration

Custom Watch Patterns

typescript
// gati.config.ts
export default {
  watch: {
    patterns: [
      'src/**/*.ts',
      'lib/**/*.js',
      'config/*.json'
    ],
    ignore: [
      '**/*.test.ts',
      '**/node_modules/**'
    ]
  }
};

Hot Reload Hooks

typescript
// gati.config.ts
export default {
  hooks: {
    beforeReload: (changedFiles) => {
      console.log('Reloading:', changedFiles);
    },
    afterReload: (loadedRoutes) => {
      console.log('Loaded routes:', loadedRoutes.length);
    }
  }
};

Conditional Hot Reload

typescript
// gati.config.ts
export default {
  hotReload: {
    enabled: process.env.NODE_ENV === 'development',
    debounce: 100, // ms
    batchUpdates: true
  }
};

Next Steps

Released under the MIT License.