# API Integration - Quick Reference Card ## Essential Files | File | Purpose | Key Methods | |------|---------|-------------| | `api_constants.dart` | API config | `baseUrl`, endpoints, timeouts | | `dio_client.dart` | HTTP client | `get()`, `post()`, `put()`, `delete()` | | `network_info.dart` | Connectivity | `isConnected`, `onConnectivityChanged` | | `exceptions.dart` | Error types | 20+ exception classes | | `failures.dart` | Domain errors | Failure classes for UI | | `product_remote_datasource.dart` | Product API | `fetchProducts()`, `searchProducts()` | | `category_remote_datasource.dart` | Category API | `fetchCategories()` | ## Quick Setup (5 Steps) ```dart // 1. In main.dart - Initialize dependencies import 'core/di/injection_container.dart' as di; void main() async { WidgetsFlutterBinding.ensureInitialized(); await di.initDependencies(); runApp(const MyApp()); } // 2. Configure API URL in api_constants.dart static const String baseUrl = 'https://your-api.com'; // 3. Enable mock data for testing (optional) static const bool useMockData = true; // 4. Get data source from service locator final productDS = sl(); // 5. Fetch data final products = await productDS.fetchProducts(); ``` ## Common Operations ### Fetch All Products ```dart final dataSource = sl(); final products = await dataSource.fetchProducts(); ``` ### Search Products ```dart final results = await dataSource.searchProducts('laptop'); ``` ### Fetch by Category ```dart final products = await dataSource.fetchProductsByCategory('electronics'); ``` ### Fetch Single Product ```dart final product = await dataSource.fetchProductById('123'); ``` ### Fetch Categories ```dart final categoryDS = sl(); final categories = await categoryDS.fetchCategories(); ``` ### Check Connectivity ```dart final networkInfo = sl(); final isConnected = await networkInfo.isConnected; ``` ## Error Handling Patterns ### Basic Try-Catch ```dart try { final products = await dataSource.fetchProducts(); // Use products } on NoInternetException { // Show "No internet" message } on ServerException catch (e) { // Show "Server error: ${e.message}" } on NetworkException catch (e) { // Show "Network error: ${e.message}" } ``` ### With Network Check ```dart final networkInfo = sl(); if (!await networkInfo.isConnected) { // Load from cache or show offline UI return; } try { final products = await dataSource.fetchProducts(); // Use products } catch (e) { // Handle error } ``` ### Repository Pattern (Recommended) ```dart Future>> getProducts() async { if (!await networkInfo.isConnected) { return Left(NoInternetFailure()); } try { final products = await remoteDataSource.fetchProducts(); return Right(products); } on ServerException catch (e) { return Left(ServerFailure(e.message, e.statusCode)); } on NetworkException catch (e) { return Left(NetworkFailure(e.message)); } } ``` ## Exception → Failure Mapping | Exception | Failure | UI Message | |-----------|---------|------------| | `NoInternetException` | `NoInternetFailure` | "No internet connection" | | `TimeoutException` | `TimeoutFailure` | "Request timeout" | | `ServerException (500+)` | `ServerFailure` | "Server error" | | `UnauthorizedException (401)` | `UnauthorizedFailure` | "Please login" | | `NotFoundException (404)` | `NotFoundFailure` | "Not found" | | `ValidationException (422)` | `ValidationFailure` | "Invalid data" | ## Configuration Options ### API Constants ```dart // In api_constants.dart // Base URL static const String baseUrl = 'https://api.example.com'; // Timeouts (milliseconds) static const int connectTimeout = 30000; static const int receiveTimeout = 30000; // Retry static const int maxRetries = 3; static const int retryDelay = 1000; // Mock data toggle static const bool useMockData = false; ``` ### DioClient Headers ```dart final dioClient = DioClient(); // Add auth token dioClient.updateAuthToken('your-jwt-token'); // Add custom header dioClient.addHeader('X-Custom', 'value'); // Remove auth token dioClient.removeAuthToken(); ``` ## Riverpod Integration ### Provider Setup ```dart @riverpod class Products extends _$Products { @override Future> build() async { final dataSource = sl(); return await dataSource.fetchProducts(); } Future refresh() async { state = const AsyncValue.loading(); state = await AsyncValue.guard(() async { final dataSource = sl(); return await dataSource.fetchProducts(); }); } } ``` ### UI Usage ```dart Consumer( builder: (context, ref, child) { final productsAsync = ref.watch(productsProvider); return productsAsync.when( data: (products) => ProductList(products), loading: () => CircularProgressIndicator(), error: (error, stack) => ErrorWidget(error), ); }, ) ``` ## API Endpoints Reference ### Products | Method | Endpoint | Purpose | |--------|----------|---------| | GET | `/products` | Get all products | | GET | `/products/:id` | Get single product | | GET | `/products/category/:id` | Get by category | | GET | `/products/search?q=query` | Search products | | POST | `/products/sync` | Bulk sync | ### Categories | Method | Endpoint | Purpose | |--------|----------|---------| | GET | `/categories` | Get all categories | | GET | `/categories/:id` | Get single category | | POST | `/categories/sync` | Bulk sync | ## Expected Response Formats ### Products List ```json { "products": [ { "id": "1", "name": "Product Name", "price": 29.99, "categoryId": "cat1", "stockQuantity": 100, "isAvailable": true, "createdAt": "2024-01-01T00:00:00Z", "updatedAt": "2024-01-01T00:00:00Z" } ] } ``` ### Categories List ```json { "categories": [ { "id": "1", "name": "Electronics", "productCount": 25, "createdAt": "2024-01-01T00:00:00Z" } ] } ``` ### Error Response ```json { "message": "Error message", "error": "Detailed error" } ``` ## Testing Quick Start ### Use Mock Data Source ```dart // In api_constants.dart static const bool useMockData = true; // Or directly final mockDS = ProductRemoteDataSourceMock(); final products = await mockDS.fetchProducts(); ``` ### Mock Network Status ```dart final mockNetwork = NetworkInfoMock(isConnected: false); final isConnected = await mockNetwork.isConnected; // false ``` ## Common Issues & Solutions | Issue | Solution | |-------|----------| | Connection timeout | Increase timeout in `api_constants.dart` | | SSL certificate error | Configure `badCertificateCallback` (dev only) | | 401 Unauthorized | Update auth token with `updateAuthToken()` | | Mock data not working | Check `useMockData = true` | | Parsing error | Verify response format matches model | ## Performance Tips 1. **Enable caching**: Use Hive to cache API responses 2. **Pagination**: Request data in chunks (20-50 items) 3. **Debounce search**: Wait 300ms before searching 4. **Image optimization**: Use `cached_network_image` 5. **Background sync**: Sync during app idle time 6. **Cancel requests**: Use `CancelToken` for expensive operations ## Debug Logging Logs are automatically generated: ``` REQUEST[GET] => PATH: /products Headers: {Content-Type: application/json} RESPONSE[200] => PATH: /products Data: {...} ``` Disable in production by removing `LoggingInterceptor`. ## Security Checklist - [ ] Use HTTPS (not HTTP) - [ ] Store API keys securely (use `flutter_secure_storage`) - [ ] Implement token refresh for expired tokens - [ ] Validate SSL certificates in production - [ ] Don't log sensitive data in production - [ ] Use environment variables for API URLs - [ ] Implement rate limiting on client side ## Next Steps 1. ✅ API integration complete 2. → Create Repository layer 3. → Implement Use Cases 4. → Wire up Riverpod Providers 5. → Build UI with error handling 6. → Add offline sync 7. → Implement authentication 8. → Add unit tests --- **Quick Links:** - Full Guide: `API_INTEGRATION_GUIDE.md` - Architecture: `API_ARCHITECTURE.md` - Examples: `examples/api_usage_example.dart` **Status**: ✅ Ready to Use