Skip to content

Hooks Guide ​

Overview ​

Hooks allow you to execute code at specific points in the request lifecycle. Gati automatically records hook definitions and execution traces for debugging and visualization.

Hook Types ​

Before Hooks ​

Execute before the handler runs.

typescript
export const handler: Handler = async (req, res, gctx, lctx) => {
  lctx.before(async () => {
    // Validate authentication
    const token = req.headers.authorization;
    if (!token) throw new Error('Unauthorized');
  }, { id: 'auth-check', level: 'global' });

  res.json({ message: 'Success' });
};

After Hooks ​

Execute after the handler completes.

typescript
lctx.after(() => {
  console.log('Request completed');
}, { id: 'log-completion', level: 'local' });

Catch Hooks ​

Execute when errors occur.

typescript
lctx.catch((error) => {
  console.error('Handler failed:', error);
}, { id: 'error-logger', level: 'global' });

Hook Levels ​

  • global - Runs for all requests
  • route - Runs for specific route patterns
  • local - Runs only for this handler

Hook Manifest Recording ​

Gati automatically extracts hook definitions during build time and stores them in manifests. This enables:

  • Runtime introspection
  • Debugging and visualization
  • Performance analysis
  • Execution order verification

Manifest Structure ​

Each handler's hooks are recorded in a manifest:

json
{
  "handlerId": "users-[id]",
  "hooks": [
    {
      "id": "auth-check",
      "type": "before",
      "level": "global",
      "isAsync": true,
      "timeout": 5000
    }
  ],
  "createdAt": 1706140800000,
  "version": "1.0.0"
}

Accessing Manifests ​

typescript
import { createManifestStore } from '@gati-framework/runtime';

const store = createManifestStore();
const manifest = store.getHookManifest('users-[id]');

console.log(`Handler has ${manifest?.hooks.length} hooks`);

Runtime Recording ​

Enable hook execution recording for debugging:

typescript
import { HookOrchestrator } from '@gati-framework/runtime';

const orchestrator = new HookOrchestrator();
const playback = orchestrator.enablePlayback();

// Hooks are now recorded during execution

Analyzing Execution ​

typescript
playback.startRequest(requestId);
// ... request executes ...
playback.endRequest(requestId);

const trace = playback.getHookTrace(requestId);
trace?.traces.forEach(hook => {
  console.log(`${hook.hookId}: ${hook.duration}ms`);
});

Configuration ​

Timeout ​

Set maximum execution time:

typescript
lctx.before(async () => {
  await fetchData();
}, { id: 'fetch', timeout: 3000 });

Retries ​

Configure retry attempts:

typescript
lctx.before(async () => {
  await unreliableOperation();
}, { id: 'retry-op', retries: 3 });

Best Practices ​

  1. Use meaningful IDs - Makes debugging easier
  2. Set appropriate timeouts - Prevent hanging requests
  3. Keep hooks focused - Single responsibility per hook
  4. Use correct levels - Global for auth, local for specific logic
  5. Handle errors gracefully - Use catch hooks for cleanup

See Also ​

Released under the MIT License.