'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