Skip to main content

Declarative HTTP client

Project description

Dequest

PyPI - Version Pre Commit Code Style - Ruff

Dequest is a full featured declarative HTTP client for Python that simplifies the creation and sending HTTP requests and retrieves the results as DTO.

Why dequest?

  • Fast to code: Develop API-integrated features 2–3× faster with zero boilerplate—just decorate and go.

  • No boilerplate: Say goodbye to manual requests, asyncio, retries, and JSON parsing. One function = one clean API call.

  • Safe and typed: Supports Pydantic-style DTOs, typed parameters, and response mapping—minimizing runtime surprises.

  • Resilient by default: Built-in support for retries, caching, and circuit breakers. Your APIs won’t bring your app down.

  • Declarative and elegant: Define path, query, form, and body parameters cleanly with PathParameter, QueryParameter, JsonBody, etc.

  • Smart caching: Add in-memory or Redis-based caching with a single flag.

  • Async without async: Use @async_client and fire off non-blocking API calls without dealing with asyncio yourself.

  • Built-in callbacks & fallbacks: Handle async responses or fallback gracefully when APIs are flaky, dequest makes it smooth.

  • Framework-agnostic: Works in any Python project, from CLI apps to FastAPI, Flask, Django, and beyond.

Key Features

✅ Supports GET, POST, PUT, PATCH and DELETE requests

✅ Sync & Async Client

✅ Optional Caching for GET Requests (Support In-Memory, Redis, Django Cache)

✅ Support authentication (Static & Dynamic)

✅ Maps API Json/XML response to DTO object and list (Supports unlimited nested DTOs)

✅ Support query parameters, JSON body, Form-data and File uploads

✅ Retry and Backoff

✅ Allows Custom Headers per Request (Static & Dynamic)

✅ Circuit Breaker with Custom Fallback Function

✅ API parameter mapping and type checking

✅ Logging

Installation

To install Dequest, simply run:

pip install dequest

Getting Started

Synchronous API Calls

Use @sync_client to make synchronous HTTP requests without writing boilerplate code:

from dequest import sync_client, QueryParameter
from typing import List
from dataclasses import dataclass

@dataclass
class UserDto:
    id: int
    name: str
    city: str


@sync_client(url="https://jsonplaceholder.typicode.com/users", dto_class=UserDto)
def get_users(city: str = QueryParameter(default="Paris", alias="city_name")) -> List[UserDto]:
    pass

users = get_users(city="New York")
print(users)

Asynchronous Fire-and-Forget API Calls

Use @async_client to make non-blocking fire-and-forget HTTP requests in synchronous/asynchronous code, with callback for handling responses:

from dequest import async_client, HTTPMethod

async def callback_function(response):
    print(response)

@async_client(url="https://api.example.com/notify", method=HTTPMethod.POST, callback=callback_function)
def notify():
    pass

notify()

Awaitable Asynchronous API Calls

Use @async_await_client to make asynchronous HTTP requests that can be awaited for their result:

import asyncio
from dequest import async_await_client, QueryParameter

@async_await_client(url="https://jsonplaceholder.typicode.com/users", dto_class=UserDto)
async def get_users_async(city: str = QueryParameter(default="Paris", alias="city_name")) -> List[UserDto]:
    pass

async def main():
    users = await get_users_async(city="New York")
    print(users)

asyncio.run(main())

Handling Parameters

Path Parameters

Pass values inside the URL using PathParameter:

from dequest import sync_client, PathParameter

@sync_client(url="https://jsonplaceholder.typicode.com/users/{user_id}", dto_class=UserDto)
def get_user(user_id: int = PathParameter()) -> UserDto:
    pass

user = get_user(user_id=1)
print(user)

Query Parameters

Pass values as URL query parameters using QueryParameter:

from dequest import sync_client, QueryParameter

@sync_client(url="https://api.example.com/search", dto_class=UserDto)
def search_users(name: str = QueryParameter()):
    pass

users = search_users(name="Alice")

JSON Parameters

For POST requests pass values as JSON payload using JsonBody:

from dequest import sync_client, HTTPMethod, JsonBody

@sync_client(url="https://api.example.com/users", method=HTTPMethod.POST, dto_class=UserDto)
def create_user(name: str = JsonBody(), city: str = JsonBody()) -> UserDto:
    pass

new_user = create_user(name="Alice", city="Berlin")

Form Parameters

For POST/PUT requests pass values as form data using FormParameter:

from dequest import sync_client, HTTPMethod, FormParameter

@sync_client(url="https://api.example.com/users", method=HTTPMethod.POST, dto_class=UserDto)
def create_user(name: str = FormParameter(), city: str = FormParameter()) -> UserDto:
    pass

new_user = create_user(name="Alice", city="Berlin")

