p2b: add Path & Query Parameters tutorial — type conversion table, Path/Query options, curl examples
swadhinbiswas committed 3 months ago
b22d2ac
1 parent 2b5b9ce
commit b22d2ac
1 file changed
+166 -0
Showing 1 changed file+166-0
▼docs_site/src/content/docs/tutorials/path-query-params.md
+166-0
| @@ -0,0 +1,166 @@ | ||
1 | +--- | |
2 | +title: Path & Query Parameters | |
3 | +description: Declare and type-validate path and query parameters in JustAPI. | |
4 | +--- | |
5 | + | |
6 | +Path and query parameters let you extract variables from the HTTP request URL. JustAPI handles type conversion and validation automatically using Python type hints. | |
7 | + | |
8 | +## Path Parameters | |
9 | + | |
10 | +Path parameters are declared as part of the URL path using `{param_name}` syntax: | |
11 | + | |
12 | +```python | |
13 | +from justapi import JustAPIApp | |
14 | + | |
15 | +app = JustAPIApp() | |
16 | + | |
17 | + | |
18 | +@app.get("/items/{item_id}") | |
19 | +def read_item(request, item_id: int): | |
20 | + return {"item_id": item_id} | |
21 | +``` | |
22 | + | |
23 | +```bash | |
24 | +curl http://127.0.0.1:8000/items/42 | |
25 | +# Output: {"item_id":42} | |
26 | +``` | |
27 | + | |
28 | +### Type Conversion | |
29 | + | |
30 | +JustAPI automatically converts path parameters based on type hints. If conversion fails, it returns **422 Unprocessable Entity** without invoking your handler: | |
31 | + | |
32 | +```python | |
33 | +from uuid import UUID | |
34 | + | |
35 | + | |
36 | +@app.get("/users/{user_id}") | |
37 | +def read_user(request, user_id: UUID): | |
38 | + return {"user_id": str(user_id)} | |
39 | +``` | |
40 | + | |
41 | +```bash | |
42 | +curl http://127.0.0.1:8000/users/550e8400-e29b-41d4-a716-446655440000 | |
43 | +# Output: {"user_id":"550e8400-e29b-41d4-a716-446655440000"} | |
44 | + | |
45 | +curl http://127.0.0.1:8000/users/not-a-uuid | |
46 | +# Output: {"detail":"validation error"} | |
47 | +``` | |
48 | + | |
49 | +### Supported Types | |
50 | + | |
51 | +| Type | Example | Valid Input | Invalid Input | | |
52 | +|---|---|---|---| | |
53 | +| `int` | `/items/{id}` | `42` | `abc` → 422 | | |
54 | +| `float` | `/price/{val}` | `19.99` | `abc` → 422 | | |
55 | +| `str` | `/hello/{name}` | any string | — | | |
56 | +| `bool` | `/flag/{val}` | `true`, `1`, `yes` | `maybe` → 422 | | |
57 | +| `UUID` | `/users/{uid}` | `550e8400-...` | `bad-uuid` → 422 | | |
58 | +| `datetime` | `/events/{ts}` | `2026-07-24T12:00:00Z` | `not-a-date` → 422 | | |
59 | + | |
60 | +## Query Parameters | |
61 | + | |
62 | +Query parameters are extracted from the URL query string (`?key=value&key2=value2`). They're declared as function parameters with default values: | |
63 | + | |
64 | +```python | |
65 | +from typing import Optional | |
66 | + | |
67 | + | |
68 | +@app.get("/items/") | |
69 | +def list_items( | |
70 | + request, | |
71 | + skip: int = 0, | |
72 | + limit: int = 10, | |
73 | + q: Optional[str] = None, | |
74 | +): | |
75 | + return {"skip": skip, "limit": limit, "q": q} | |
76 | +``` | |
77 | + | |
78 | +```bash | |
79 | +curl "http://127.0.0.1:8000/items/?skip=0&limit=5&q=search" | |
80 | +# Output: {"skip":0,"limit":5,"q":"search"} | |
81 | +``` | |
82 | + | |
83 | +### Optional vs Required | |
84 | + | |
85 | +Parameters with default values are optional query parameters. Parameters without defaults are **required**: | |
86 | + | |
87 | +```python | |
88 | +@app.get("/search/") | |
89 | +def search(request, query: str, page: int = 1): | |
90 | + # `query` is required (no default) | |
91 | + # `page` is optional (defaults to 1) | |
92 | + return {"query": query, "page": page} | |
93 | +``` | |
94 | + | |
95 | +```bash | |
96 | +curl "http://127.0.0.1:8000/search/?query=rust" | |
97 | +# Output: {"query":"rust","page":1} | |
98 | + | |
99 | +curl "http://127.0.0.1:8000/search/" | |
100 | +# Output: {"detail":"validation error"} (missing required query) | |
101 | +``` | |
102 | + | |
103 | +## Combining Path and Query Parameters | |
104 | + | |
105 | +```python | |
106 | +@app.get("/users/{user_id}/items/") | |
107 | +def list_user_items( | |
108 | + request, | |
109 | + user_id: int, | |
110 | + skip: int = 0, | |
111 | + limit: int = 10, | |
112 | +): | |
113 | + return {"user_id": user_id, "skip": skip, "limit": limit} | |
114 | +``` | |
115 | + | |
116 | +```bash | |
117 | +curl "http://127.0.0.1:8000/users/42/items/?limit=3" | |
118 | +# Output: {"user_id":42,"skip":0,"limit":3} | |
119 | +``` | |
120 | + | |
121 | +## Using `Path()` and `Query()` for Explicit Control | |
122 | + | |
123 | +For more control, use the `Path()` and `Query()` extractors: | |
124 | + | |
125 | +```python | |
126 | +from justapi import Path, Query | |
127 | + | |
128 | + | |
129 | +@app.get("/items/{item_id}") | |
130 | +def read_item( | |
131 | + request, | |
132 | + item_id: int = Path(..., description="The item ID"), | |
133 | + q: str = Query(None, max_length=50, description="Search query"), | |
134 | +): | |
135 | + return {"item_id": item_id, "q": q} | |
136 | +``` | |
137 | + | |
138 | +### `Path()` Options | |
139 | + | |
140 | +| Parameter | Type | Description | | |
141 | +|---|---|---| | |
142 | +| `default` | any | Default value (`...` means required) | | |
143 | +| `description` | str | Field description for OpenAPI docs | | |
144 | + | |
145 | +### `Query()` Options | |
146 | + | |
147 | +| Parameter | Type | Description | | |
148 | +|---|---|---| | |
149 | +| `default` | any | Default value | | |
150 | +| `description` | str | Field description for OpenAPI docs | | |
151 | +| `max_length` | int | Maximum string length | | |
152 | +| `min_length` | int | Minimum string length | | |
153 | +| `regex` | str | Regular expression pattern | | |
154 | + | |
155 | +## How It Works Internally | |
156 | + | |
157 | +1. JustAPI's radix-trie router (matchit) extracts path parameters during route matching — O(1) time | |
158 | +2. Type conversion runs in Rust before Python is called | |
159 | +3. Query parameters are parsed in Rust via `serde_urlencoded` | |
160 | +4. Invalid types return 422 without GIL acquisition | |
161 | + | |
162 | +## Next Steps | |
163 | + | |
164 | +- [Request Body & Validation](/tutorials/request-body/) — Validate JSON request bodies | |
165 | +- [Dependency Injection](/tutorials/dependency-injection/) — Extract parameters with Depends | |
166 | +- [Error Handling](/tutorials/error-handling/) — Custom error responses | |
0 | 167 | |