Welcome to apiwrappers

apiwrappers is a library for building API clients that work both with regular and async code.

Features

  • DRY - support both regular and async code with one implementation

  • Flexible - middleware mechanism to customize request/response

  • Typed - library is fully typed and it’s relatively easy to get fully typed wrappers

  • Modern - decode JSON with no effort using dataclasses and type annotations

  • Unified interface - work with different python HTTP client libraries in the same way. Currently supported:

Installation

pip install 'apiwrappers[aiohttp,requests]'

Note: extras are mainly needed for the final user of your API client

Getting Started

With apiwrappers you can bootstrap clients for different API pretty fast and easily.

Here is how a typical API client would look like:

from __future__ import annotations

 from dataclasses import dataclass
 from typing import Awaitable, Generic, List, TypeVar, overload

 from apiwrappers import AsyncDriver, Driver, Request, Url, fetch

 T = TypeVar("T", Driver, AsyncDriver)


 @dataclass
 class Repo:
     id: int
     name: str


 class Github(Generic[T]):
     def __init__(self, host: str, driver: T):
         self.url = Url(host)
         self.driver: T = driver

     @overload
     def get_repos(
         self: Github[Driver], username: str
     ) -> List[Repo]:
         ...

     @overload
     def get_repos(
         self: Github[AsyncDriver], username: str
     ) -> Awaitable[List[Repo]]:
         ...

     def get_repos(self, username: str):
         url = self.url("/users/{username}/repos", username=username)
         request = Request("GET", url)
         return fetch(self.driver, request, model=List[Repo])

This is small, but fully typed, API client for one of the api.github.com endpoints to get all user repos by username:

Here we defined Repo dataclass that describes what we want to get from response and pass it to the fetch() function. fetch() will then make a request and cast response to that type.

And here how we can use it:

>>> from apiwrappers import make_driver
>>> driver = make_driver("requests")
>>> github = Github("https://api.github.com", driver=driver)
>>> github.get_repos("unmade")
[Repo(id=47463599, name='am-date-picker'),
 Repo(id=231653904, name='apiwrappers'),
 Repo(id=144204778, name='conway'),
 ...
]

To use it with asyncio all we need to do is provide a proper driver and don’t forget to await method call:

Use IPython or Python 3.8+ with python -m asyncio to try this code interactively

>>> from apiwrappers import make_driver
>>> driver = make_driver("aiohttp")
>>> github = Github("https://api.github.com", driver=driver)
>>> await github.get_repos("unmade")
[Repo(id=47463599, name='am-date-picker'),
 Repo(id=231653904, name='apiwrappers'),
 Repo(id=144204778, name='conway'),
 ...
]