Advanced Features

Explore advanced capabilities like streaming, GraphQL, and real-time communication

Advanced 20 min read

Advanced Apisto Capabilities

Apisto provides powerful advanced features for complex API scenarios including streaming data, GraphQL support, real-time communication, and sophisticated request patterns.

🚀 Pro Tips: These advanced features can significantly improve performance and user experience in data-intensive applications.

Streaming Support

Handle large datasets or real-time data streams efficiently with Apisto's streaming capabilities.


// Basic streaming example
const api = new Apisto({
  baseURL: 'https://api.example.com'
});

async function processStream() {
  try {
    const stream = await api.stream('/data-stream');
    
    // Process chunks as they arrive
    for await (const chunk of stream) {
      console.log('Received chunk:', chunk);
      // Process data incrementally
      processChunk(JSON.parse(chunk));
    }
    
    console.log('Stream completed');
  } catch (error) {
    console.error('Stream error:', error);
  }
}

// Cancelable streaming with progress
async function streamWithControl() {
  const stream = await api.stream('/large-dataset', {
    onProgress: (progress) => {
      console.log(`Downloaded: ${progress.loaded} bytes`);
    }
  });
  
  // Cancel after 5 seconds
  setTimeout(() => {
    stream.cancel();
    console.log('Stream cancelled');
  }, 5000);
  
  try {
    const fullText = await stream.text();
    console.log('Complete data:', fullText);
  } catch (error) {
    if (error.message.includes('cancelled')) {
      console.log('Stream was cancelled');
    }
  }
}
        

💡 Use Case: Perfect for large file downloads, real-time data feeds, or processing datasets that don't fit in memory.

GraphQL Support

Seamlessly integrate with GraphQL APIs using Apisto's dedicated GraphQL method.


// Basic GraphQL query
const api = new Apisto({
  baseURL: 'https://api.example.com/graphql'
});

async function fetchUserData() {
  try {
    const query = `
      query GetUser($id: ID!) {
        user(id: $id) {
          id
          name
          email
          posts {
            title
            content
          }
        }
      }
    `;
    
    const variables = { id: '123' };
    
    const response = await api.graphql(query, variables);
    const user = response.data.user;
    console.log('User data:', user);
    
  } catch (error) {
    console.error('GraphQL error:', error);
  }
}

// GraphQL with authentication
async function fetchProtectedData() {
  const mutation = `
    mutation CreatePost($title: String!, $content: String!) {
      createPost(title: $title, content: $content) {
        id
        title
        createdAt
      }
    }
  `;
  
  const variables = {
    title: 'My New Post',
    content: 'This is the content...'
  };
  
  // Set auth token first
  api.setAuthToken('your-jwt-token');
  
  const result = await api.graphql(mutation, variables, 'CreatePost', {
    headers: {
      'X-Custom-Header': 'value'
    }
  });
  
  console.log('Created post:', result.data.createPost);
}

// Handle GraphQL errors
async function handleGraphQLErrors() {
  try {
    const response = await api.graphql('query { invalidField }');
    
    if (response.errors) {
      console.error('GraphQL errors:', response.errors);
      // Handle specific GraphQL errors
      response.errors.forEach(error => {
        if (error.extensions?.code === 'UNAUTHENTICATED') {
          // Redirect to login
          window.location.href = '/login';
        }
      });
    }
    
  } catch (error) {
    console.error('Request failed:', error);
  }
}
        

Real-time Communication

Build real-time features with WebSocket and Server-Sent Events support.

🔌 WebSocket


const api = new Apisto({
  baseURL: 'https://api.example.com'
});

// Create WebSocket connection
const socket = api.createWebSocket('/realtime', ['protocol1', 'protocol2']);

socket.onopen = () => {
  console.log('WebSocket connected');
  socket.send(JSON.stringify({ type: 'subscribe', channel: 'updates' }));
};

socket.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log('Received:', data);
  updateUI(data);
};

socket.onclose = () => {
  console.log('WebSocket disconnected');
  // Attempt reconnection
  setTimeout(connectWebSocket, 5000);
};

