# ClientFactory
[](https://badge.fury.io/py/clientfactory)
[](https://www.python.org/downloads/)
[](https://opensource.org/licenses/MIT)
[](https://github.com/schizoprada/clientfactory)
A declarative framework for building comprehensive API clients with minimal boilerplate.
## Quick Start
```py
from clientfactory import (
Client, resource, headers, session,
param, payload, get, post
)
@headers
class ExampleHeaders:
accept = "application/json" # header key derived from attribute name
useragent = ("User-Agent", "Mozilla Firefox 5.0") # explicit header key
@session
class ExampleSession:
headers = ExampleHeaders
@param
class Username:
type = str # automatic type conversion & enforcement
required = True # enforce usage
transform = lambda value: value.lower() # custom value transformation
def emailvalidator(email: str, extant: list) -> None:
if email in extant:
raise ValueError(f"An account with email: {email} is already registered")
@param
class Email:
type = str
required = True
validator = emailvalidator # custom data validation
@param
class Country:
target = "country_name" # request body key to assign value to
choices = [...] # explicit choices
default = "USA"
@payload
class UserRegistrationPayload:
username = Username
email = Email
country = Country
class Example(Client):
__session__ = ExampleSession
baseurl = "https://www.example.com"
@resource
class Users:
@get("{id}") # url path parameter formation
def read(self, id, *args, **kwargs): pass
@post(payload=UserRegistrationPayload)
def create(self, *args, **kwargs): pass
# nested resources automatically extend the baseurl path with the class name
# all methods in this endpoint will request (and optionally extend) the resource baseurl
# which would be "https://www.example.com/users"
# to override, use 'path' attribute e.g.
# path = "" // dont use resource name in url path
if __name__ == "__main__":
client = Example()
client.users.read(1) # GET request to https://www.example.com/users/1
client.users.create(username="TESTUSER", email="test@user.com", country="USA") # POST request to https://www.example.com/users
# with json body = {'username': 'testuser', 'email': 'test@user.com', 'country_name': 'USA'}
# on client instantiation, resource classes are instantiated and attributed with lowercase
# client.users = instance of Users
# client.Users = Users class
```
## Key Features
`ClientFactory` provides a comprehensive library for rapidly building production-ready API clients.
### Declarative Component Definitions
Define API components through simple class attributes, rather than complex configuration objects. Transform any class/method into a functional component with minimal code.
```py
from clientfactory import Headers
# while this is valid, and works:
session_headers = Headers(
some_header = "some_value",
...
)
# the preferred approach would be:
class Session_Headers(Headers):
some_header = "some_value" # automatically resolves to (Some-Header) in final request headers
# or better yet, using decorators (more on those below)
from clientfactory import headers
@headers
class Session_Headers:
some_header = "some_value"
```
### Intuitive Decorators System
Every `ClientFactory` component has a decorator to match. Transform classes and methods into API client components with zero boilerplate, inheritance chains, or complex setup.
```py
from clientfactory import Client, searchable
class Marketplace(Client):
baseurl = "https://www.marketplace.com"
@searchable # automatically builds a searchable endpoint, with sensible defaults (can be overrided)
class Listings:
pass
if __name__ == "__main__":
market = Marketplace()
market.listings.search(keyword="example")
```
### Built-in Authentication
`JWT`, `DPoP`, and custom authentication with automatic header management. Additional authentication mechanism(s) support e.g. `OAuth` in development.
```py
from clientfactory import dpop, session
@dpop
class MyAuth:
algorithm = "ES256"
headerkey = "DPoP" # where to add authentication value(s) in the headers during request construction pipeline
jwk = {...} # public/private keys etc.
@session
class MySession:
__auth__ = MyAuth # thats all you need !
```
### Backend Adapters
Native support for specialized API protocols, including Algolia & GraphQL, with more in development. Incorporates automatic request/response transformation and protocol-specific optimizations.
```py
from clientfactory import algolia
@algolia
class Adapter:
# some fields are used for request context
appid = "..."
apikey = "..."
# some for direct manipulation of body
facetsmap = {
"brand": "brand.name"
} # will be used to construct the algolia request query string
```
### Robust Parameter/Payload System
Built on [`schematix`](https://github.com/schizoprada/schematix) for powerful data validation, type safety, transformation, conditional logic, and complex compositions of the aforementioned.
```py
from clientfactory import param, payload
from rapidfuzz import process
BrandIDMap: dict[str, int] = {...}
@param
class Brand:
target = "brand_id" # what key the value should be assigned to in request body
required = True
mapping = BrandIDMap
mapper = process.extractOne # custom mapping lookup logic
```
### Advanced Operational Abilities
Components in this are defined in a manner that allows them to stand and function alone, but also complement and interface with eachother seamlessly to allow for constructing complex operational processes to meet objective needs out the box.
```py
from clientfactory import Client, resource, get
class Marketplace(Client):
baseurl = "https://www.marketplace.com"
@resource
class Listings:
@get("{id}")
def view(self, id, *args, **kwargs):
"""view individual listings"""
pass
market = Marketplace()
preparedexecutable = market.listings.view.prepare(1) # returns ExecutableRequest object prepared to call
for response in market.listings.view.iterate(range(1, 100)):
... # automatic request iteration with dynamic params/body
```
### Extensible
Extending and customizing library components without modifying core framework. Simple and intuitive abstract method implementations allow for minimal implementations to achieve desired functionality.
```py
from clientfactory import BaseAuth, RequestModel
class MyAuth(BaseAuth):
def __init__(
self,
token: str,
*args,
**kwargs
) -> None:
super().__init__(*args, **kwargs)
self._token = token
# implement concrete counterparts to the abstract methods
def _applyauth(self, request: RequestModel) -> Requestmodel:
headers = {'token-key': self._token}
return request.withheaders(headers)
```
### Type Safety
Full type hinting and IDE support provided throughout the library.
## Installation
To install `ClientFactory`, simply run:
```bash
pip install clientfactory
```
## Additional Documentation
- [Architecture](ARCHITECTURE.md)
- [API Reference](API.md)
- [Examples](EXAMPLES.md)
Raw data
{
"_id": null,
"home_page": null,
"name": "clientfactory",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.13",
"maintainer_email": "Joel Yisrael <schizoprada@gmail.com>",
"keywords": "adapter, algolia, api, authentication, backend, client, declarative, decorators, dpop, framework, graphql, http, jwt, pydantic, requests",
"author": null,
"author_email": "Joel Yisrael <schizoprada@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/3c/f0/14aa2b25c8ec36636d4383dcd9de6611c6154f2f0f07e44b8014027e6ae3/clientfactory-0.9.38.tar.gz",
"platform": null,
"description": "# ClientFactory\n\n[](https://badge.fury.io/py/clientfactory)\n[](https://www.python.org/downloads/)\n[](https://opensource.org/licenses/MIT)\n[](https://github.com/schizoprada/clientfactory)\n\nA declarative framework for building comprehensive API clients with minimal boilerplate.\n\n\n## Quick Start\n```py\nfrom clientfactory import (\n Client, resource, headers, session,\n param, payload, get, post\n)\n\n@headers\nclass ExampleHeaders:\n accept = \"application/json\" # header key derived from attribute name\n useragent = (\"User-Agent\", \"Mozilla Firefox 5.0\") # explicit header key\n\n@session\nclass ExampleSession:\n headers = ExampleHeaders\n\n@param\nclass Username:\n type = str # automatic type conversion & enforcement\n required = True # enforce usage\n transform = lambda value: value.lower() # custom value transformation\n\ndef emailvalidator(email: str, extant: list) -> None:\n if email in extant:\n raise ValueError(f\"An account with email: {email} is already registered\")\n\n@param\nclass Email:\n type = str\n required = True\n validator = emailvalidator # custom data validation\n\n@param\nclass Country:\n target = \"country_name\" # request body key to assign value to\n choices = [...] # explicit choices\n default = \"USA\"\n\n@payload\nclass UserRegistrationPayload:\n username = Username\n email = Email\n country = Country\n\n\nclass Example(Client):\n __session__ = ExampleSession\n baseurl = \"https://www.example.com\"\n\n @resource\n class Users:\n\n @get(\"{id}\") # url path parameter formation\n def read(self, id, *args, **kwargs): pass\n\n @post(payload=UserRegistrationPayload)\n def create(self, *args, **kwargs): pass\n\n # nested resources automatically extend the baseurl path with the class name\n # all methods in this endpoint will request (and optionally extend) the resource baseurl\n # which would be \"https://www.example.com/users\"\n # to override, use 'path' attribute e.g.\n # path = \"\" // dont use resource name in url path\n\nif __name__ == \"__main__\":\n client = Example()\n client.users.read(1) # GET request to https://www.example.com/users/1\n client.users.create(username=\"TESTUSER\", email=\"test@user.com\", country=\"USA\") # POST request to https://www.example.com/users\n # with json body = {'username': 'testuser', 'email': 'test@user.com', 'country_name': 'USA'}\n\n # on client instantiation, resource classes are instantiated and attributed with lowercase\n # client.users = instance of Users\n # client.Users = Users class\n```\n\n## Key Features\n`ClientFactory` provides a comprehensive library for rapidly building production-ready API clients.\n\n### Declarative Component Definitions\nDefine API components through simple class attributes, rather than complex configuration objects. Transform any class/method into a functional component with minimal code.\n```py\nfrom clientfactory import Headers\n\n# while this is valid, and works:\nsession_headers = Headers(\n some_header = \"some_value\",\n ...\n)\n\n# the preferred approach would be:\nclass Session_Headers(Headers):\n some_header = \"some_value\" # automatically resolves to (Some-Header) in final request headers\n\n# or better yet, using decorators (more on those below)\nfrom clientfactory import headers\n\n@headers\nclass Session_Headers:\n some_header = \"some_value\"\n```\n\n### Intuitive Decorators System\nEvery `ClientFactory` component has a decorator to match. Transform classes and methods into API client components with zero boilerplate, inheritance chains, or complex setup.\n```py\nfrom clientfactory import Client, searchable\n\nclass Marketplace(Client):\n baseurl = \"https://www.marketplace.com\"\n\n @searchable # automatically builds a searchable endpoint, with sensible defaults (can be overrided)\n class Listings:\n pass\n\nif __name__ == \"__main__\":\n market = Marketplace()\n market.listings.search(keyword=\"example\")\n```\n\n### Built-in Authentication\n`JWT`, `DPoP`, and custom authentication with automatic header management. Additional authentication mechanism(s) support e.g. `OAuth` in development.\n```py\nfrom clientfactory import dpop, session\n\n@dpop\nclass MyAuth:\n algorithm = \"ES256\"\n headerkey = \"DPoP\" # where to add authentication value(s) in the headers during request construction pipeline\n jwk = {...} # public/private keys etc.\n\n@session\nclass MySession:\n __auth__ = MyAuth # thats all you need !\n```\n\n### Backend Adapters\nNative support for specialized API protocols, including Algolia & GraphQL, with more in development. Incorporates automatic request/response transformation and protocol-specific optimizations.\n```py\nfrom clientfactory import algolia\n\n@algolia\nclass Adapter:\n # some fields are used for request context\n appid = \"...\"\n apikey = \"...\"\n # some for direct manipulation of body\n facetsmap = {\n \"brand\": \"brand.name\"\n } # will be used to construct the algolia request query string\n```\n\n### Robust Parameter/Payload System\nBuilt on [`schematix`](https://github.com/schizoprada/schematix) for powerful data validation, type safety, transformation, conditional logic, and complex compositions of the aforementioned.\n```py\nfrom clientfactory import param, payload\nfrom rapidfuzz import process\n\nBrandIDMap: dict[str, int] = {...}\n\n@param\nclass Brand:\n target = \"brand_id\" # what key the value should be assigned to in request body\n required = True\n mapping = BrandIDMap\n mapper = process.extractOne # custom mapping lookup logic\n```\n\n### Advanced Operational Abilities\nComponents in this are defined in a manner that allows them to stand and function alone, but also complement and interface with eachother seamlessly to allow for constructing complex operational processes to meet objective needs out the box.\n```py\nfrom clientfactory import Client, resource, get\n\nclass Marketplace(Client):\n baseurl = \"https://www.marketplace.com\"\n\n @resource\n class Listings:\n @get(\"{id}\")\n def view(self, id, *args, **kwargs):\n \"\"\"view individual listings\"\"\"\n pass\n\nmarket = Marketplace()\n\npreparedexecutable = market.listings.view.prepare(1) # returns ExecutableRequest object prepared to call\n\nfor response in market.listings.view.iterate(range(1, 100)):\n ... # automatic request iteration with dynamic params/body\n```\n\n### Extensible\nExtending and customizing library components without modifying core framework. Simple and intuitive abstract method implementations allow for minimal implementations to achieve desired functionality.\n```py\nfrom clientfactory import BaseAuth, RequestModel\n\nclass MyAuth(BaseAuth):\n def __init__(\n self,\n token: str,\n *args,\n **kwargs\n ) -> None:\n super().__init__(*args, **kwargs)\n self._token = token\n\n # implement concrete counterparts to the abstract methods\n def _applyauth(self, request: RequestModel) -> Requestmodel:\n headers = {'token-key': self._token}\n return request.withheaders(headers)\n```\n\n### Type Safety\nFull type hinting and IDE support provided throughout the library.\n\n\n## Installation\nTo install `ClientFactory`, simply run:\n```bash\npip install clientfactory\n```\n\n## Additional Documentation\n- [Architecture](ARCHITECTURE.md)\n- [API Reference](API.md)\n- [Examples](EXAMPLES.md)\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A declarative framework for building API clients with minimal boilerplate using decorators, authentication, and backend adapters",
"version": "0.9.38",
"project_urls": {
"Bug Tracker": "https://github.com/schizoprada/clientfactory/issues",
"Changelog": "https://github.com/schizoprada/clientfactory/blob/main/docs/CHANGELOG.md",
"Homepage": "https://github.com/schizoprada/clientfactory",
"Repository": "https://github.com/schizoprada/clientfactory"
},
"split_keywords": [
"adapter",
" algolia",
" api",
" authentication",
" backend",
" client",
" declarative",
" decorators",
" dpop",
" framework",
" graphql",
" http",
" jwt",
" pydantic",
" requests"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "06eb928e59263b26e6ba92b871ac439fc9df8aff9386daab3cf21a659a50ffc8",
"md5": "539903981a4887c016dbe6a8959b3a29",
"sha256": "899e9337a7b7ae66fb24c577c3a6887661e4facb3ccecbdd5203d68722abb13c"
},
"downloads": -1,
"filename": "clientfactory-0.9.38-py3-none-any.whl",
"has_sig": false,
"md5_digest": "539903981a4887c016dbe6a8959b3a29",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.13",
"size": 129367,
"upload_time": "2025-07-29T19:29:42",
"upload_time_iso_8601": "2025-07-29T19:29:42.676795Z",
"url": "https://files.pythonhosted.org/packages/06/eb/928e59263b26e6ba92b871ac439fc9df8aff9386daab3cf21a659a50ffc8/clientfactory-0.9.38-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "3cf014aa2b25c8ec36636d4383dcd9de6611c6154f2f0f07e44b8014027e6ae3",
"md5": "89823de034a3e766f3d9b1bfa196c10c",
"sha256": "ac7413571cb12a449667b3c7d1631511bf33deb71c66651356bcd9170b97c267"
},
"downloads": -1,
"filename": "clientfactory-0.9.38.tar.gz",
"has_sig": false,
"md5_digest": "89823de034a3e766f3d9b1bfa196c10c",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.13",
"size": 176800,
"upload_time": "2025-07-29T19:29:44",
"upload_time_iso_8601": "2025-07-29T19:29:44.044851Z",
"url": "https://files.pythonhosted.org/packages/3c/f0/14aa2b25c8ec36636d4383dcd9de6611c6154f2f0f07e44b8014027e6ae3/clientfactory-0.9.38.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-29 19:29:44",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "schizoprada",
"github_project": "clientfactory",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "clientfactory"
}