{"id":23414193,"url":"https://github.com/unmade/apiwrappers","last_synced_at":"2025-04-12T05:44:30.803Z","repository":{"id":44960774,"uuid":"231653904","full_name":"unmade/apiwrappers","owner":"unmade","description":"Build API clients that work both with regular and async code","archived":false,"fork":false,"pushed_at":"2022-08-29T07:24:53.000Z","size":274,"stargazers_count":15,"open_issues_count":2,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-12T05:44:24.365Z","etag":null,"topics":["aiohttp","api","api-wrapper","async","asyncio","client","python3","requests","typed","wrapper"],"latest_commit_sha":null,"homepage":"http://apiwrappers.rtfd.io/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/unmade.png","metadata":{"files":{"readme":"README.rst","changelog":null,"contributing":"CONTRIBUTING.rst","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-01-03T19:43:47.000Z","updated_at":"2023-09-08T18:01:55.000Z","dependencies_parsed_at":"2022-09-02T22:33:35.984Z","dependency_job_id":null,"html_url":"https://github.com/unmade/apiwrappers","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unmade%2Fapiwrappers","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unmade%2Fapiwrappers/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unmade%2Fapiwrappers/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unmade%2Fapiwrappers/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/unmade","download_url":"https://codeload.github.com/unmade/apiwrappers/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248525155,"owners_count":21118616,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aiohttp","api","api-wrapper","async","asyncio","client","python3","requests","typed","wrapper"],"created_at":"2024-12-22T19:57:23.937Z","updated_at":"2025-04-12T05:44:30.779Z","avatar_url":"https://github.com/unmade.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"========\nOverview\n========\n\n.. start-badges\n\n.. image:: https://github.com/unmade/apiwrappers/workflows/test/badge.svg?branch=master\n    :alt: Build Status\n    :target: https://github.com/unmade/apiwrappers/blob/master/.github/workflows/test.yml\n\n.. image:: https://readthedocs.org/projects/apiwrappers/badge/?version=latest\n    :alt: Documentation Status\n    :target: https://apiwrappers.readthedocs.io/en/latest/?badge=latest\n\n.. image:: https://codecov.io/gh/unmade/apiwrappers/branch/master/graph/badge.svg\n    :alt: Coverage Status\n    :target: https://codecov.io/gh/unmade/apiwrappers\n\n.. image:: http://www.mypy-lang.org/static/mypy_badge.svg\n    :alt: Checked with mypy\n    :target: http://mypy-lang.org/\n\n.. image:: https://img.shields.io/pypi/v/apiwrappers.svg\n    :alt: PyPI Package latest release\n    :target: https://pypi.org/project/apiwrappers\n\n.. image:: https://img.shields.io/pypi/pyversions/apiwrappers.svg\n    :alt: Supported versions\n    :target: https://pypi.org/project/apiwrappers\n\n.. image:: https://img.shields.io/badge/License-MIT-purple.svg\n    :alt: MIT License\n    :target: https://github.com/unmade/apiwrappers/blob/master/LICENSE\n\n.. end-badges\n\n----------\n\n*apiwrappers* is a library for building API clients\nthat work both with regular and async code.\n\nFeatures\n========\n\n- **DRY** - support both regular and async code with one implementation\n- **Flexible** - middleware mechanism to customize request/response\n- **Typed** - library is fully typed and it's relatively easy\n  to get fully typed wrappers\n- **Modern** - decode JSON with no effort using dataclasses and type annotations\n- **Unified interface** - work with different python HTTP client libraries\n  in the same way. Currently supported:\n\n    - `requests \u003chttps://requests.readthedocs.io/en/master/\u003e`_\n    - `aiohttp \u003chttps://docs.aiohttp.org/en/stable/client.html\u003e`_\n\nInstallation\n============\n\n.. code-block:: bash\n\n    pip install 'apiwrappers[aiohttp,requests]'\n\n*Note: extras are mainly needed for the final user of your API client*\n\nQuickStart\n==========\n\nMaking request is rather straightforward:\n\n.. code-block:: python\n\n    from dataclasses import dataclass\n    from typing import List\n\n    from apiwrappers import Request, fetch, make_driver\n\n    @dataclass\n    class Repo:\n        name: str\n\n    url = \"https://api.github.com/users/unmade/repos\"\n    request = Request(\"GET\", url)\n\n    driver = make_driver(\"requests\")\n    fetch(driver, request)  # Response(..., status_code=200, ...)\n    fetch(driver, request, model=List[Repo])  # [Repo(name='am-date-picker'), ...]\n\n    driver = make_driver(\"aiohttp\")\n    await fetch(driver, request)  # Response(..., status_code=200, ...)\n    await fetch(driver, request, model=List[Repo])  # [Repo(name='am-date-picker'), ...]\n\nWriting a Simple API Client\n---------------------------\n\nWith *apiwrappers* you can bootstrap clients for different API\npretty fast and easily.\n\nHere is how a typical API client would look like:\n\n.. code-block:: python\n\n    from __future__ import annotations\n\n    from dataclasses import dataclass\n    from typing import Awaitable, Generic, List, TypeVar, overload\n\n    from apiwrappers import AsyncDriver, Driver, Request, Url, fetch\n\n    T = TypeVar(\"T\", Driver, AsyncDriver)\n\n\n    @dataclass\n    class Repo:\n        id: int\n        name: str\n\n\n    class GitHub(Generic[T]):\n        def __init__(self, host: str, driver: T):\n            self.url = Url(host)\n            self.driver: T = driver\n\n        @overload\n        def get_repos(self: Github[Driver], username: str) -\u003e List[Repo]:\n            ...\n\n        @overload\n        def get_repos(self: Github[AsyncDriver], username: str) -\u003e Awaitable[List[Repo]]:\n            ...\n\n        def get_repos(self, username: str):\n            url = self.url(\"/users/{username}/repos\", username=username)\n            request = Request(\"GET\", url)\n            return fetch(self.driver, request, model=List[Repo])\n\nThis is small, but fully typed, API client for one of the\n`api.github.com \u003chttps://api.github.com\u003e`_ endpoints to get all user repos\nby username:\n\nHere we defined ``Repo`` dataclass that describes what we want\nto get from response and pass it to the ``fetch`` function.\n``fetch`` will then make a request and will cast response to that type.\n\nNote how we create URL:\n\n.. code-block:: python\n\n    url = self.url(\"/users/{username}/repos\", username=username)\n\nSometimes, it's useful to have an URL template, for example, for logging\nor for aggregating metrics, so instead of formatting immediately, we\nprovide a template and replacement fields.\n\nUsing the API Client\n--------------------\n\nHere how we can use it:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e from apiwrappers import make_driver\n    \u003e\u003e\u003e driver = make_driver(\"requests\")\n    \u003e\u003e\u003e github = GitHub(\"https://api.github.com\", driver=driver)\n    \u003e\u003e\u003e github.get_repos(\"unmade\")\n    [Repo(id=47463599, name='am-date-picker'),\n     Repo(id=231653904, name='apiwrappers'),\n     Repo(id=144204778, name='conway'),\n     ...\n    ]\n\nTo use it with asyncio all we need to do is provide a proper driver\nand don't forget to ``await`` method call:\n\n*Use IPython or Python 3.8+ with python -m asyncio\nto try this code interactively*\n\n.. code-block:: python\n\n    \u003e\u003e\u003e from apiwrappers import make_driver\n    \u003e\u003e\u003e driver = make_driver(\"aiohttp\")\n    \u003e\u003e\u003e github = GitHub(\"https://api.github.com\", driver=driver)\n    \u003e\u003e\u003e await github.get_repos(\"unmade\")\n    [Repo(id=47463599, name='am-date-picker'),\n     Repo(id=231653904, name='apiwrappers'),\n     Repo(id=144204778, name='conway'),\n     ...\n    ]\n\nDocumentation\n=============\n\nDocumentation for *apiwrappers* can be found at\n`Read The Docs \u003chttps://apiwrappers.readthedocs.io/\u003e`_.\n\nCheck out `Extended Client Example \u003cexample/README.md\u003e`_.\n\nContributing\n============\n\nContributions are welcome, and they are greatly appreciated! Every\nlittle bit helps, and credit will always be given.\n\nSee `contributing guide \u003cCONTRIBUTING.rst\u003e`_ to learn more.\n\nCurrently the code and the issues are hosted on GitHub.\n\nThe project is licensed under MIT.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funmade%2Fapiwrappers","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Funmade%2Fapiwrappers","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funmade%2Fapiwrappers/lists"}