apix 1.0.0 copy "apix: ^1.0.0" to clipboard
apix: ^1.0.0 copied to clipboard

Production-ready Flutter/Dart API client with auth refresh queue, exponential retry, smart caching and built-in Sentry. Powered by Dio.

[Apix Logo]

ApiX

pub package CI coverage License: MIT

🚀 Production-ready Flutter/Dart API client — auth refresh queue, exponential retry with backoff, smart & flexible caching, and built-in Sentry. Zero boilerplate, maximum reliability. Powered by Dio.

Quick Start #

import 'package:apix/apix.dart';

final client = ApiClientFactory.create(baseUrl: 'https://api.example.com');
final response = await client.get('/users');

That's it! You have a fully configured API client with sensible defaults.

Features #

Feature Description
🔐 Auth Refresh Queue Automatic token refresh with request queuing
Secure Token Storage Built-in secure storage with flutter_secure_storage
�🔄 Retry Logic Exponential backoff with jitter
💾 Smart Caching CacheFirst, NetworkFirst, HttpCacheAware strategies
📊 Logging Configurable request/response logging
🐛 Sentry Integration Built-in error tracking and breadcrumbs
📈 Metrics Request timing and performance tracking

Installation #

Add to your pubspec.yaml:

dependencies:
  apix: ^0.0.1

Then run:

flutter pub get

Usage #

Basic Client #

import 'package:apix/apix.dart';

// Create a simple client
final client = ApiClientFactory.create(
  baseUrl: 'https://api.example.com',
);

// GET request
final users = await client.get('/users');

// POST request
final newUser = await client.post('/users', data: {
  'name': 'John Doe',
  'email': 'john@example.com',
});

Using SecureTokenProvider for zero-boilerplate secure token management:

final tokenProvider = SecureTokenProvider();

final client = ApiClientFactory.create(
  baseUrl: 'https://api.example.com',
  authConfig: AuthConfig(
    tokenProvider: tokenProvider,
    refreshEndpoint: '/auth/refresh',
    onTokenRefreshed: (response) async {
      final data = response.data;
      await tokenProvider.saveTokens(
        data['access_token'],
        data['refresh_token'],
      );
    },
  ),
);

// After login, save tokens
await tokenProvider.saveTokens(accessToken, refreshToken);

// On logout, clear tokens
await tokenProvider.clearTokens();

Secure Token Storage #

SecureTokenProvider uses flutter_secure_storage under the hood with secure defaults:

// Basic usage - tokens stored securely
final tokenProvider = SecureTokenProvider();

// Custom storage keys
final tokenProvider = SecureTokenProvider(
  accessTokenKey: 'my_access_token',
  refreshTokenKey: 'my_refresh_token',
);

// Share storage with other secrets
final storage = SecureStorageService();
final tokenProvider = SecureTokenProvider(storage: storage);

// Store other secrets using the same storage
await storage.write('firebase_token', firebaseToken);
await storage.write('api_key', apiKey);

With Retry #

final client = ApiClientFactory.create(
  baseUrl: 'https://api.example.com',
  retryConfig: RetryConfig(
    maxRetries: 3,
    retryableStatusCodes: {408, 429, 500, 502, 503, 504},
    initialDelay: Duration(milliseconds: 500),
    maxDelay: Duration(seconds: 30),
  ),
);

// Disable retry for specific request
final response = await client.get(
  '/critical-endpoint',
  options: Options(extra: {'noRetry': true}),
);

With Caching #

final client = ApiClientFactory.create(
  baseUrl: 'https://api.example.com',
  cacheConfig: CacheConfig(
    defaultStrategy: CacheStrategy.networkFirst,
    defaultTtl: Duration(minutes: 5),
  ),
);

// Use cache-first for static data
final config = await client.get(
  '/app-config',
  options: Options(extra: {
    'cacheStrategy': CacheStrategy.cacheFirst,
    'cacheTtl': Duration(hours: 24),
  }),
);

// Force refresh
final freshData = await client.get(
  '/users',
  options: Options(extra: {'forceRefresh': true}),
);

With Logging #

final client = ApiClientFactory.create(
  baseUrl: 'https://api.example.com',
  loggerConfig: LoggerConfig(
    level: LogLevel.info,
    logRequestHeaders: true,
    logResponseBody: true,
    redactedHeaders: ['Authorization', 'Cookie'],
  ),
);

With Sentry #

import 'package:sentry_flutter/sentry_flutter.dart';