socket.onerror = (error) => {
  console.error('WebSocket error:', error);
};

// Send message
function sendMessage(message) {
  if (socket.readyState === WebSocket.OPEN) {
    socket.send(JSON.stringify(message));
  }
}

// Close connection
function disconnect() {
  socket.close();
}
            

📡 Server-Sent Events


const api = new Apisto({
  baseURL: 'https://api.example.com'
});

// Create EventSource connection
const eventSource = api.createEventSource('/events', {
  withCredentials: true
});

eventSource.onopen = () => {
  console.log('EventSource connected');
};

eventSource.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log('Event received:', data);
  handleEvent(data);
};

eventSource.addEventListener('custom-event', (event) => {
  const data = JSON.parse(event.data);
  console.log('Custom event:', data);
});

eventSource.onerror = (error) => {
  console.error('EventSource error:', error);
  // EventSource will automatically attempt to reconnect
};

// Close connection
function closeEventSource() {
  eventSource.close();
}

// Reconnect manually
function reconnectEventSource() {
  eventSource.close();
  // Recreate connection
  setTimeout(() => {
    createEventSource();
  }, 1000);
}
            

Batch Requests

Execute multiple requests efficiently with controlled concurrency.


const api = new Apisto({
  baseURL: 'https://api.example.com',
  maxConcurrent: 5 // Control batch concurrency
});

async function loadDashboardData() {
  const requests = [
    { endpoint: '/users', options: { method: 'GET' } },
    { endpoint: '/posts', options: { method: 'GET' } },
    { endpoint: '/stats', options: { method: 'GET' } },
    { endpoint: '/notifications', options: { method: 'GET' } },
    { endpoint: '/settings', options: { method: 'GET' } }
  ];
  
  try {
    const results = await api.batch(requests, {
      concurrency: 3 // Override global concurrency
    });
    
    // Process results
    results.forEach((result, index) => {
      if (result.status === 'fulfilled') {
        console.log(`Request ${index} succeeded:`, result.value);
      } else {
        console.error(`Request ${index} failed:`, result.reason);
      }
    });
    
    // Extract successful results
    const successfulResults = results
      .filter(result => result.status === 'fulfilled')
      .map(result => result.value);
      
    return successfulResults;
    
  } catch (error) {
    console.error('Batch request failed:', error);
  }
}

// Batch with different HTTP methods
async function batchWithMixedMethods() {
  const requests = [
    { endpoint: '/users/1', options: { method: 'GET' } },
    { endpoint: '/users', options: { 
      method: 'POST', 
      body: { name: 'John', email: 'john@example.com' }
    }},
    { endpoint: '/users/2', options: { method: 'DELETE' } },
    { endpoint: '/users/3', options: { 
      method: 'PATCH', 
      body: { name: 'Jane Updated' }
    }}
  ];
  
  const results = await api.batch(requests);
  console.log('Mixed batch results:', results);
}

// Handle batch with error tolerance
async function tolerantBatch() {
  const requests = [
    { endpoint: '/data1', options: { method: 'GET' } },
    { endpoint: '/nonexistent', options: { method: 'GET' } }, // This will fail
    { endpoint: '/data2', options: { method: 'GET' } }
  ];
  
  const results = await api.batch(requests);
  
  const data = {};
  results.forEach((result, index) => {
    if (result.status === 'fulfilled') {
      data[`request${index}`] = result.value;
    } else {
      console.warn(`Request ${index} failed but continuing:`, result.reason);
      data[`request${index}`] = null;
    }
  });
  
  return data;
}
        

Advanced Error Handling

Sophisticated error handling patterns for production applications.


// Global error handler
api.addErrorInterceptor(async (error, options) => {
  // Log to monitoring service
  await logToMonitoringService({
    error: error.message,
    url: options.url,
    method: options.method,
    timestamp: new Date().toISOString(),
    userAgent: navigator.userAgent
  });
  
  // Handle specific error types
  if (error instanceof ApiError) {
    switch (error.status) {
      case 401:
        // Redirect to login
        window.location.href = '/login?redirect=' + encodeURIComponent(window.location.pathname);
        break;
      case 403:
        // Show access denied
        showNotification('Access denied', 'error');
        break;
      case 429:
        // Rate limited
        showNotification('Too many requests. Please wait.', 'warning');
        break;
      case 500:
        // Server error
        showNotification('Server error. Please try again later.', 'error');
        break;
    }
  }
  
  return error;
});

