Metadata-Version: 2.1
Name: rapid-api-client
Version: 0.3.1
Summary: Rapidly develop your API clients using decorators and annotations
Author: Sébastien MB
Author-email: seb@essembeh.org
Requires-Python: >=3.11,<4.0
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Web Environment
Classifier: Framework :: AsyncIO
Classifier: Framework :: Pydantic
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Information Technology
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Internet
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Dist: httpx (>=0.27.2,<0.28.0)
Requires-Dist: pydantic (>=2.9.2,<3.0.0)
Description-Content-Type: text/markdown

![Github](https://img.shields.io/github/tag/essembeh/rapid-api-client.svg)
![PyPi](https://img.shields.io/pypi/v/rapid-api-client.svg)
![Python](https://img.shields.io/pypi/pyversions/rapid-api-client.svg)
![CI](https://github.com/essembeh/python-helloworld/actions/workflows/poetry.yml/badge.svg)


# Rapid Api Client

Library to **rapidly** develop *API clients* in Python, based on [Pydantic](https://docs.pydantic.dev/) and [Httpx](https://www.python-httpx.org/), using almost only *decorators* and *annotations*.

✨ Main features:
- ✏️ You don't write any code, you only declare the endpoints using *decorators* and *annotations*.
- 🚚 Support *Pydantic* to automatically parse and validate reponses (and also for posting content).
- 🏗️ Does not reimplement the low-level http-related logic, it simply uses `httpx.AsyncClient` like you would do and you can customize it.
- ⚡️ Asynchronous, because `httpx` and `asyncio` are just amazingly fast.

🙏 This project is inspired by [FastAPI](https://fastapi.tiangolo.com/), I always wanted a library to create an API client that is as simple as *FastAPI* for handling the server-side part.

# Usage 

Install the project

```sh
pip install rapid-api-client
```

Declare your API endpoints using decorators and annotations, **the method does not need any code, it will be generated by the decorator**, just write `...` or `pass` or whatever, it won't be called anyway 🙈.

```python
class GithubIssuesApi(RapidApi):

    @get("/repos/{owner}/{repo}/issues", response_class=TypeAdapter(List[Issue]))
    async def list_issues(self, owner: Annotated[str, Path()], repo: Annotated[str, Path()]): ...

    @get("/repos/{owner}/{repo}/releases", response_class=TypeAdapter(List[Release]))
    async def list_issues(self, owner: Annotated[str, Path()], repo: Annotated[str, Path()]): ...

```

Use it 

```python
    api = GithubIssuesApi(client)
    issues = await api.list_issues("essembeh", "rapid-api-client", state="closed")
    for issue in issues:
        print(f"Issue: {issue.title} [{issue.url}]")
```

# Features

## Http method

Any HTTP method can be used with `http` decorator

```python
class MyApi(RapidApi)

    @http("GET", "/anything")
    async def get(self): ...

    @http("POST", "/anything")
    async def post(self): ...

    @http("DELETE", "/anything")
    async def delete(self): ...
```

Convenient decorators are available like `get`, `post`, `delete`, `put`, `patch`

```python
class MyApi(RapidApi)

    @get("/anything")
    async def get(self): ...

    @post("/anything")
    async def post(self): ...

    @delete("/anything")
    async ef delete(self): ...
```


## Response class

By default methods return a `httpx.Response` object and the http return code is not tested (you have to call `resp.raise_for_status()` if you need to ensure the response is OK).

But you can also specify a class so that the response is parsed, you can use:
- `httpx.Response` to get the response itself, this is the default behavior
- `str` to get the `response.text` 
- `bytes` to get the `response.content` 
- Any *Pydantic* model class (subclass of `BaseModel`), the *json* will be automatically validated
- Any *Pydantic-xml* model class (subclass of `BaseXmlModel`), the *xml* will be automatically validated
- Any `TypeAdapter` to parse the *json*, see [pydantic doc](https://docs.pydantic.dev/latest/api/type_adapter/)

> Note: When `response_class` is given (and is not `httpx.Response`), the `raise_for_status()` is always called to ensure the http response is OK

```python
class User(BaseModel): ...

class MyApi(RapidApi)

    # this method return a httpx.Response
    @get("/user/me")
    async def get_user_resp(self): ...

    # this method returns a User class
    @get("/user/me", response_class=User)
    async def get_user(self): ...
```


## Path parameters

Like `fastapi` you can use your method arguments to build the api path to call.

```python
class MyApi(RapidApi)

    @get("/user/{user_id}")
    async def get_user(self, user_id: Annotated[int, Path()]): ...

    # Path parameters dans have a default value
    @get("/user/{user_id}")
    async def get_user(self, user_id: Annotated[int, Path()] = 1): ...

```

## Query parameters

You can add `query parameters` to your request using the `Query` annotation.

```python
class MyApi(RapidApi)

    @get("/issues")
    async def get_issues(self, sort: Annotated[str, Query()]): ...

    # Query parameters can have a default value
    @get("/issues")
    async def get_issues_default(self, sort: Annotated[str, Query()] = "date"): ...

    # Query parameters can have an alias to change the key in the http request
    @get("/issues")
    async def get_issues_alias(self, sort: Annotated[str, Query(alias="sort-by")] = "date"): ...
```


## Header parameter

You can add `headers` to your request using the `Header` annotation.

```python
class MyApi(RapidApi)

    @get("/issues")
    async def get_issues(self, version: Annotated[str, Header()]): ...

    # Headers can have a default value
    @get("/issues")
    async def get_issues(self, version: Annotated[str, Header()] = "1"): ...

    # Headers can have an alias to change the key in the http request
    @get("/issues")
    async def get_issues(self, version: Annotated[str, Header(alias="X-API-Version")] = "1"): ...
```

## Body parameter

You can send a body with your request using the `Body` annotation. 

This body can be 
 - a *raw* object with `Body`
 - a *Pydantic* object  with `PydanticBody`
 - one or more files with `FileBody`

 ```python
class MyApi(RapidApi)

    # send a string in request content
    @post("/string")
    async def message(self, body: Annotated[str, Body()]): ...

    # send a string in request content
    @post("/model")
    async def model(self, body: Annotated[MyPydanticClass, PydanticBody()]): ...

    # send a multiple files
    @post("/files")
    async def model(self, report: Annotated[bytes, FileBody()], image: Annotated[bytes, FileBody()]): ...

 ```

 ## Xml Support

 Xml is also supported is you use [Pydantic-Xml](https://pydantic-xml.readthedocs.io/), either for responses with `response_class` or for POST/PUT content with `PydanticXmlBody`.

 ```python
class ResponseXmlRootModel(BaseXmlModel): ...

class MyApi(RapidApi)

    # parse response xml content
    @get("/get", response_class=ResponseXmlRootModel)
    async def get_xml(self): ...

    # serialize xml model automatically
    @post("/post")
    async def post_xml(self, body: Annotated[ResponseXmlRootModel, PydanticXmlBody()]): ...

 ```

 # Examples

 See [example directory](./examples/) for some examples