void main() async {
  // Optional: ensures Flutter errors are captured before runApp
  SentryWidgetsFlutterBinding.ensureInitialized();

  await SentrySetup.init(
    options: SentrySetupOptions(
      dsn: 'your-sentry-dsn',
      environment: 'production',
    ),
    appRunner: () async {
      runApp(const MyApp());
    },
  );
}

// In your app
final client = ApiClientFactory.create(
  baseUrl: 'https://api.example.com',
);

// Add Sentry interceptor (environment already set in SentrySetup.init)
client.interceptors.add(SentryInterceptor(
  config: SentryConfig(
    captureException: (e, {stackTrace, extra, tags}) async {
      await Sentry.captureException(e, stackTrace: stackTrace);
    },
    addBreadcrumb: (data) {
      Sentry.addBreadcrumb(Breadcrumb(
        message: data['message'] as String?,
        category: data['category'] as String?,
        data: data['data'] as Map<String, dynamic>?,
      ));
    },
  ),
));

With Metrics #

client.interceptors.add(MetricsInterceptor(
  config: MetricsConfig(
    onMetrics: (metrics) {
      analytics.track('api_request', {
        'method': metrics.method,
        'path': metrics.path,
        'duration_ms': metrics.durationMs,
        'status_code': metrics.statusCode,
        'success': metrics.success,
      });
    },
    onBreadcrumb: (breadcrumb) {
      // Track navigation breadcrumbs
    },
  ),
));

Full Configuration #

final client = ApiClientFactory.create(
  baseUrl: 'https://api.example.com',
  config: ApiClientConfig(
    connectTimeout: Duration(seconds: 30),
    receiveTimeout: Duration(seconds: 30),
    defaultHeaders: {
      'X-App-Version': '1.0.0',
      'X-Platform': Platform.operatingSystem,
    },
  ),
  authConfig: AuthConfig(...),
  retryConfig: RetryConfig(...),
  cacheConfig: CacheConfig(...),
  loggerConfig: LoggerConfig(...),
);

Result Type #

Apix provides a Result<T> type for safe error handling:

final result = await client.getResult<User>('/users/1');

result.when(
  success: (user) => print('Got user: ${user.name}'),
  failure: (error) => print('Error: ${error.message}'),
);

// Or use pattern matching
if (result.isSuccess) {
  final user = result.data;
}

Error Handling #

try {
  final response = await client.get('/users');
} on HttpException catch (e) {
  print('HTTP ${e.statusCode}: ${e.message}');
} on NetworkException catch (e) {
  print('Network error: ${e.message}');
} on ApiException catch (e) {
  print('API error: ${e.message}');
}

API Reference #

Interceptors #

Interceptor Description
AuthInterceptor Handles token injection and refresh
RetryInterceptor Retries failed requests with backoff
CacheInterceptor Caches responses with configurable strategies
LoggerInterceptor Logs requests and responses
SentryInterceptor Captures errors and breadcrumbs
MetricsInterceptor Tracks request metrics

Cache Strategies #

Strategy Description
CacheStrategy.cacheFirst Return cache, fetch in background
CacheStrategy.networkFirst Try network, fallback to cache
CacheStrategy.httpCacheAware Respect HTTP cache headers

Log Levels #

Level Description
LogLevel.none No logging
LogLevel.error Errors only
LogLevel.warn Warnings and errors
LogLevel.info Info, warnings, errors
LogLevel.trace Everything

Example App #

A complete Flutter app demonstrating all ApiX features is available on GitHub:

👉 apix_example_app

[ApiX Example App]

Features demonstrated:

  • 🔐 SecureTokenProvider with simplified refresh flow
  • 💾 Cache strategies (CacheFirst, NetworkFirst, HttpCache)
  • 🔄 Retry logic with exponential backoff
  • 🐛 Sentry integration with error testing
  • 📊 Request metrics and logging

Contributing #

Contributions are welcome! Please read our contributing guidelines first.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'feat: add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License #

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments #

  • Built on top of Dio
  • Inspired by best practices from production Flutter apps

Made with ❤️ by Germinator

2
likes
0
points
156
downloads

Publisher

unverified uploader

Weekly Downloads

Production-ready Flutter/Dart API client with auth refresh queue, exponential retry, smart caching and built-in Sentry. Powered by Dio.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

crypto, dio, flutter, flutter_secure_storage, sentry_flutter

More

Packages that depend on apix