Extend Apisto's functionality with custom plugins and built-in extensions
Apisto's plugin system allows you to extend its functionality with custom behaviors, interceptors, and utilities. Plugins can modify requests, handle responses, add caching, logging, and much more.
🔌 Powerful Extensibility: Plugins can hook into Apisto's request lifecycle at multiple points, giving you complete control over API interactions.
Apisto comes with several useful built-in plugins that you can enable with a single line of code.
Automatically logs all requests and responses for debugging and monitoring.
// Enable logger plugin
const api = new Apisto({
baseURL: 'https://api.example.com',
enableLogging: true // Enabled by default
});
// Or manually register
api.use({
name: 'custom-logger',
install(apisto) {
apisto.addRequestInterceptor(async (options, url) => {
console.log(`📤 [${options.method}] ${url}`, options);
return options;
});
apisto.addResponseInterceptor(async (response, options) => {
console.log(`📥 [${response.status}] ${options.url}`);
return response;
});
apisto.addErrorInterceptor(async (error, options) => {
console.error(`❌ [ERROR] ${options.method} ${options.url}`, error);
return error;
});
}
});
Automatic caching of GET requests with configurable TTL and cache invalidation.
// Enable cache plugin
const api = new Apisto({
baseURL: 'https://api.example.com',
enableCache: true, // Enabled by default
cacheTTL: 300000 // 5 minutes
});
// Usage with cache control
async function fetchData() {
// This request will be cached
const data = await api.get('/users', {
cacheTTL: 60000 // Override global TTL
});
// Skip cache for this request
const freshData = await api.get('/users', {
skipCache: true
});
return data;
}
// Manual cache management
function clearCache() {
// Custom cache clearing logic
const cacheKeys = Object.keys(localStorage)
.filter(key => key.startsWith('apisto-cache-'));
cacheKeys.forEach(key => localStorage.removeItem(key));
}
Build your own plugins to add custom functionality to Apisto. Plugins can implement various lifecycle hooks.
// Basic plugin structure
const MyCustomPlugin = {
name: 'my-custom-plugin',
// Required: install method
install(apisto) {
console.log('MyCustomPlugin installed!');
// Add request interceptor
apisto.addRequestInterceptor(async (options, url) => {
// Add custom header to all requests
options.headers['X-My-Custom-Header'] = 'plugin-value';
return options;
});
// Add response interceptor
apisto.addResponseInterceptor(async (response, options) => {
// Process all responses
const data = await response.clone().json();
// Add custom processing
if (data.metadata) {
data.processedByPlugin = true;
data.processedAt = new Date().toISOString();
}
// Return new response with processed data
return new Response(JSON.stringify(data), {
status: response.status,
headers: response.headers
});
});
// Add error interceptor
apisto.addErrorInterceptor(async (error, options) => {
// Custom error handling
if (error.status === 429) {
// Implement custom retry logic for rate limits
console.warn('Rate limited, implementing custom backoff...');
await new Promise(resolve => setTimeout(resolve, 5000));
return apisto.request(options.url, options);
}
return error;
});
// Add custom methods to apisto instance
apisto.customMethod = function() {
console.log('Custom method called!');
return this;
};
}
};
// Register the plugin
const api = new Apisto({
baseURL: 'https://api.example.com'
});
api.use(MyCustomPlugin);
// Now you can use the custom method
api.customMethod();
Real-world plugin examples for common use cases.
const AuthPlugin = {
name: 'auth-plugin',
install(apisto) {
let authToken = null;
let refreshToken = null;
let refreshPromise = null;
// Store auth methods
apisto.setAuthToken = function(token) {
authToken = token;
return this;
};
apisto.setRefreshToken = function(token) {
refreshToken = token;
return this;
};
// Add auth header to all requests
apisto.addRequestInterceptor(async (options) => {
if (authToken) {
options.headers.Authorization = `Bearer ${authToken}`;
}
return options;
});
// Handle token refresh on 401
apisto.addErrorInterceptor(async (error, options) => {
if (error.status === 401 && refreshToken && !options._retry) {
if (!refreshPromise) {
refreshPromise = refreshAuthToken(refreshToken)
.finally(() => { refreshPromise = null; });
}
const newTokens = await refreshPromise;
authToken = newTokens.accessToken;
refreshToken = newTokens.refreshToken;
// Retry original request with new token
options._retry = true;
options.headers.Authorization = `Bearer ${authToken}`;
return apisto.request(options.url, options);
}
return error;
});
async function refreshAuthToken(token) {
const response = await fetch('/auth/refresh', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refreshToken: token })
});
if (!response.ok) {
throw new Error('Token refresh failed');
}
return response.json();
}
}
};
const AnalyticsPlugin = {
name: 'analytics-plugin',
install(apisto) {
const metrics = {
totalRequests: 0,
successfulRequests: 0,
failedRequests: 0,
averageResponseTime: 0
};
const requestTimers = new Map();
// Track request start
apisto.addRequestInterceptor(async (options, url) => {
requestTimers.set(url, performance.now());
metrics.totalRequests++;
return options;
});
// Track response success and timing
apisto.addResponseInterceptor(async (response, options) => {
const startTime = requestTimers.get(options.url);
if (startTime) {
const duration = performance.now() - startTime;
requestTimers.delete(options.url);
metrics.successfulRequests++;
metrics.averageResponseTime =
(metrics.averageResponseTime * (metrics.successfulRequests - 1) + duration) /
metrics.successfulRequests;
// Send to analytics service
sendAnalytics('request_success', {
url: options.url,
method: options.method,
duration,
status: response.status
});
}
return response;
});
// Track errors
apisto.addErrorInterceptor(async (error, options) => {
metrics.failedRequests++;
sendAnalytics('request_error', {
url: options.url,
method: options.method,
error: error.message,
status: error.status
});
return error;
});
// Expose metrics
apisto.getMetrics = function() {
return { ...metrics };
};
async function sendAnalytics(event, data) {
// Send to your analytics service
try {
await fetch('/api/analytics', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ event, data, timestamp: new Date().toISOString() })
});
} catch (error) {
console.warn('Analytics send failed:', error);
}
}
}
};
Configure plugins with options and handle plugin dependencies.
// Plugin with configuration options
const ConfigurablePlugin = {
name: 'configurable-plugin',
install(apisto, options = {}) {
const {
enabled = true,
logLevel = 'info',
maxRetries = 3,
customHeader = null
} = options;
if (!enabled) {
console.log('ConfigurablePlugin is disabled');
return;
}
// Store plugin instance for later access
const pluginInstance = {
options,
log(message, level = 'info') {
if (level === logLevel || logLevel === 'debug') {
console.log(`[${this.name}] ${message}`);
}
}
};
apisto.plugins[this.name] = pluginInstance;
// Add custom header if specified
if (customHeader) {
apisto.addRequestInterceptor(async (options) => {
options.headers[customHeader.name] = customHeader.value;
return options;
});
}
// Add retry logic
apisto.addErrorInterceptor(async (error, requestOptions) => {
if (error.status >= 500 && maxRetries > 0) {
const retryCount = requestOptions._retryCount || 0;
if (retryCount < maxRetries) {
pluginInstance.log(`Retrying request (${retryCount + 1}/${maxRetries})`);
requestOptions._retryCount = retryCount + 1;
await new Promise(resolve =>
setTimeout(resolve, Math.pow(2, retryCount) * 1000)
);
return apisto.request(requestOptions.url, requestOptions);
}
}
return error;
});
pluginInstance.log('Plugin installed successfully');
}
};
// Usage with configuration
const api = new Apisto({
baseURL: 'https://api.example.com'
});
api.use(ConfigurablePlugin, {
enabled: true,
logLevel: 'debug',
maxRetries: 5,
customHeader: {
name: 'X-Plugin-Enabled',
value: 'true'
}
});
Understand when and how plugins are executed during Apisto's lifecycle.
const LifecyclePlugin = {
name: 'lifecycle-plugin',
// Called when plugin is registered
install(apisto) {
console.log('📦 Plugin installed');
// Store reference to apisto instance
this.apisto = apisto;
// Request phase
apisto.addRequestInterceptor(async (options, url) => {
console.log('🚀 Request starting:', url);
options._startTime = Date.now();
return options;
}, 'request-start');
// Response phase
apisto.addResponseInterceptor(async (response, options) => {
const duration = Date.now() - options._startTime;
console.log(`✅ Request completed in ${duration}ms`);
return response;
}, 'response-success');
// Error phase
apisto.addErrorInterceptor(async (error, options) => {
console.log('❌ Request failed:', error.message);
return error;
}, 'response-error');
},
// Optional: Called when plugin is unregistered
uninstall() {
console.log('🗑️ Plugin uninstalled');
// Clean up any event listeners, timers, etc.
if (this.apisto) {
// Remove interceptors, etc.
}
},
// Optional: Called before request is sent
beforeRequest(options) {
console.log('🔧 Before request:', options);
return options;
},
// Optional: Called after response is received
afterResponse(response, options) {
console.log('🔧 After response:', response.status);
return response;
}
};
// Plugin with multiple interceptors at different phases
const MultiPhasePlugin = {
name: 'multi-phase-plugin',
install(apisto) {
// Early request phase - modify headers
apisto.addRequestInterceptor(async (options) => {
options.headers['X-Phase'] = 'early';
return options;
}, 'early');
// Late request phase - add timestamps
apisto.addRequestInterceptor(async (options) => {
options.headers['X-Timestamp'] = Date.now();
return options;
}, 'late');
// Early response phase - log response
apisto.addResponseInterceptor(async (response) => {
console.log('Early response processing');
return response;
}, 'early');
// Late response phase - transform data
apisto.addResponseInterceptor(async (response) => {
const data = await response.clone().json();
data.processedAt = new Date().toISOString();
return new Response(JSON.stringify(data), response);
}, 'late');
}
};
Discover popular community plugins and how to use them.
Advanced retry logic with exponential backoff and custom retry conditions.
import { RetryPlugin } from 'apisto-retry';
api.use(RetryPlugin, {
maxRetries: 3,
backoff: 'exponential'
});
Comprehensive metrics collection and performance monitoring.
import { MetricsPlugin } from 'apisto-metrics';
api.use(MetricsPlugin, {
sendTo: 'https://metrics.example.com'
});
Complete authentication flow with token refresh and storage.
import { AuthPlugin } from 'apisto-auth';
api.use(AuthPlugin, {
storage: 'localStorage',
refreshEndpoint: '/auth/refresh'
});
Offline support with request queuing and automatic sync when online.
import { OfflinePlugin } from 'apisto-offline';
api.use(OfflinePlugin, {
queueSize: 100,
syncOnReconnect: true
});
You've completed the Apisto tutorial! Start building amazing applications with confidence.