// Retry with custom conditions
async function fetchWithCustomRetry() {
  return api.request('/sensitive-data', {
    maxRetries: 5,
    retryCondition: (error) => {
      // Only retry on network errors and 5xx status codes
      return error.code === 'NETWORK_ERROR' || 
             (error.status >= 500 && error.status < 600);
    }
  });
}

// Circuit breaker integration
async function makeResilientRequest() {
  const circuitBreaker = api._getCircuitBreaker('/api');
  
  if (!circuitBreaker.canExecute()) {
    throw new ApistoError('Circuit breaker open', 'CIRCUIT_BREAKER_OPEN');
  }
  
  try {
    const result = await api.get('/api/data');
    circuitBreaker.onSuccess();
    return result;
  } catch (error) {
    circuitBreaker.onFailure();
    throw error;
  }
}

// Fallback strategies
async function fetchWithFallback() {
  try {
    return await api.get('/primary-endpoint');
  } catch (error) {
    console.warn('Primary endpoint failed, trying fallback:', error);
    
    try {
      return await api.get('/fallback-endpoint');
    } catch (fallbackError) {
      console.error('Fallback also failed:', fallbackError);
      // Return cached data or default values
      return getCachedData();
    }
  }
}
        

Performance Optimization

Optimize your API calls for better performance and user experience.


// Request deduplication
const pendingRequests = new Map();

async function deduplicatedRequest(endpoint, options = {}) {
  const key = `${endpoint}:${JSON.stringify(options)}`;
  
  if (pendingRequests.has(key)) {
    return pendingRequests.get(key);
  }
  
  const promise = api.request(endpoint, options).finally(() => {
    pendingRequests.delete(key);
  });
  
  pendingRequests.set(key, promise);
  return promise;
}

// Smart caching with validation
async function getCachedData(endpoint, validator) {
  const cacheKey = `cache:${endpoint}`;
  const cached = localStorage.getItem(cacheKey);
  
  if (cached) {
    const data = JSON.parse(cached);
    
    // Validate cache (e.g., check timestamp)
    if (validator(data)) {
      return data;
    }
  }
  
  // Fetch fresh data
  const freshData = await api.get(endpoint);
  
  // Cache with timestamp
  const cacheData = {
    data: freshData,
    timestamp: Date.now(),
    ttl: 5 * 60 * 1000 // 5 minutes
  };
  
  localStorage.setItem(cacheKey, JSON.stringify(cacheData));
  return freshData;
}

// Progressive loading with placeholders
async function loadProgressiveData() {
  // Show skeleton/shimmer immediately
  showLoadingState();
  
  try {
    // Request with low priority headers
    const data = await api.get('/data', {
      headers: {
        'Accept': 'application/json',
        'X-Priority': 'low' // Could be used by server for QoS
      }
    });
    
    updateUI(data);
    
  } catch (error) {
    showErrorState(error);
  } finally {
    hideLoadingState();
  }
}

// Prefetching and background sync
function prefetchData() {
  // Prefetch likely needed data
  const endpoints = ['/user/profile', '/notifications', '/dashboard/stats'];
  
  endpoints.forEach(endpoint => {
    api.get(endpoint, {
      priority: 'low',
      background: true // Custom option for background requests
    }).catch(() => {
      // Silently fail for background prefetch
    });
  });
}

// Lazy loading with intersection observer
function setupLazyLoading() {
  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        const endpoint = entry.target.dataset.endpoint;
        loadLazyData(endpoint, entry.target);
        observer.unobserve(entry.target);
      }
    });
  });
  
  document.querySelectorAll('[data-lazy-endpoint]').forEach(el => {
    observer.observe(el);
  });
}
        

Ready to extend Apisto?

Learn how to create custom plugins to extend Apisto's functionality.

Continue to Plugin System →