'Is this a good structure for a restapi client?
I'm trying to come up with a way to structure a restAPI client so that it is mockable, and easy to read. Preferably the syntax would closely mimic the way restAPI uris are built:
GET http://example.com/resource/id/subResource/id
In my project there are currently two resources: Plans and Serialnumbers
Plans are the root resource and serialnumbers can be assigned to them:
GET http://example.com/plans/id/serialnumbers/id
Ive come up with the follow set of classes (the actual HTTP calls to the client have been omitted)
abstract class ITrackerApiClient {
ITrackerApiPlans get plans;
}
abstract class ITrackerApiPlans {
Future<void> getAll();
Future<void> create();
ITrackerApiPlan byId(String id);
}
abstract class ITrackerApiPlan {
ITrackerApiPlan_Serialnumbers get serialnumbers;
Future<void> delete() async {}
Future<void> get() async {}
}
abstract class ITrackerApiPlan_Serialnumbers {
Future<void> getAll();
Future<void> create();
ITrackerApiPlan_Serialnumber byId(String id);
}
abstract class ITrackerApiPlan_Serialnumber {
Future<void> delete();
Future<void> get();
}
class TrackerApiClient implements ITrackerApiClient {
@override
ITrackerApiPlans get plans => TrackerApiPlans();
}
class TrackerApiPlans implements ITrackerApiPlans {
@override
Future<void> create() async {
//httpclient.put(uri)
}
@override
Future<void> getAll() async {
//httpclient.get(uri)
}
@override
ITrackerApiPlan byId(String id) {
return TrackerApiPlan(id);
}
}
class TrackerApiPlan implements ITrackerApiPlan {
final String id;
TrackerApiPlan(this.id);
@override
ITrackerApiPlan_Serialnumbers get serialnumbers => TrackerApiPlan_Serialnumbers(id);
@override
Future<void> delete() async {
//httpclient.delete(uri)
}
@override
Future<void> get() async {
//httpclient.get(uri)
}
}
class TrackerApiPlan_Serialnumbers implements ITrackerApiPlan_Serialnumbers {
final String planId;
TrackerApiPlan_Serialnumbers(this.planId);
@override
Future<void> create() async {
//httpclient.put(uri)
}
@override
Future<void> getAll() async {
//httpclient.get(uri)
}
@override
ITrackerApiPlan_Serialnumber byId(String id) {
return TrackerApiPlan_Serialnumber(planId, id);
}
}
class TrackerApiPlan_Serialnumber implements ITrackerApiPlan_Serialnumber {
final String planId;
final String id;
TrackerApiPlan_Serialnumber(this.planId, this.id);
@override
Future<void> delete() async {
//httpclient.delete(uri)
}
@override
Future<void> get() async {
//httpclient.get(uri)
}
}
This code structure allows me to do things like this:
class Test {
void foo() {
ITrackerApiClient client = TrackerApiClient();
client.plans.getAll();
client.plans.create();
client.plans.byId("id").get();
client.plans.byId("id").delete();
client.plans.byId("id").serialnumbers.getAll();
client.plans.byId("id").serialnumbers.create();
client.plans.byId("id").serialnumbers.byId("foo").get();
client.plans.byId("id").serialnumbers.byId("foo").delete();
}
}
I really like the readability of the client calls. I don't like the number of classes required but on the other hand it's not too much effort. The actual http calls I can either perform in each of those classes, or delegate a call to another "internalApiClient" class which contains methods to perform each of the http calls.
Does this seem like a good idea given the fact that the api will continue to grow and will in the end require many more classes?
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