File Uploads

Upload files along with form data using FileParameter. Supports bytes, file paths, and file-like objects:

from dequest import sync_client, HTTPMethod, FileParameter, FormParameter

@sync_client(url="https://api.example.com/upload", method=HTTPMethod.POST, dto_class=dict)
def upload_file(file: bytes = FileParameter(), description: str = FormParameter()) -> dict:
    pass

# Upload with bytes
upload_file(file=b"file content", description="My document")

# FileParameter also supports:
# - File paths: upload_file(file="/path/to/file.pdf", description="...")
# - File-like objects: upload_file(file=open("file.pdf", "rb"), description="...")

Note: FileParameter automatically converts the request to multipart/form-data format. Cannot be combined with JsonBody.

Deprecated Parameter Syntax

The old subscription-based parameter style is deprecated and will be removed in a future release.

Deprecated style:

from dequest import sync_client, QueryParameter

@sync_client(url="https://api.example.com/search")
def search_users(name: QueryParameter[str, "fullname"]):
    pass

Use the new FastAPI-style declaration instead:

from dequest import sync_client, QueryParameter

@sync_client(url="https://api.example.com/search")
def search_users(name: str = QueryParameter(alias="fullname")):
    pass

Notes:

  • The Python type now comes from the normal annotation, such as name: str.
  • Mapping options such as alias and default now go inside QueryParameter(...), PathParameter(...), FormParameter(...), or JsonBody(...).
  • If old-style subscription syntax is still used, Dequest emits a FutureWarning to help identify code that should be migrated.

Advanced Features

Retries

Automatically retry failed requests on specified exceptions:

from dequest.exceptions import HTTPError, ConnectTimeout

@sync_client(url="https://api.example.com/data", retries=3, retry_on_exceptions=(HTTPError, ConnectTimeout), retry_delay=2)
def get_data():
    pass

Caching

Enable caching for GET requests:

@sync_client(url="https://api.example.com/popular-posts", enable_cache=True, cache_ttl=60)
def get_popular_posts():
    pass

Circuit Breaker

Prevent excessive calls to failing APIs using a circuit breaker:

from dequest import sync_client, CircuitBreaker

breaker = CircuitBreaker(failure_threshold=5, recovery_timeout=30)

@sync_client(url="https://api.unstable.com/data", circuit_breaker=breaker)
def get_unstable_data():
    pass

Fallback on Failure

Define a fallback function for when the circuit breaker is open:

from dequest import CircuitBreaker

def fallback_response():
    return {"message": "Service unavailable, returning cached data"}

breaker = CircuitBreaker(failure_threshold=3, recovery_timeout=10, fallback_function=fallback_response)

@sync_client(url="https://api.unstable.com/data", circuit_breaker=breaker)
def fetch_unstable_data():
    pass

Configuration

Dequest allows global configuration via DequestConfig, the configuration can be set using .config method of the DequestConfig class:

from dequest import DequestConfig

DequestConfig.config(
    cache_provider=CacheProvider.REDIS, # defaults is "in_memory"
    redis_host="my-redis-server",
    redis_port=6380,
    redis_db=1,
    redis_password="securepassword",
    redis_ssl=True,
)

Documentation

For comprehensive details on Dequest, please refer to the full documentation available at Read the Docs.

License

Dequest is released under the BSD 3-Clause License.

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

dequest-0.8.0.tar.gz (28.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dequest-0.8.0-py3-none-any.whl (35.1 kB view details)

Uploaded Python 3

File details

Details for the file dequest-0.8.0.tar.gz.

File metadata

  • Download URL: dequest-0.8.0.tar.gz
  • Upload date:
  • Size: 28.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.9

File hashes

Hashes for dequest-0.8.0.tar.gz
Algorithm Hash digest
SHA256 b2e6b4e70506f2c3dc6876e6d688f7c1cf4d01b4245b7ed55362e487c783e95c
MD5 d7bf0257dbc161ba6232794d91987dc9
BLAKE2b-256 6ef095de43be572c4dfb4996d996dc76d72632f8bea3737017ff8f0457669604

See more details on using hashes here.

File details

Details for the file dequest-0.8.0-py3-none-any.whl.

File metadata

  • Download URL: dequest-0.8.0-py3-none-any.whl
  • Upload date:
  • Size: 35.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.9

File hashes

Hashes for dequest-0.8.0-py3-none-any.whl
Algorithm Hash digest
SHA256 06ed86c03f81d4ddd30240b7c1936c2c8bbaebf91fb9a85e4fee260352baec2a
MD5 473fc248f66e7e1217b8974bc943684f
BLAKE2b-256 880972b65fa979f58a2c3ae419b2cbe55a47719ff68a2840ceff762110132618

See more details on using hashes here.

Supported by

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