{"id":15284448,"url":"https://github.com/pmateusz/meatie","last_synced_at":"2025-04-06T12:07:37.510Z","repository":{"id":216971276,"uuid":"735134836","full_name":"pmateusz/meatie","owner":"pmateusz","description":"Meatie is a Python metaprogramming library that eliminates the need for boilerplate code when integrating with REST APIs.  Meatie abstracts away mechanics related to HTTP communication, such as building URLs, encoding query parameters, parsing, and dumping Pydantic models.","archived":false,"fork":false,"pushed_at":"2025-03-23T19:08:14.000Z","size":648,"stargazers_count":52,"open_issues_count":1,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-30T11:06:29.719Z","etag":null,"topics":["aiohttp","api","api-client","api-rest","asyncio","curl","http","http-client","httpx","json","mypy","pydantic","pydantic-v2","python","requests","rest","rest-api","type-hints"],"latest_commit_sha":null,"homepage":"https://meatie.readthedocs.io/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pmateusz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-12-23T19:37:49.000Z","updated_at":"2025-03-23T19:08:18.000Z","dependencies_parsed_at":"2024-09-08T19:06:31.589Z","dependency_job_id":"11ff6684-7acf-4077-8bba-4de8bcbd3364","html_url":"https://github.com/pmateusz/meatie","commit_stats":{"total_commits":73,"total_committers":2,"mean_commits":36.5,"dds":0.4657534246575342,"last_synced_commit":"e325c45d82ca9b6df93ba590a34175c8cd347bfc"},"previous_names":["pmateusz/meatie"],"tags_count":26,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pmateusz%2Fmeatie","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pmateusz%2Fmeatie/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pmateusz%2Fmeatie/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pmateusz%2Fmeatie/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pmateusz","download_url":"https://codeload.github.com/pmateusz/meatie/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247478318,"owners_count":20945266,"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-client","api-rest","asyncio","curl","http","http-client","httpx","json","mypy","pydantic","pydantic-v2","python","requests","rest","rest-api","type-hints"],"created_at":"2024-09-30T14:56:40.711Z","updated_at":"2025-04-06T12:07:37.481Z","avatar_url":"https://github.com/pmateusz.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cimg src=\"https://repository-images.githubusercontent.com/735134836/df6752b8-38fa-4550-968e-cd2eda4adb37\" alt=\"meatie\"\u003e\n\n[![GitHub Test Badge][1]][2] [![Docs][3]][4] [![codecov.io][5]][6] [![pypi.org][7]][8] [![versions][9]][10]\n[![downloads][11]][12] [![License][13]][14]\n\n[1]: https://github.com/pmateusz/meatie/actions/workflows/ci.yaml/badge.svg \"GitHub CI Badge\"\n\n[2]: https://github.com/pmateusz/meatie/actions/workflows/ci.yaml \"GitHub Actions Page\"\n\n[3]: https://readthedocs.org/projects/meatie/badge/?version=latest \"Docs Latest Version Badge\"\n\n[4]: https://meatie.readthedocs.io/\n\n[5]: https://codecov.io/gh/pmateusz/meatie/branch/master/graph/badge.svg?branch=master \"Coverage Badge\"\n\n[6]: https://codecov.io/gh/pmateusz/meatie?branch=master \"Codecov site\"\n\n[7]: https://img.shields.io/pypi/v/meatie.svg \"Pypi Latest Version Badge\"\n\n[8]: https://pypi.python.org/pypi/meatie \"Pypi site\"\n\n[9]:https://img.shields.io/pypi/pyversions/meatie.svg\n\n[10]: https://github.com/pmateusz/meatie\n\n[11]: https://static.pepy.tech/badge/meatie\n\n[12]: https://pepy.tech/project/meatie\n\n[13]: https://img.shields.io/github/license/pmateusz/meatie \"License Badge\"\n\n[14]: https://opensource.org/license/bsd-3-clause \"License\"\n\nMeatie is a Python library that simplifies the implementation of REST API clients. The library generates code for\ncalling REST endpoints based on method signatures annotated with type hints. Meatie takes care of mechanics related to\nHTTP communication, such as building URLs, encoding query parameters, and serializing the body in the HTTP requests and\nresponses. Rate limiting, retries, and caching are available with some modest extra setup.\n\nMeatie works with all major HTTP client libraries (request, httpx, aiohttp) and offers seamless integration with\nPydantic (v1 and v2). The minimum officially supported version is Python 3.9.\n\n## TL;DR\n\nGenerate HTTP clients using type annotations.\n\n```python\nfrom typing import Annotated\n\nfrom aiohttp import ClientSession\nfrom meatie import api_ref, endpoint\nfrom meatie_aiohttp import Client\nfrom pydantic import BaseModel, Field\n\n\nclass Todo(BaseModel):\n    user_id: int = Field(alias=\"userId\")\n    id: int\n    title: str\n    completed: bool\n\n\nclass JsonPlaceholderClient(Client):\n    def __init__(self) -\u003e None:\n        super().__init__(ClientSession(base_url=\"https://jsonplaceholder.typicode.com\"))\n\n    @endpoint(\"/todos\")\n    async def get_todos(self, user_id: Annotated[int, api_ref(\"userId\")] = None) -\u003e list[Todo]: ...\n\n    @endpoint(\"/users/{user_id}/todos\")\n    async def get_todos_by_user(self, user_id: int) -\u003e list[Todo]: ...\n\n    @endpoint(\"/todos\")\n    async def post_todo(self, todo: Annotated[Todo, api_ref(\"body\")]) -\u003e Todo: ...\n```\n\nDo you use a different HTTP client library in your project? See the example adapted for [\n`requests`](./tests/examples/requests/tutorial/test_basics.py) and [\n`httpx`](./tests/examples/httpx/tutorial/test_basics.py).\n\n## Documentation\n\nhttps://meatie.readthedocs.io/\n\n## Installation\n\nMeatie is available on [pypi](https://pypi.org/project/meatie/). You can install it with:\n\n```shell\npip install meatie\n```\n\n## Add Meatie to the Awesome Python list 📢\n\nIf you've had a positive experience with Meatie and would like to support the project, please consider helping us by approving our [pull request](https://github.com/vinta/awesome-python/pull/2662) in the Awesome Python repository.\nYour support is greatly appreciated!\n\n## Features\n\n### Caching\n\nCache result for a given TTL.\n\n```python\nfrom typing import Annotated\n\nfrom meatie import MINUTE, api_ref, cache, endpoint, Cache\nfrom meatie_aiohttp import Client\nfrom pydantic import BaseModel\n\n\nclass Todo(BaseModel):\n    ...\n\n\nclass JsonPlaceholderClient(Client):\n    def __init__(self) -\u003e None:\n        super().__init__(\n            ClientSession(base_url=\"https://jsonplaceholder.typicode.com\"),\n            local_cache=Cache(max_size=100),\n        )\n\n    @endpoint(\"/todos\", cache(ttl=MINUTE, shared=False))\n    async def get_todos(self, user_id: Annotated[int, api_ref(\"userId\")] = None) -\u003e list[Todo]:\n        ...\n```\n\nA cache key is built based on the URL path and query parameters. It does not include the scheme or the network location.\nBy default, every HTTP client instance has an independent cache. The behavior can be changed in the endpoint definition\nto share cached results across all HTTP client class instances.\n\nYou can pass your custom cache to the local_cache parameter. The built-in cache provides a max_size parameter to limit\nits size.\n\n### Rate Limiting\n\nMeatie can delay HTTP requests that exceed the predefined rate limit.\n\n```python\nfrom typing import Annotated\n\nfrom aiohttp import ClientSession\nfrom meatie import Limiter, Rate, api_ref, endpoint, limit\nfrom meatie_aiohttp import Client\nfrom pydantic import BaseModel\n\n\nclass Todo(BaseModel):\n    ...\n\n\nclass JsonPlaceholderClient(Client):\n    def __init__(self) -\u003e None:\n        super().__init__(\n            ClientSession(base_url=\"https://jsonplaceholder.typicode.com\"),\n            limiter=Limiter(Rate(tokens_per_sec=10), capacity=10),\n        )\n\n    @endpoint(\"/todos\", limit(tokens=2))\n    async def get_todos(self, user_id: Annotated[int, api_ref(\"userId\")] = None) -\u003e list[Todo]:\n        ...\n```\n\n### Retries\n\nMeatie can retry failed HTTP requests following the strategy set in the endpoint definition. The retry strategy is\ncontrolled using third-party functions that specify when a retry should be attempted, how long to wait between\nconsecutive attempts to call the endpoint, and whether to abort further retries.\n\n```python\nfrom typing import Annotated\n\nfrom aiohttp import ClientSession\nfrom meatie import (\n    HttpStatusError,\n    RetryContext,\n    after_attempt,\n    api_ref,\n    endpoint,\n    fixed,\n    jit,\n    retry,\n)\nfrom meatie_aiohttp import Client\nfrom pydantic import BaseModel\n\n\nclass Todo(BaseModel):\n    ...\n\n\ndef should_retry(ctx: RetryContext) -\u003e bool:\n    if isinstance(ctx.error, HttpStatusError):\n        return ctx.error.response.status \u003e= 500\n    return False\n\n\nclass JsonPlaceholderClient(Client):\n    def __init__(self) -\u003e None:\n        super().__init__(\n            ClientSession(base_url=\"https://jsonplaceholder.typicode.com\", raise_for_status=True)\n        )\n\n    @endpoint(\"/todos\", retry(on=should_retry, stop=after_attempt(3), wait=fixed(5) + jit(2)))\n    async def get_todos(self, user_id: Annotated[int, api_ref(\"userId\")] = None) -\u003e list[Todo]:\n        ...\n```\n\nMeatie comes with a built-in set of predefined functions for building retry strategies. See\nthe [meatie.retry](./src/meatie/option/retry_option.py) option for more details.\n\n### Calling Private Endpoints\n\nMeatie can inject additional information into the HTTP request. A typical example is adding the `Authorization` header\nwith a token or signing the request using API keys.\n\n```python\nfrom typing import Annotated, override\n\nfrom aiohttp import ClientSession\nfrom meatie import Request, api_ref, endpoint, private\nfrom meatie_aiohttp import Client\nfrom pydantic import BaseModel\n\n\nclass Todo(BaseModel):\n    ...\n\n\nclass JsonPlaceholderClient(Client):\n    def __init__(self) -\u003e None:\n        super().__init__(ClientSession(base_url=\"https://jsonplaceholder.typicode.com\"))\n\n    @endpoint(\"/todos\", private)\n    async def get_todos(self, user_id: Annotated[int, api_ref(\"userId\")] = None) -\u003e list[Todo]:\n        ...\n\n    @override\n    async def authenticate(self, request: Request) -\u003e None:\n        request.headers[\"Authorization\"] = \"Bearer bWVhdGll\"\n```\n\n## More Examples\n\nNeed more control over processing the HTTP requests or responses? See the [Meatie Cookbook](./docs/cookbook.md) with\nsolutions to the most frequently asked questions by the community.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpmateusz%2Fmeatie","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpmateusz%2Fmeatie","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpmateusz%2Fmeatie/lists"}