HTTP declaration¶
DeclarativeX supports two ways of declaring clients: class-based and function-based.
Both are equally powerful and flexible, so it's up to you to decide which one to use.
@http
decorator¶
The @http
decorator is the core of DeclarativeX. It's used to declare endpoints and specify their parameters.
Syntax¶
@http(method, path, *, base_url, timeout, default_headers, default_query_params, middlewares)
def method_name() -> dict:
...
@http(method, path, *, base_url, timeout, default_headers, default_query_params, middlewares)
async def method_name() -> dict:
...
Tip
Sync and async functions are supported. Just define your function as async
and you're good to go.
Keyword-only arguments
All arguments after path
are keyword-only arguments, so you must specify them by name.
Supported methods¶
DeclarativeX currently supports the following HTTP methods:
GET
POST
PUT
PATCH
DELETE
More HTTP methods will be added in the future.
Case
The HTTP method is case-insensitive, so you can use either Get
or get
.
Unsupported methods
If you will try to use unsupported HTTP method, you will get MisconfiguredException exception at the runtime.
Declare parameters¶
This table outlines the arguments you can pass to the decorator, detailing their type, whether they're required or optional, and what each argument is for.
Positional arguments¶
Name | Type | Required | Arg type | Description |
---|---|---|---|---|
method |
str |
Yes | Position | Specifies the HTTP method (e.g., GET, POST, PUT) you want to use. |
path |
str |
Yes | Position | Defines the API endpoint path you're hitting. |
Keyword-only arguments¶
Name | Type | Required | Arg type | Description |
---|---|---|---|---|
base_url |
str |
Not always | Keyword | Sets the base URL for the request. |
timeout |
int |
No, default: None |
Keyword | The timeout to use. |
auth |
declarativex.Auth |
No, default: None |
Keyword | The auth instance to use. |
default_headers |
dict |
No, default: None |
Keyword | The headers to use with every request. |
default_query_params |
dict |
No, default: None |
Keyword | The params to use with every request. |
middlewares |
list |
No, default: None |
Keyword | The middlewares to use with every request. |
error_mappings |
dict |
No, default: None |
Keyword | The error mappings to use with every request. |
proxies |
dict | str | None | URL | Proxy |
No, default: None |
Keyword | The proxies to use with every request. |
base_url
This is necessary if the method is not part of a class that already specifies it.
If you don't specify base_url
in function-based declaration, you will get MisconfiguredException exception at the runtime.
Priority of the parameters resolution¶
The priority of the parameters is as follows:
- Pick parameter from the decorator.
- Pick parameter from the class.
- If both are specified, merge them.
Priority
Decorator parameters have higher priority and will overwrite the same values of class parameter
Return Type¶
You can use any of the custom dataclasses, Pydantic models or built-in types to parse the response automatically.
-> dict
If you don't specify a return type, the decorator will return a httpx.Response
object.
But, your IDE will not be able to detect the type of the response, so it's recommended to specify the return type.
explicitly specifying httpx.Response return type
Specifying httpx.Respose
will both return unprocessed httpx.Response
object and
preserve type hint information for IDE.
Class-based declaration¶
Class-based declaration is the most common way to declare clients. It's also the most flexible one.
Use the BaseClient
class as a base class for your client and declare methods using the http
decorator.
Example¶
from declarativex import BaseClient, http
class MyClient(BaseClient):
base_url = "https://example.com"
default_query_params = {"api_key": "123456"}
default_headers = {"X-Trace": "<hash>"}
@http("GET", "/users/{user_id}", timeout=10)
def get_user(self, user_id: int) -> dict:
...
my_client = MyClient()
user: dict = my_client.get_user(user_id=1)
import asyncio
from declarativex import BaseClient, http
class MyClient(BaseClient):
base_url = "https://example.com"
default_query_params = {"api_key": "123456"}
default_headers = {"X-Trace": "<hash>"}
@http("GET", "/users/{user_id}", timeout=10)
async def get_user(self, user_id: int) -> dict:
...
my_client = MyClient()
user: dict = asyncio.run(my_client.get_user(user_id=1))
Class-based declaration
If you're using class-based declaration, you must pass self
as the first argument to the method.
It will be used to get the base_url
, default_query_params
, default_headers
and other values.
Function-based declaration¶
Function-based declaration is a great alternative to class-based declaration. It's more concise and doesn't require you to create a class. Commonly used for simple clients with one endpoint.
Example¶
from declarativex import http
@http(
"GET",
"/users/{user_id}",
base_url"https://example.com",
timeout=10,
default_headers={"X-Trace": "<hash>"},
default_query_params={"api_key": "123456"}
)
def get_user(user_id: int) -> dict:
...
import asyncio
from declarativex import http
@http(
"GET",
"/users/{user_id}",
base_url"https://example.com",
timeout=10,
default_headers={"X-Trace": "<hash>"},
default_query_params={"api_key": "123456"}
)
def get_user(user_id: int) -> dict:
...
user: dict = asyncio.run(get_user(user_id=1))
Function-based declaration
If you're using function-based declaration, you don't need to add self
as first argument.
The parameters of the decorator will be used to get the base_url
, default_query_params
and default_headers
values.