Easier and safer way of storing API endpoints in Flutter
This was how I used to store API endpoints in Flutter:
class RestfulEndpoints {
const RestfulEndpoints._();
static const String baseUrl = 'https://example.com/api';
static const String userInfo = '$baseUrl/user/info';
static const String saveCard = '$baseUrl/user/card/save';
static const String addToCart = '$baseUrl/cart/add';
}
Are you asking what's wrong with this approach? Can you tell me what's the method to get the user info? Or does save card endpoint need a request body? Or maybe it needs path parameters? What are they? ...
I have found a better way. Look at this beauty:
class RestfulEndpoints {
const RestfulEndpoints._();
static const String baseUrl = 'https://example.com/api';
static Endpoint userInfo() {
return Endpoint.get(
url: '${baseUrl}/user',
);
}
static Endpoint saveCard({
required String cardType,
}) {
return Endpoint.post(
url: '${baseUrl}/user/card/save?cardType=$cardType',
);
}
static Endpoint addToCart({
required String productId,
required int quantity,
}) {
return Endpoint.post(
url: '${baseUrl}/cart/add',
body: {
'productId': productId,
'quantity': quantity,
},
);
}
}
I know. It's better.
Then you'll use it like this:
Future<User> getUser() async {
final Endpoint endpoint = RestfulEndpoints.userInfo();
final Response response = await locator<Dio>().fetch(
endpoint.toRequestOptions(),
);
return User.fromJson(response.data['data']);
}
Of course, do your own kind of parsing, validation, onion layering, etc. The point is that the endpoint now contains all the information about... the endpoint.
To have this there are two things you need to have.
Endpoint
class:
class Endpoint {
final String url;
final String method;
final dynamic body;
const Endpoint({
required this.url,
required this.method,
this.body,
});
factory Endpoint.get({
required String url,
}) {
return Endpoint(
url: Uri.encodeFull(url),
method: 'GET',
);
}
factory Endpoint.post({
required String url,
dynamic body,
}) {
return Endpoint(
url: url,
method: 'POST',
body: body,
);
}
factory Endpoint.put({
required String url,
dynamic body,
}) {
return Endpoint(
url: url,
method: 'PUT',
body: body,
);
}
factory Endpoint.delete({
required String url,
}) {
return Endpoint(
url: url,
method: 'DELETE',
);
}
factory Endpoint.patch({
required String url,
dynamic body,
}) {
return Endpoint(
url: url,
method: 'PATCH',
body: body,
);
}
}

UserOrient - Feature Voting Board
Tired of building features nobody uses? Let your users vote on what to build next with UserOrient. Stop wasting development time and build only what users actually want.
Add To Your Flutter AppEndpoint
class extension:
extension EndpointX on Endpoint {
RequestOptions toRequestOptions() {
return RequestOptions(
method: method,
baseUrl: url,
data: body,
);
}
}
That's it.
If you want, add enums for methods, maybe add headers, store URL as Uri
instead of String
, add onSendProgress
to the toRequestOptions
method, etc. It's up to you.
Hope you find it useful.
Happy coding!