# API Integration Guide - Retail POS App ## Overview This guide provides comprehensive documentation for the API integration layer of the Retail POS application. The integration is built using **Dio** for HTTP requests with a robust error handling, retry logic, and offline-first architecture. --- ## Architecture ### Layered Architecture ``` Presentation Layer (UI) “ Providers (Riverpod) “ Use Cases “ Repositories “ Data Sources (Remote + Local) “ Network Layer (Dio) ``` --- ## Components ### 1. DioClient (`/lib/core/network/dio_client.dart`) **Purpose**: Centralized HTTP client configuration with Dio. **Features**: - Base URL and timeout configuration - Request/response interceptors - Authentication header management - Automatic retry logic - Error handling and conversion to custom exceptions **Usage Example**: ```dart final dioClient = DioClient(); // GET request final response = await dioClient.get('/products'); // POST request final response = await dioClient.post( '/products', data: {'name': 'Product Name', 'price': 29.99}, ); // With query parameters final response = await dioClient.get( '/products/search', queryParameters: {'q': 'laptop'}, ); ``` **Methods**: - `get()` - GET requests - `post()` - POST requests - `put()` - PUT requests - `delete()` - DELETE requests - `patch()` - PATCH requests - `download()` - File downloads - `updateAuthToken()` - Update authentication token - `removeAuthToken()` - Remove authentication token - `addHeader()` - Add custom header - `removeHeader()` - Remove custom header --- ### 2. API Constants (`/lib/core/constants/api_constants.dart`) **Purpose**: Centralized configuration for API endpoints, timeouts, and settings. **Configuration**: ```dart // Base URL static const String baseUrl = 'https://api.retailpos.example.com'; static const String apiVersion = '/api/v1'; // Timeouts (in milliseconds) static const int connectTimeout = 30000; static const int receiveTimeout = 30000; static const int sendTimeout = 30000; // Retry configuration static const int maxRetries = 3; static const int retryDelay = 1000; ``` **Endpoints**: ```dart // Products ApiConstants.products // GET /products ApiConstants.productById('123') // GET /products/123 ApiConstants.productsByCategory('cat1') // GET /products/category/cat1 ApiConstants.searchProducts // GET /products/search?q=query ApiConstants.syncProducts // POST /products/sync // Categories ApiConstants.categories // GET /categories ApiConstants.categoryById('123') // GET /categories/123 ApiConstants.syncCategories // POST /categories/sync ``` --- ### 3. Network Info (`/lib/core/network/network_info.dart`) **Purpose**: Check network connectivity status using `connectivity_plus`. **Features**: - Check current connectivity status - Stream of connectivity changes - Check connection type (WiFi, Mobile, etc.) **Usage Example**: ```dart final networkInfo = NetworkInfoImpl(Connectivity()); // Check if connected final isConnected = await networkInfo.isConnected; // Listen to connectivity changes networkInfo.onConnectivityChanged.listen((isConnected) { if (isConnected) { print('Device is online'); } else { print('Device is offline'); } }); // Check connection type final isWiFi = await networkInfo.isConnectedViaWiFi; final isMobile = await networkInfo.isConnectedViaMobile; ``` --- ### 4. API Interceptors (`/lib/core/network/api_interceptor.dart`) #### Logging Interceptor Logs all requests and responses for debugging. ```dart REQUEST[GET] => PATH: /products Headers: {Content-Type: application/json} RESPONSE[200] => PATH: /products Data: {...} ``` #### Authentication Interceptor Automatically adds authentication headers to requests. ```dart // Set auth token authInterceptor.setAuthToken('your-jwt-token'); // All requests now include: // Authorization: Bearer your-jwt-token // Clear token authInterceptor.clearAuthToken(); ``` #### Error Interceptor Converts HTTP status codes to custom exceptions. **Status Code Mapping**: - `400` ’ `BadRequestException` - `401` ’ `UnauthorizedException` - `403` ’ `ForbiddenException` - `404` ’ `NotFoundException` - `422` ’ `ValidationException` - `429` ’ `RateLimitException` - `500+` ’ `ServerException` - `503` ’ `ServiceUnavailableException` #### Retry Interceptor Automatically retries failed requests. **Retry Conditions**: - Connection timeout - Send/receive timeout - Connection errors - HTTP 408, 429, 502, 503, 504 **Retry Strategy**: - Max retries: 3 (configurable) - Exponential backoff: delay * (retry_count + 1) - Default delay: 1000ms --- ### 5. Custom Exceptions (`/lib/core/errors/exceptions.dart`) **Network Exceptions**: - `NoInternetException` - No internet connection - `TimeoutException` - Request timeout - `ConnectionException` - Connection failed - `NetworkException` - General network error **Server Exceptions**: - `ServerException` - Server error (500+) - `ServiceUnavailableException` - Service unavailable (503) **Client Exceptions**: - `BadRequestException` - Invalid request (400) - `UnauthorizedException` - Authentication required (401) - `ForbiddenException` - Access forbidden (403) - `NotFoundException` - Resource not found (404) - `ValidationException` - Validation failed (422) - `RateLimitException` - Rate limit exceeded (429) **Cache Exceptions**: - `CacheException` - Cache operation failed - `CacheNotFoundException` - Data not found in cache **Data Exceptions**: - `DataParsingException` - Failed to parse data - `InvalidDataFormatException` - Invalid data format **Business Exceptions**: - `OutOfStockException` - Product out of stock - `InsufficientStockException` - Insufficient stock - `TransactionException` - Transaction failed - `PaymentException` - Payment processing failed --- ### 6. Failure Classes (`/lib/core/errors/failures.dart`) Failures are used in the domain/presentation layer to represent errors without throwing exceptions. **Network Failures**: - `NoInternetFailure` - `ConnectionFailure` - `TimeoutFailure` - `NetworkFailure` **Server Failures**: - `ServerFailure` - `ServiceUnavailableFailure` **Client Failures**: - `BadRequestFailure` - `UnauthorizedFailure` - `ForbiddenFailure` - `NotFoundFailure` - `ValidationFailure` - `RateLimitFailure` **Usage Pattern**: ```dart // In repository try { final products = await remoteDataSource.fetchProducts(); return Right(products); } on NoInternetException { return Left(NoInternetFailure()); } on ServerException catch (e) { return Left(ServerFailure(e.message, e.statusCode)); } catch (e) { return Left(NetworkFailure('Unexpected error: ${e.toString()}')); } ``` --- ### 7. Remote Data Sources #### Product Remote Data Source (`/lib/features/products/data/datasources/product_remote_datasource.dart`) **Methods**: ```dart // Fetch all products Future> fetchProducts(); // Fetch single product Future fetchProductById(String id); // Fetch products by category Future> fetchProductsByCategory(String categoryId); // Search products Future> searchProducts(String query); // Sync products Future syncProducts(List products); ``` **Usage Example**: ```dart final dataSource = ProductRemoteDataSourceImpl(dioClient); // Fetch all products final products = await dataSource.fetchProducts(); // Search products final results = await dataSource.searchProducts('laptop'); // Fetch by category final categoryProducts = await dataSource.fetchProductsByCategory('electronics'); ``` #### Category Remote Data Source (`/lib/features/categories/data/datasources/category_remote_datasource.dart`) **Methods**: ```dart // Fetch all categories Future> fetchCategories(); // Fetch single category Future fetchCategoryById(String id); // Sync categories Future syncCategories(List categories); ``` --- ## Setup & Installation ### 1. Dependencies Already added to `pubspec.yaml`: ```yaml dependencies: dio: ^5.7.0 connectivity_plus: ^6.1.1 equatable: ^2.0.7 get_it: ^8.0.4 ``` ### 2. Initialize Dependencies In `main.dart`: ```dart import 'package:flutter/material.dart'; import 'core/di/injection_container.dart' as di; void main() async { WidgetsFlutterBinding.ensureInitialized(); // Initialize dependencies await di.initDependencies(); runApp(const MyApp()); } ``` ### 3. Configure API Base URL Update in `/lib/core/constants/api_constants.dart`: ```dart static const String baseUrl = 'https://your-api-url.com'; ``` ### 4. Using Mock Data (Development) Enable mock data in `/lib/core/constants/api_constants.dart`: ```dart static const bool useMockData = true; ``` --- ## Usage Examples ### Example 1: Fetch Products in a Repository ```dart import 'package:dartz/dartz.dart'; import '../../../core/errors/exceptions.dart'; import '../../../core/errors/failures.dart'; import '../../domain/repositories/product_repository.dart'; import '../datasources/product_remote_datasource.dart'; import '../models/product_model.dart'; class ProductRepositoryImpl implements ProductRepository { final ProductRemoteDataSource remoteDataSource; final NetworkInfo networkInfo; ProductRepositoryImpl({ required this.remoteDataSource, required this.networkInfo, }); @override Future>> getProducts() async { // Check network connectivity 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 TimeoutException { return Left(TimeoutFailure()); } on NetworkException catch (e) { return Left(NetworkFailure(e.message, e.statusCode)); } catch (e) { return Left(NetworkFailure('Unexpected error: ${e.toString()}')); } } } ``` ### Example 2: Using in a Riverpod Provider ```dart import 'package:riverpod_annotation/riverpod_annotation.dart'; import '../../../core/di/injection_container.dart'; import '../../data/datasources/product_remote_datasource.dart'; import '../../data/models/product_model.dart'; part 'products_provider.g.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(); }); } } ``` ### Example 3: Handling Errors in UI ```dart // In your widget Consumer( builder: (context, ref, child) { final productsAsync = ref.watch(productsProvider); return productsAsync.when( data: (products) => ProductGrid(products: products), loading: () => const CircularProgressIndicator(), error: (error, stack) { // Handle different error types if (error is NoInternetFailure) { return ErrorWidget( message: 'No internet connection', onRetry: () => ref.refresh(productsProvider), ); } else if (error is ServerFailure) { return ErrorWidget( message: 'Server error. Please try again later', onRetry: () => ref.refresh(productsProvider), ); } return ErrorWidget( message: 'An error occurred', onRetry: () => ref.refresh(productsProvider), ); }, ); }, ); ``` --- ## Testing ### Testing Network Connectivity ```dart // Use mock implementation final mockNetworkInfo = NetworkInfoMock(isConnected: false); // Test offline scenario final repository = ProductRepositoryImpl( remoteDataSource: mockRemoteDataSource, networkInfo: mockNetworkInfo, ); final result = await repository.getProducts(); expect(result.isLeft(), true); expect(result.fold((l) => l, (r) => null), isA()); ``` ### Testing API Calls ```dart // Use mock data source final mockDataSource = ProductRemoteDataSourceMock(); final products = await mockDataSource.fetchProducts(); expect(products, isNotEmpty); expect(products.first.name, 'Sample Product 1'); ``` --- ## API Response Format ### Expected JSON Response Formats #### Products List ```json { "products": [ { "id": "1", "name": "Product Name", "description": "Product description", "price": 29.99, "imageUrl": "https://example.com/image.jpg", "categoryId": "cat1", "stockQuantity": 100, "isAvailable": true, "createdAt": "2024-01-01T00:00:00Z", "updatedAt": "2024-01-01T00:00:00Z" } ] } ``` Or direct array: ```json [ { "id": "1", "name": "Product Name", ... } ] ``` #### Single Product ```json { "product": { "id": "1", "name": "Product Name", ... } } ``` #### Categories List ```json { "categories": [ { "id": "1", "name": "Electronics", "description": "Electronic devices", "iconPath": "assets/icons/electronics.png", "color": "#2196F3", "productCount": 25, "createdAt": "2024-01-01T00:00:00Z" } ] } ``` #### Error Response ```json { "message": "Error message", "error": "Detailed error", "errors": { "field1": ["Validation error 1"], "field2": ["Validation error 2"] } } ``` --- ## Troubleshooting ### Issue: Connection Timeout **Solution**: - Increase timeout in `api_constants.dart` - Check network connectivity - Verify API endpoint is accessible ### Issue: SSL Certificate Error **Solution**: In development, you can allow self-signed certificates: ```dart (_dio.httpClientAdapter as IOHttpClientAdapter).createHttpClient = () { final client = HttpClient(); client.badCertificateCallback = (cert, host, port) => true; return client; }; ``` **  Warning**: Never use this in production! ### Issue: 401 Unauthorized **Solution**: - Check authentication token is set correctly - Verify token hasn't expired - Refresh token if necessary ### Issue: Data Parsing Error **Solution**: - Verify API response format matches expected format - Check JSON field names match model properties - Add debug logging to see actual response --- ## Best Practices 1. **Always check network connectivity** before making API calls 2. **Use offline-first approach** - read from cache first, sync in background 3. **Handle all error scenarios** gracefully with user-friendly messages 4. **Implement proper retry logic** for transient errors 5. **Log requests/responses** in development, disable in production 6. **Use mock data sources** for development and testing 7. **Implement request cancellation** for expensive operations 8. **Cache API responses** to improve performance 9. **Use proper timeout values** based on expected response time 10. **Implement proper authentication** token management --- ## Next Steps 1. **Implement Repositories**: Create repository implementations 2. **Add Use Cases**: Define business logic in use cases 3. **Create Riverpod Providers**: Wire up data layer with UI 4. **Implement Offline Sync**: Add background sync logic 5. **Add Authentication**: Implement OAuth/JWT authentication 6. **Implement Caching**: Add response caching with Hive 7. **Add Pagination**: Implement paginated API requests 8. **Error Tracking**: Integrate error tracking (Sentry, Firebase Crashlytics) --- ## Support & Resources - **Dio Documentation**: https://pub.dev/packages/dio - **Connectivity Plus**: https://pub.dev/packages/connectivity_plus - **GetIt Documentation**: https://pub.dev/packages/get_it - **Riverpod Documentation**: https://riverpod.dev --- ## File Structure ``` lib/  core/   constants/    api_constants.dart  API configuration   di/    injection_container.dart  Dependency injection   errors/    exceptions.dart  Custom exceptions    failures.dart  Failure classes   network/   dio_client.dart  Dio HTTP client   api_interceptor.dart  Interceptors   network_info.dart  Network connectivity  features/   products/    data/    datasources/     product_remote_datasource.dart     models/    product_model.dart    categories/   data/   datasources/    category_remote_datasource.dart    models/   category_model.dart  ``` --- **All API integration components are now complete and ready to use!** <‰