Skip to content

Commit f6f987a

Browse files
authored
Header and Cookie param support (#230)
* Add tests for header param * Add tests for the cookies * Add tests for all combinations * Added release notes
1 parent 48ae69d commit f6f987a

13 files changed

Lines changed: 1104 additions & 147 deletions

docs/en/docs/parameters.md

Lines changed: 118 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,146 @@
1-
# Query Parameters
1+
# Parameters
22

3-
Query parameters are an essential part of API development in Lilya, allowing you to extract dynamic
4-
values from the **query string** of an HTTP request. This guide explains what query parameters are,
5-
why and when to use them, and how Lilya makes it simple and elegant to declare and inject them into your
6-
endpoint logic.
3+
Lilya supports **Query**, **Header**, and **Cookie** parameters to cleanly and declaratively extract data
4+
from HTTP requests.
5+
6+
1. **What** each parameter type is
7+
2. **Why** and **when** to use them
8+
3. **Benefits** of Lilya’s parameter system
9+
4. **How** to declare and inject them
710

811
---
912

10-
## What Are Query Parameters?
13+
## What Are Request Parameters?
1114

12-
Query parameters are key-value pairs passed at the end of a URL after the `?` symbol.
13-
They are used to filter, search, paginate, or otherwise customize the data returned by an API.
15+
* **Query Parameters**: Key‑value pairs in the URL after `?`, used for filtering, searching, pagination, and optional flags.
16+
* **Header Parameters**: Metadata in HTTP headers (like `Authorization`, `X‑API‑TOKEN`), used for authentication, content negotiation, and custom flags.
17+
* **Cookie Parameters**: Key‑value pairs stored in cookies, used for sessions, CSRF tokens, user preferences, and stateful data.
1418

15-
For example:
19+
---
1620

17-
```
21+
## ✅ Why Use Them?
1822

19-
GET /items?category=books\&limit=10
23+
* **Separation of concerns**: Clearly distinguish URL modifiers (query) from metadata (headers) and state (cookies).
24+
* **Type safety**: Lilya casts and validates values automatically.
25+
* **Declarative design**: Declare parameters in your function signature, not inside your handler code.
26+
* **Consistency**: Uniform API for all parameter types with `required`, `default`, `alias`/`value`, and `cast` options.
2027

21-
````
28+
---
2229

23-
Here, `category` and `limit` are query parameters.
30+
## Benefits of Lilya’s Parameter System
31+
32+
* **Clean signatures**: No manual extraction from `request`; Lilya handles it.
33+
* **Automatic validation**: Missing required fields or invalid types immediately return 422.
34+
* **Rich metadata**: Control `required`, set `default` or `alias`/`value`, and perform runtime `cast`.
35+
* **Unified API**: Same workflow for `Query`, `Header`, and `Cookie` with minimal boilerplate.
2436

2537
---
2638

27-
## Why Use Query Parameters?
39+
## Declaration Syntax
2840

29-
- **Filtering results:** e.g., `?status=active`
30-
- **Pagination:** e.g., `?page=2&limit=20`
31-
- **Searching:** e.g., `?query=laptop`
32-
- **Optional values:** for things that are not required in the URL path
33-
- **Stateless design:** clients can change the request behavior without altering the endpoint structure
41+
### Query
3442

35-
---
43+
```python
44+
from lilya.params import Query
3645

37-
## Benefits of Lilya's Parameter System
46+
async def handler(
47+
q: str = Query(default=None, alias="q", required=False, cast=str)
48+
):
49+
...
50+
```
3851

39-
- **Declarative:** Declare query parameters directly in your function signature
40-
- **Type-safe:** Parameters are automatically cast to the correct types
41-
- **Optional and required support:** You can control whether a param is optional or required
42-
- **Cleaner APIs:** Avoid manual extraction from the request object
52+
| Option | Type | Description |
53+
| ---------- | ------ | ------------------------------ |
54+
| `default` | `Any` | Fallback if not present |
55+
| `alias` | `str` | Query key name in URL |
56+
| `required` | `bool` | Whether to enforce presence |
57+
| `cast` | `type` | Callable to convert raw string |
4358

4459
---
4560

46-
## Declaring Query Parameters
47-
48-
You can declare a query parameter using the `Query` class:
61+
### Header
4962

5063
```python
51-
from lilya.params import Query
64+
from lilya.params import Header
5265

53-
async def get_user(name: str = Query(), age: int = Query(default=30)):
66+
async def handler(
67+
token: str = Header(value="X-API-TOKEN", required=True, cast=str)
68+
):
5469
...
55-
````
70+
```
5671

57-
* If you omit `default`, it will default to `None`.
58-
* You can also use `required=True` to enforce presence.
72+
| Option | Type | Description |
73+
| ---------- | ------ | ------------------------------ |
74+
| `value` | `str` | Header key name (required) |
75+
| `required` | `bool` | Whether to enforce presence |
76+
| `cast` | `type` | Callable to convert raw string |
5977

6078
---
6179

62-
## Examples
63-
64-
### Example 1: Basic Query Injection
80+
### Cookie
6581

6682
```python
67-
from lilya.params import Query
83+
from lilya.params import Cookie
6884

69-
async def search_books(query: str = Query()):
70-
return {"query": query}
85+
async def handler(
86+
session: str = Cookie(value="csrftoken", required=True, cast=str)
87+
):
88+
...
7189
```
7290

73-
Request:
91+
| Option | Type | Description |
92+
| ---------- | ------ | ------------------------------ |
93+
| `value` | `str` | Cookie name (required) |
94+
| `required` | `bool` | Whether to enforce presence |
95+
| `cast` | `type` | Callable to convert raw string |
96+
97+
---
98+
99+
## 🔍 Real‑World Examples
100+
101+
### 1. Basic Query Injection
74102

103+
```python
104+
async def search_books(query: str = Query()) -> dict:
105+
return {"query": query}
75106
```
107+
108+
```http
76109
GET /search?query=python
77110
```
78111

79-
Response:
112+
### 2. Header‑Based Auth
80113

81-
```json
82-
{
83-
"query": "python"
84-
}
114+
```python
115+
async def get_user(
116+
token: str = Header(value="Authorization", required=True)
117+
) -> dict:
118+
return {"user": validate_token(token)}
85119
```
86120

87-
---
121+
```http
122+
GET /profile
123+
Authorization: Bearer TOKEN123
124+
```
88125

89-
### Example 2: Required vs Optional Parameters
126+
### 3. Cookie‑Based Session
90127

91128
```python
92-
async def search(
93-
q: str = Query(required=True),
94-
page: int = Query(default=1),
95-
):
96-
return {"q": q, "page": page}
129+
async def dashboard(
130+
session_id: str = Cookie(value="sessionid", required=True)
131+
) -> dict:
132+
return {"session": load_session(session_id)}
97133
```
98134

99-
* `q` is **required** — missing it will raise an error
100-
* `page` defaults to `1` if not provided
101-
102-
---
135+
```http
136+
GET /dashboard
137+
Cookie: sessionid=abc123
138+
```
103139

104-
### Example 3: Query + Path + Body + Dependency
140+
### 4. Combined Query, Header, Cookie, Body, Dependency
105141

106142
```python
107-
from lilya.apps import Lilya
108-
from lilya.routing import Path
109-
from lilya.params import Query
143+
from lilya.params import Query, Header, Cookie
110144
from lilya.dependencies import Provide
111145
from pydantic import BaseModel
112146

@@ -116,70 +150,40 @@ class User(BaseModel):
116150

117151
class Service:
118152
def show(self):
119-
return "test"
120-
121-
async def handle_user(
122-
user: User, # Inferred from body
123-
name: str, # Inferred from path
124-
service: Service, # Injected via Provide(...)
125-
q: str = Query() # Inferred from query string
153+
return "ok"
154+
155+
async def handle(
156+
user: User, # from JSON body
157+
q: str = Query(alias="q", default="none"),
158+
token: str = Header(value="X-TOKEN", required=True),
159+
session: str = Cookie(value="csrftoken"),
160+
svc: Service = Provide(Service) # injected dependency
126161
):
127162
return {
128163
"user": user.model_dump(),
129-
"name": name,
130-
"service": service.show(),
131-
"search": q
132-
}
133-
134-
app = Lilya(
135-
routes=[
136-
Path("/", handle_user)
137-
],
138-
dependencies={
139-
"service": Provide(Service)
164+
"q": q,
165+
"token": token,
166+
"session": session,
167+
"svc": svc.show(),
140168
}
141-
)
142169
```
143170

144-
Request:
171+
Request example:
145172

146173
```
147-
GET /lilya?q=python
148-
Body: {"name": "lilya", "age": 2}
149-
```
150-
151-
Response:
152-
153-
```json
154-
{
155-
"user": {"name": "lilya", "age": 2},
156-
"name": "lilya",
157-
"service": "test",
158-
"search": "python"
159-
}
174+
GET /?q=hello
175+
Headers: X-TOKEN: tok
176+
Cookies: csrftoken=sess
177+
Body: {"name": "tiago", "age": 35}
160178
```
161179

162180
---
163181

164-
## How Lilya Resolves Parameters
165-
166-
When Lilya resolves parameters, it classifies them by:
167-
168-
* **Path-bound**: Automatically from the route (`/{name}`)
169-
* **Query-bound**: If declared with `Query(...)`
170-
* **Body-bound**: Any `BaseModel` not matched to another source
171-
* **Dependencies**: Declared via `Provide(...)` or `Provides(...)`
172-
173-
This helps prevent incorrect assumptions and ensures each parameter comes from the correct place in the request
174-
lifecycle.
175-
176-
---
177-
178-
## 📌 Summary
179-
180-
* Use `Query()` to declare query-bound parameters
181-
* Control optionality with `default` and `required`
182-
* Combine with path and body params for powerful, clean APIs
183-
* Lilya automatically wires everything with type safety
182+
## Summary
184183

185-
---
184+
* **Query**: URL-based filters/flags
185+
* **Header**: HTTP metadata
186+
* **Cookie**: Client‑stored data
187+
* **Declare** with `Query`, `Header`, `Cookie` in signature
188+
* **Control** `required`, `default`, `alias`/`value`, and `cast`
189+
* **Combine** freely with path, body, and dependencies

docs/en/docs/release-notes.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,13 @@ hide:
99

1010
### Added
1111

12-
Added support for `Query` parameter markers.
13-
* Implemented `alias` support for query parameters to map custom keys.
14-
* Introduced `cast` field in `Query` for runtime type coercion with validation.
15-
* Improved error handling for missing and invalid query parameter types.
12+
- Added support for `Query` parameter markers.
13+
* Implemented `alias` support for query parameters to map custom keys.
14+
* Introduced `cast` field in `Query` for runtime type coercion with validation.
15+
* Improved error handling for missing and invalid query parameter types.
16+
17+
- Introduced `Header` and `Cookie` parameter markers with `value`, `required`, and `cast` support.
18+
- Expanded documentation into a comprehensive “Request Parameters” guide covering declaration, options, and real-world examples for all three types.
1619

1720
## 0.18.1
1821

0 commit comments

Comments
 (0)