apiarist 1.0.7
apiarist: ^1.0.7 copied to clipboard
Library for handling api responses in a more nuanced way
Apiarist #
A Dart package for handling API responses in a structured and type-safe way. Apiarist provides a clean abstraction for managing different API response states including loading, success, error, and failure scenarios.
Features #
- Type-safe API response handling - Generic
ApiResponse<T>class for any data type - Comprehensive state management - Support for loading, data, error, and failure states
- HTTP status code handling - Built-in support for all standard HTTP status codes
- Riverpod integration - Seamless integration with Riverpod for state management
- Composite responses - Combine multiple parallel API responses with intelligent error handling
- Sequential chaining - Chain dependent API calls with automatic error propagation
- Graceful error handling - Distinguish between API errors and application failures
Installation #
Add this to your package's pubspec.yaml file:
dependencies:
apiarist: ^1.0.7
Then run:
dart pub get
Usage #
Basic Usage #
import 'package:apiarist/apiarist.dart';
// Create different response states
ApiResponse<String> loadingResponse = ApiResponse.loading();
ApiResponse<String> dataResponse = ApiResponse.data("Hello, World!");
ApiResponse<String> errorResponse = ApiResponse.error(ApiError(/* error details */));
ApiResponse<String> failureResponse = ApiResponse.failure(ApiFailure(/* failure details */));
// Handle responses with pattern matching
String result = response.when(
data: (data) => "Success: $data",
loading: () => "Loading...",
error: (error) => "Error: ${error.message}",
failure: (failure) => "Failure: ${failure.message}",
);
Advanced Error Handling #
Handle specific HTTP status codes:
String handleResponse = response.when(
data: (data) => "Success: $data",
loading: () => "Loading...",
error: (error) => "Generic error: ${error.message}",
failure: (failure) => "Failure: ${failure.message}",
// Specific HTTP status code handlers
badRequest: (error) => "Bad request: ${error.message}",
unauthorized: (error) => "Please log in again",
forbidden: (error) => "Access denied",
notfound: (error) => "Resource not found",
internalServerError: (error) => "Server error, please try again later",
// ... and many more status codes
);
Composite Responses (Parallel API Calls) #
Combine multiple parallel API responses with intelligent error prioritization:
ApiResponse<CombinedData> combinedResponse = ApiResponse.composite([
userResponse,
settingsResponse,
notificationsResponse,
], (responses) {
// All responses are successful, combine the data
return CombinedData(
user: responses[0],
settings: responses[1],
notifications: responses[2],
);
});
Use this when you need to wait for multiple independent API calls to complete before displaying data.
Chaining Responses (Sequential API Calls) #
Chain sequential API calls where each call depends on the previous response:
// Fluent chaining syntax
final result = await api
.getUser(userId)
.chain((user) => api.getProfile(user.profileId))
.chain((profile) => api.getSettings(profile.settingsId))
.convertData((settings) => settings.getDetails());
// Handle the final result
result.when(
data: (details) => print("Success: $details"),
loading: () => print("Loading..."),
error: (error) => print("API error: ${error.statusCode}"),
failure: (failure) => print("Failed: ${failure.error}"),
);
The chain() method automatically propagates errors and failures through the chain. If any call fails, subsequent calls are skipped and the error/failure is returned.
Data Conversion #
Convert response data to different types:
ApiResponse<User> userResponse = ApiResponse.data(userData);
ApiResponse<String> userNameResponse = userResponse.convertData((user) => user.name);
Checking Response State #
if (response.isLoading) {
// Show loading indicator
}
if (response.hasValue) {
// Use response.value
}
if (response.hasError) {
// Handle API error
print("Error: ${response.error?.message}");
}
if (response.hasFailure) {
// Handle application failure
print("Failure: ${response.failure?.message}");
}
Integration with Riverpod #
import 'package:riverpod/riverpod.dart';
final userProvider = FutureProvider<ApiResponse<User>>((ref) async {
try {
final response = await apiService.getUser();
return ApiResponse.data(response);
} on ApiError catch (e) {
return ApiResponse.error(e);
} catch (e) {
return ApiResponse.failure(ApiFailure(e.toString()));
}
});
// In your widget
Consumer(
builder: (context, ref, child) {
final userResponse = ref.watch(userProvider);
return userResponse.when(
data: (response) => response.when(
data: (user) => UserWidget(user: user),
loading: () => CircularProgressIndicator(),
error: (error) => ErrorWidget(error: error),
failure: (failure) => FailureWidget(failure: failure),
),
loading: () => CircularProgressIndicator(),
error: (error, stack) => ErrorWidget(error: error),
);
},
)
API Reference #
ApiResponse #
The main class for handling API responses.
Constructors
ApiResponse.data(T value)- Create a successful response with dataApiResponse.loading()- Create a loading responseApiResponse.error(ApiError error)- Create an error responseApiResponse.failure(ApiFailure failure)- Create a failure response
Properties
bool isLoading- Whether the response is in loading statebool hasValue- Whether the response has dataT? value- The response data (null if not in data state)ApiError? error- The API error (null if not in error state)bool hasError- Whether the response has an errorApiFailure? failure- The failure details (null if not in failure state)bool hasFailure- Whether the response has a failure
Methods
when<R>({...})- Pattern match on the response state with optional specific status code handlersconvertData<R>(R Function(T) converter)- Convert the response data to a different typechain<R>(Future<ApiResponse<R>> Function(T) fn)- Chain sequential API calls with automatic error propagationstatic composite<T>(List<ApiResponse> responses, T Function(List) combiner)- Combine multiple parallel responses
ApiError #
Represents an API error with HTTP response details.
ApiFailure #
Represents an application-level failure (non-HTTP errors).
Error vs Failure #
- ApiError: HTTP-related errors from the server (4xx, 5xx status codes)
- ApiFailure: Application-level failures (network issues, parsing errors, etc.)
Changelog #
See CHANGELOG.md for a detailed history of changes.
License #
This project is licensed under the MIT License - see the LICENSE file for details.
Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.