Files
retail/docs/API_QUICK_REFERENCE.md
Phuoc Nguyen b94c158004 runable
2025-10-10 16:38:07 +07:00

8.2 KiB

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)

// 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<ProductRemoteDataSource>();

// 5. Fetch data
final products = await productDS.fetchProducts();

Common Operations

Fetch All Products

final dataSource = sl<ProductRemoteDataSource>();
final products = await dataSource.fetchProducts();

Search Products

final results = await dataSource.searchProducts('laptop');

Fetch by Category

final products = await dataSource.fetchProductsByCategory('electronics');

Fetch Single Product

final product = await dataSource.fetchProductById('123');

Fetch Categories

final categoryDS = sl<CategoryRemoteDataSource>();
final categories = await categoryDS.fetchCategories();

Check Connectivity

final networkInfo = sl<NetworkInfo>();
final isConnected = await networkInfo.isConnected;

Error Handling Patterns

Basic Try-Catch

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

final networkInfo = sl<NetworkInfo>();
if (!await networkInfo.isConnected) {
  // Load from cache or show offline UI
  return;
}

try {
  final products = await dataSource.fetchProducts();
  // Use products
} catch (e) {
  // Handle error
}
Future<Either<Failure, List<Product>>> 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

// 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

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

@riverpod
class Products extends _$Products {
  @override
  Future<List<ProductModel>> build() async {
    final dataSource = sl<ProductRemoteDataSource>();
    return await dataSource.fetchProducts();
  }
  
  Future<void> refresh() async {
    state = const AsyncValue.loading();
    state = await AsyncValue.guard(() async {
      final dataSource = sl<ProductRemoteDataSource>();
      return await dataSource.fetchProducts();
    });
  }
}

UI Usage

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

{
  "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

{
  "categories": [
    {
      "id": "1",
      "name": "Electronics",
      "productCount": 25,
      "createdAt": "2024-01-01T00:00:00Z"
    }
  ]
}

Error Response

{
  "message": "Error message",
  "error": "Detailed error"
}

Testing Quick Start

Use Mock Data Source

// In api_constants.dart
static const bool useMockData = true;

// Or directly
final mockDS = ProductRemoteDataSourceMock();
final products = await mockDS.fetchProducts();

Mock Network Status

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