Skip to main content

A library for creating fully typed declarative API clients quickly and easily.

Project description

Quick Api Client

Release Build status codecov

A library for creating fully typed declarative API clients quickly and easily.

A quick example

An API definition for a simple service could look like this:

import attrs
import quickapi


# An example type that will be part of the API response
@attrs.define
class Fact:
    fact: str
    length: int


# What the API response should look like
@attrs.define
class ResponseBody(quickapi.BaseResponseBody):
    current_page: int
    data: list[Fact]


# Now we can define our API
class MyApi(quickapi.BaseApi[ResponseBody]):
    url = "https://catfact.ninja/facts"
    response_body = ResponseBody

And you would use it like this:

api_client = MyApi()
response = api_client.execute()

# That's it! Now `response` is fully typed and conforms to our `ResponseBody` definition
assert isinstance(response.body, ResponseBody) == True
assert isinstance(response.body.data[0], Fact) == True

Features

It's still early development and this is currently tightly coupled with attrs, but could expand to support others in the future if there's interest.

  • Write fully typed declarative API clients quickly and easily
    • Fully typed request params / body
    • Fully typed response body
    • Built in serialization/deserialization with attrs
    • Basic error and serialization handling
    • Nested/inner class definitions
    • Improved HTTP status codes error handling
    • Sessions support and/or allow building several related APIs through a single interface
    • Generate API boilerplate from OpenAPI specs?
  • HTTP client libraries
    • httpx
    • requests
    • aiohttp
  • Authentication mechanisms
    • Basic Auth
    • Token / JWT
    • Digest
    • NetRC
    • Any auth supported by httpx or httpx_auth or requests, including custom schemes
  • Serialization/deserialization
    • attrs
    • dataclasses
    • pydantic
  • API support
    • REST
    • GraphQL
    • Maybe others?
  • Response types supported
    • JSON
    • XML
    • Others?

Installation

You can easily install this using pip:

pip install quickapiclient
# Or if you want to use `requests` over `httpx`:
pip install quickapiclient[requests]

Or if using poetry:

poetry add quickapiclient
# Or if you want to use `requests` over `httpx`:
poetry add quickapiclient[requests]

More examples

A GET request with query params

An example of a GET request with query parameters with overridable default values.

import attrs
import quickapi


@attrs.define
class RequestParams(quickapi.BaseRequestParams):
    max_length: int = 100
    limit: int = 10


@attrs.define
class Fact:
    fact: str
    length: int


@attrs.define
class ResponseBody(quickapi.BaseResponseBody):
    current_page: int
    data: list[Fact]


class MyApi(quickapi.BaseApi[ResponseBody]):
    url = "https://catfact.ninja/facts"
    request_params = RequestParams
    response_body = ResponseBody

And to use it:

client = MyApi()
# Using default request param values
response = client.execute()

# Using custom request param values
request_params = RequestParams(max_length=5, limit=10)
response = client.execute(request_params=request_params)

A POST request

An example of a POST request with some optional and required data.

import attrs
import quickapi


@attrs.define
class RequestBody(quickapi.BaseRequestBody):
    required_input: str
    optional_input: str | None = None


@attrs.define
class Fact:
    fact: str
    length: int


@attrs.define
class ResponseBody(quickapi.BaseResponseBody):
    current_page: int
    data: list[Fact]


class MyApi(quickapi.BaseApi[ResponseBody]):
    url = "https://catfact.ninja/facts"
    method = quickapi.BaseApiMethod.POST
    request_body = RequestBody
    response_body = ResponseBody

And to use it:

client = MyApi()
request_body = RequestBody(required_input="dummy")
response = client.execute(request_body=request_body)

A POST request with authentication

An example of a POST request with HTTP header API key.

import attrs
import httpx_auth
import quickapi


@attrs.define
class RequestBody(quickapi.BaseRequestBody):
    required_input: str
    optional_input: str | None = None


@attrs.define
class Fact:
    fact: str
    length: int


@attrs.define
class AuthResponseBody(quickapi.BaseResponseBody):
    authenticated: bool
    user: str


class MyApi(quickapi.BaseApi[AuthResponseBody]):
    url = "https://httpbin.org/bearer"
    method = quickapi.BaseApiMethod.POST
    # You could specify it here if you wanted
    # auth = httpx_auth.HeaderApiKey(header_name="X-Api-Key", api_key="secret_api_key")
    response_body = AuthResponseBody

And to use it:

client = MyApi()
request_body = RequestBody(required_input="dummy")
auth = httpx_auth.HeaderApiKey(header_name="X-Api-Key", api_key="secret_api_key")
response = client.execute(request_body=request_body, auth=auth)

A POST request with validation and conversion

An example of a POST request with custom validators and converters (from attrs).

import attrs
import quickapi
import enum


class State(enum.Enum):
    ON = "on"
    OFF = "off"


@attrs.define
class RequestBody(quickapi.BaseRequestBody):
    state: State = attrs.field(validator=attrs.validators.in_(State))
    email: str = attrs.field(
        validator=attrs.validators.matches_re(
            r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)"
        )
    )


@attrs.define
class ResponseBody(quickapi.BaseResponseBody):
    success: bool = attrs.field(converter=attrs.converters.to_bool)


class MyApi(quickapi.BaseApi[ResponseBody]):
    url = "https://example.com/"
    method = quickapi.BaseApiMethod.POST
    request_body = RequestBody
    response_body = ResponseBody

And to use it:

client = MyApi()
request_body = RequestBody(email="invalid_email", state="on") # Will raise an error
response = client.execute(request_body=request_body)

Check out attrs for full configuration.

Using requests library

An example of a GET request using the requests HTTP library instead of HTTPx.

import attrs
import quickapi


@attrs.define
class ResponseBody(quickapi.BaseResponseBody):
    current_page: int
    data: list[Fact]


class MyApi(quickapi.BaseApi[ResponseBody]):
    url = "https://catfact.ninja/facts"
    response_body = ResponseBody
    http_client = quickapi.RequestsClient()

And to use it:

client = MyApi()
response = client.execute()

Contributing

Contributions are welcomed, and greatly appreciated!

If you want to contribute, check out the contributing guide to get started.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

quickapiclient-0.0.8.tar.gz (8.1 kB view hashes)

Uploaded Source

Built Distribution

quickapiclient-0.0.8-py3-none-any.whl (7.4 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page