Skip to content

birddevelper/dequest

Repository files navigation

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 and Form-data

✅ 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: QueryParameter[str, "city_name"]) -> List[UserDto]: pass users = get_users(city="New York") print(users)

Asynchronous API Calls

Use @async_client to make non-blocking HTTP requests:

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()

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: PathParameter[int]) -> 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: QueryParameter[str]): 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: JsonBody, city: JsonBody) -> UserDto: pass new_user = create_user(name="Alice", city="Berlin")

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.