Caller Manager

The runtime engine that executes HTTP requests. Learn about global configuration, event handling, batch requests, and cache invalidation.

Overview

The IgniterCallerManager is the runtime engine behind every Caller instance. When you call .build() on your configuration, you receive a manager that handles all the heavy lifting: executing requests, managing interceptors, emitting events, and coordinating caching.

While the Request Builder focuses on configuring individual requests, the Manager provides global capabilities that affect all requests or operate outside the request lifecycle.

import { IgniterCallerManager } from '@igniter-js/caller';

// Your api instance IS a manager
const api = IgniterCaller.create()
  .withBaseUrl('https://api.example.com')
  .build();

// Manager-level operations
IgniterCallerManager.on('response', handleResponse);
IgniterCallerManager.invalidate('users:*');

Instance vs Static Methods

The Manager exposes two types of methods:

TypeAccessPurpose
Instanceapi.get(), api.post()Creating requests for this client
StaticIgniterCallerManager.on()Global operations across all clients

This design allows you to have multiple independent API clients while still coordinating global behaviors like event handling and cache invalidation.

// Two independent clients
const apiV1 = IgniterCaller.create().withBaseUrl('/api/v1').build();
const apiV2 = IgniterCaller.create().withBaseUrl('/api/v2').build();

// Global event listener applies to BOTH
IgniterCallerManager.on('response', (event) => {
  console.log(`Response from ${event.url}: ${event.status}`);
});

Creating Requests

The manager provides convenience methods for all HTTP verbs:


Batch Requests

Execute multiple requests in parallel with automatic error handling:

const results = await IgniterCallerManager.batch([
  api.get('/users').execute(),
  api.get('/posts').execute(),
  api.get('/comments').execute(),
]);

// results is an array of { data, error, status } objects
const [users, posts, comments] = results;

if (users.error) {
  console.log('Failed to fetch users');
} else {
  console.log(`Fetched ${users.data.length} users`);
}

Batch with Settlement

Batch requests use Promise.allSettled semantics—one failure doesn't abort the others:

const results = await IgniterCallerManager.batch([
  api.get('/always-works').execute(),
  api.get('/sometimes-fails').execute(),
  api.get('/always-works-2').execute(),
]);

// Even if the second request fails, you get results from all three
results.forEach((result, index) => {
  if (result.error) {
    console.log(`Request ${index} failed: ${result.error.message}`);
  } else {
    console.log(`Request ${index} succeeded`);
  }
});

Typed Batch Results

For type safety with heterogeneous requests, use the generic form:

const [usersResult, postsResult] = await IgniterCallerManager.batch<
  [User[], Post[]]
>([
  api.get('/users').execute(),
  api.get('/posts').execute(),
]);

// usersResult.data is User[] | null
// postsResult.data is Post[] | null

Global Events

Subscribe to events across all Caller instances for logging, analytics, or error tracking:

Event Handler Examples

// Error tracking
IgniterCallerManager.on('error', (event) => {
  Sentry.captureException(event.error, {
    tags: { url: event.url, method: event.method },
  });
});

// Performance monitoring
IgniterCallerManager.on('response', (event) => {
  if (event.duration > 1000) {
    console.warn(`Slow request: ${event.url} took ${event.duration}ms`);
  }
});

// Debug logging in development
if (process.env.NODE_ENV === 'development') {
  IgniterCallerManager.on('request', (e) => console.log('→', e.method, e.url));
  IgniterCallerManager.on('response', (e) => console.log('←', e.status, e.url));
}

Cache Invalidation

Programmatically invalidate cached responses when data changes:

Cache Invalidation Patterns

// Pattern 1: Invalidate on mutation
async function createUser(data: CreateUserInput) {
  const { data: user, error } = await api.post('/users')
    .body(data)
    .execute();
  
  if (!error) {
    IgniterCallerManager.invalidate('users:list');
  }
  
  return user;
}

// Pattern 2: Invalidate related data
async function updateUserProfile(userId: string, data: ProfileInput) {
  await api.patch('/users/:id/profile')
    .params({ id: userId })
    .body(data)
    .execute();
  
  // Invalidate multiple related caches
  IgniterCallerManager.invalidate(`users:${userId}`);
  IgniterCallerManager.invalidate(`users:${userId}:profile`);
  IgniterCallerManager.invalidate('users:list'); // List might show profile data
}

// Pattern 3: React hook integration
function useCreateUser() {
  const queryClient = useQueryClient();
  
  return async (data: CreateUserInput) => {
    const { data: user } = await api.post('/users').body(data).execute();
    
    // Invalidate Caller cache
    IgniterCallerManager.invalidate('users:list');
    
    // Invalidate React Query cache (if using both)
    queryClient.invalidateQueries(['users']);
    
    return user;
  };
}

Common Patterns


Best Practices

Manager Guidelines

  1. Use instance methods for requestsapi.get(), api.post(), etc.
  2. Use static methods for global operations — events, invalidation
  3. Set up event handlers early — typically in your app's entry point
  4. Clean up subscriptions — especially in React useEffect or component unmount
  5. Use meaningful cache keys — makes invalidation patterns clearer

Next Steps