2

Here's the problem I'm trying to solve:

There's a rather large API I'm trying to write a wrapper class around. The simplest approach would be to make one class with a method representing each possible API call. This gets unwieldly though as the API is very large.

My first thought is to break up each API section into a separate class. If this was the Github API I might have a class for the users API and a class for the repositories API etc. However I want the final interface to be accessible from one namespace like so (this is in Python):

from my_api import APIClient

api = APIClient(api_token)

api.users_api_call()
api.repositories_api_call()

How should I achieve this? At first multiple inheritance seemed like a good option, but I'm unsure how to access things like an API token and other general properties/functions in the specialized classes, furthermore conventional wisdom suggests that MI is a poor design choice. What are some approaches to consider here?

3 Answers3

3

This sis quite common, at least in APIs I deal with.

First, you should create a class for each logical component of your API: users, transactions, tweets, etc etc. I recommend putting a good amount of thought into this. Clear organization is instrumental to a usable API. You should make all these classes publicly available, so users can import these components individually.

If you decide you really must have a single class to handle all API requests, just create a wrapper class API (choose a better name of course) that will hold handles to all of your other classes. Then you can do:

my_api = API(api_token)
my_api.users.get_users()

If you want to go a step further, you can even create forwarding methods such as

def get_users():
    users.get_users()

so your users can save some keystrokes.

gardenhead
  • 4,747
2

Since this is Python-specific, you can have the best of both worlds: a package which splits your API in manageable pieces, avoids the god-object, and lets it have a single import. The solution? Python packages typically have an __init__.py file, which can be used to express how the package imports things and presents them to the outside world.

Example:

package\
  __init__.py
  module_a.py
  module_b.py

in __init__.py:

__all__ = ['module_a', 'module_b']

Now you should be able to import them directly using import package.

See this post for additional information.

0

What you are trying to do is the Facade pattern, but by trying to wrap all of a giant API you are incurring in the God Object antipattern.

God objects violates Single responsbility principle and interface segregation principle and make it hard to comply with Open/Closed principle.

I don't know what language you are using but some languages support packages (a kind of namespace) so you can import several classes at once:

import com.apifacade.*;

My advice is that you model different classes with specific concerns like in the example you give: User, RepositoryList, Repository, etc. That way you will have less dependency problems.

  • 1
    You're throwing around a lot of "laws" here, but at the end of the day, it's just a façade, so the problem amounts to "what is convenient, both for the end user and the programmer." – Robert Harvey Jul 27 '16 at 17:30
  • @RobertHarvey Not laws but principles. Principles are not mandatory. Those principles ammount to more maintainable code, which is convenient for both end users and programmers. – Tulains Córdova Jul 27 '16 at 17:34
  • The thing is, for the end user I want it to be a god object of sorts. I want them to instantiate my API class, and be able to make any API call that they want to on that object. – Spencer Wood Jul 27 '16 at 17:48
  • 2
    @SpencerWood You might be right that "any API call from that object" is desirable to your users, but I rather doubt it. Often two or three objects will get you a good amount of conceptual segregation without forcing the user to grapple with a 20 object API. Finding a sweet spot is tricky, Think of the old ADO API. You could do almost anything you needed to with only "Connection" and "Recordset". – Mike Jul 27 '16 at 18:06