This commit is contained in:
Phuoc Nguyen
2025-10-10 16:38:07 +07:00
parent e5b247d622
commit b94c158004
177 changed files with 25080 additions and 152 deletions

345
docs/API_QUICK_REFERENCE.md Normal file
View File

@@ -0,0 +1,345 @@
# 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<ProductRemoteDataSource>();
// 5. Fetch data
final products = await productDS.fetchProducts();
```
## Common Operations
### Fetch All Products
```dart
final dataSource = sl<ProductRemoteDataSource>();
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<CategoryRemoteDataSource>();
final categories = await categoryDS.fetchCategories();
```
### Check Connectivity
```dart
final networkInfo = sl<NetworkInfo>();
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<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
}
```
### Repository Pattern (Recommended)
```dart
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
```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<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
```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