ApparenceKit Api and Http requests module
Introduction
ApparenceKit provides you an hexagonal architecture to handle http requests and api calls. This means that the only allowed layer to make http requests is the api layer.
đ By architecture we use http call only in API layer.
The Api layer can also be used to call other api's like firebase, cloud messaging, secured storage... It's the layer responsible to talk with the outside world.
You can check our architecture doc for more informations.
Create an Api call
âšī¸ If you use firebase you don't need anything to read this. Firebase provides us a ready to use client to talk with him. So we don't provide a DIO client when choosing firebase usage.
This project uses the dio package to handle http requests. You will find a lot of examples in the code.
For example
Future<Credentials> signup(String email, String password) async {
try {
final response = await _client.post(
'/auth/signup',
data: {
'email': email,
'password': password,
},
);
return Credentials.fromJson(response.data as Map<String, dynamic>);
} on DioException catch (e, stackTrace) {
_logger.e(e, stackTrace: stackTrace);
throw ApiError.fromDioException(e);
} catch (e) {
_logger.e(e);
throw ApiError(
code: 0,
message: '$e',
);
}
}
To create an api you must inject the http client from Riverpod
final authenticationApiProvider = Provider<AuthenticationApi>(
(ref) => HttpAuthenticationApi(
logger: Logger(),
client: ref.read(httpClientProvider), /// <--- Here
),
);
This client is a dio client with some interceptors. Like the authentication interceptor that adds the authentication token to every request if the user is authenticated.
Error handling
We use a custom error class to handle errors.
ApiError.fromDioException(e)
This is useful if you have managed uniform errors from your backend. I like to use a custom error class to handle errors from my backend with a code, a message and a reason (reason sometimes can be a solution to help the user or frontend developer to resolve the error).
Using Firebase
If you use firebase you don't need to create an api to handle http requests. But we still have wrapped all method within api call for multiple reasons.
- Writing tests is easier
- We keep the same architecture for all api calls (if we needs to call another api)
- We can add some logic to the api call (like error handling, logging, ...)
You could think that we lose our time wrapping firebase methods but it's not the case. Using this architecture I can easily maintain my tests and tests saves a lot of time.