api_request 1.1.1
api_request: ^1.1.1 copied to clipboard
api request for handle call api. every request is an action has execute method.
API Request #
⚡ Action-based HTTP client for Flutter - Single-responsibility API request classes built on Dio.
A Flutter package that introduces a clean, testable approach to organizing API logic through dedicated action classes. Instead of monolithic service classes, create small, focused classes that handle specific API requests.
✨ Features #
- Single Responsibility Principle: Each action class handles one specific API request
- Functional Error Handling: Uses
Either<Error, Success>pattern with fpdart - Dynamic Configuration: Runtime base URL and token resolution
- Performance Monitoring: Built-in request timing and reporting
- Flexible Authentication: Multiple token provider strategies
- Path Variables: Dynamic URL path substitution
- Global Error Handling: Centralized error management
- Comprehensive Logging: Request/response debugging
📦 Installation #
Add to your pubspec.yaml:
dependencies:
api_request: ^1.0.9
Then run:
flutter pub get
🚀 Quick Start #
1. Global Configuration #
Configure the package in your main() function:
import 'package:api_request/api_request.dart';
void main() {
ApiRequestOptions.instance?.config(
baseUrl: 'https://jsonplaceholder.typicode.com/',
// Authentication
tokenType: ApiRequestOptions.bearer,
getAsyncToken: () => getTokenFromSecureStorage(),
// Global error handling
onError: (error) => print('API Error: ${error.message}'),
// Default headers
defaultHeaders: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
// Development settings
enableLog: true,
connectTimeout: const Duration(seconds: 30),
);
runApp(MyApp());
}
2. Create Action Classes #
Simple GET Request (No Request Data)
class GetPostsAction extends ApiRequestAction<List<Post>> {
@override
bool get authRequired => false;
@override
String get path => 'posts';
@override
RequestMethod get method => RequestMethod.GET;
@override
ResponseBuilder<List<Post>> get responseBuilder =>
(json) => (json as List).map((item) => Post.fromJson(item)).toList();
}
POST Request with Data
class CreatePostRequest with ApiRequest {
final String title;
final String body;
final int userId;
CreatePostRequest({
required this.title,
required this.body,
required this.userId,
});
@override
Map<String, dynamic> toMap() => {
'title': title,
'body': body,
'userId': userId,
};
}
class CreatePostAction extends RequestAction<Post, CreatePostRequest> {
CreatePostAction(CreatePostRequest request) : super(request);
@override
bool get authRequired => true;
@override
String get path => 'posts';
@override
RequestMethod get method => RequestMethod.POST;
@override
ResponseBuilder<Post> get responseBuilder =>
(json) => Post.fromJson(json);
}
3. Execute Actions #
Simple Execution
// GET request
final postsResult = await GetPostsAction().execute();
postsResult?.fold(
(error) => print('Error: ${error.message}'),
(posts) => print('Loaded ${posts.length} posts'),
);
// POST request
final request = CreatePostRequest(
title: 'My New Post',
body: 'This is the post content',
userId: 1,
);
final result = await CreatePostAction(request).execute();
Queue Execution with Callbacks
final action = GetPostsAction();
action.subscribe(
onSuccess: (posts) => print('Success: ${posts.length} posts loaded'),
onError: (error) => print('Error: ${error.message}'),
onDone: () => print('Request completed'),
);
action.onQueue(); // Execute without waiting
🔧 Advanced Features #
Dynamic Path Variables #
Use path variables in your URLs:
class GetPostAction extends RequestAction<Post, GetPostRequest> {
@override
String get path => 'posts/{id}'; // {id} will be replaced
// ... other implementation
}
class GetPostRequest with ApiRequest {
final int id;
GetPostRequest(this.id);
@override
Map<String, dynamic> toMap() => {'id': id}; // Provides value for {id}
}
Multi-Environment Support #
Configure different base URLs for different environments:
ApiRequestOptions.instance?.config(
getBaseUrl: () {
switch (Environment.current) {
case Environment.dev:
return 'https://api-dev.example.com';
case Environment.staging:
return 'https://api-staging.example.com';
case Environment.prod:
return 'https://api.example.com';
}
},
);
Custom Error Handling #
// Per-action error handling
class MyAction extends ApiRequestAction<Data> {
@override
ErrorHandler get onError => (error) {
// Handle specific errors for this action
if (error.statusCode == 404) {
// Handle not found
}
};
@override
bool get disableGlobalOnError => true; // Skip global error handler
}
Performance Monitoring #
// Get performance report
final report = ApiRequestPerformance.instance?.actionsReport;
print('Request Performance: $report');
// Or log to console
print(ApiRequestPerformance.instance.toString());
Action Lifecycle Events #
class MyAction extends ApiRequestAction<Data> {
@override
Function get onInit => () => print('Action initialized');
@override
Function get onStart => () => print('Request started');
@override
SuccessHandler<Data> get onSuccess =>
(data) => print('Request succeeded: $data');
@override
ErrorHandler get onError =>
(error) => print('Request failed: ${error.message}');
}
🏗️ Architecture #
The package follows these core principles:
- Action Classes: Each API request is a dedicated class
- Functional Error Handling: Using
Either<Error, Success>pattern - Dependency Injection Ready: Easy to mock for testing
- Configuration Management: Centralized options with runtime flexibility
- Performance Tracking: Built-in monitoring and reporting
Core Components #
ApiRequestAction<T>: Base class for simple requestsRequestAction<T, R>: Base class for requests with dataApiRequestOptions: Global configuration singletonRequestClient: HTTP client wrapper around DioApiRequestPerformance: Performance monitoring
🧪 Testing #
Actions are easy to test due to their single responsibility:
void main() {
group('GetPostsAction', () {
test('should return list of posts', () async {
final action = GetPostsAction();
final result = await action.execute();
expect(result, isNotNull);
result?.fold(
(error) => fail('Expected success but got error: ${error.message}'),
(posts) => expect(posts, isA<List<Post>>()),
);
});
});
}
📖 Complete Example #
Check out the example directory for a complete Flutter app demonstrating:
- CRUD operations
- Error handling
- Performance monitoring
- Mock vs live API switching
- Clean architecture implementation
To run the example:
cd example
flutter run
🤝 Contributing #
Contributions are welcome! Please read our contributing guidelines and submit pull requests to our repository.
📄 License #
This project is licensed under the MIT License - see the LICENSE file for details.
📚 API Reference #
For detailed API documentation, visit pub.dev.
🆘 Support #
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Documentation: pub.dev documentation