swadhinbiswas

[email protected]
2026-07-25 17:37:18 +0600 committed NaN years ago
98106de
commit 98106de
37 files changed
+2546 -33
Showing 37 changed files+2546-33
docs_site/astro.config.mjsdocs_site/src/content/docs/how-to/graphql.mddocs_site/src/content/docs/how-to/index.mddocs_site/src/content/docs/resources/about.mddocs_site/src/content/docs/resources/external-links.mddocs_site/src/content/docs/resources/help.mddocs_site/src/content/docs/tutorials/async-await.mddocs_site/src/content/docs/tutorials/body-updates.mddocs_site/src/content/docs/tutorials/cookie-params.mddocs_site/src/content/docs/tutorials/cors.mddocs_site/src/content/docs/tutorials/debugging.mddocs_site/src/content/docs/tutorials/dependencies/classes-as-dependencies/index.mddocs_site/src/content/docs/tutorials/dependencies/dependencies-in-path-ops/index.mddocs_site/src/content/docs/tutorials/dependencies/dependencies-with-yield/index.mddocs_site/src/content/docs/tutorials/dependencies/global-dependencies/index.mddocs_site/src/content/docs/tutorials/dependencies/sub-dependencies/index.mddocs_site/src/content/docs/tutorials/encoder.mddocs_site/src/content/docs/tutorials/extra-data-types.mddocs_site/src/content/docs/tutorials/extra-models.mddocs_site/src/content/docs/tutorials/form-data.mddocs_site/src/content/docs/tutorials/form-models.mddocs_site/src/content/docs/tutorials/header-params.mddocs_site/src/content/docs/tutorials/metadata.mddocs_site/src/content/docs/tutorials/path-operation-config.mddocs_site/src/content/docs/tutorials/path-params.mddocs_site/src/content/docs/tutorials/python-types.mddocs_site/src/content/docs/tutorials/query-params.mddocs_site/src/content/docs/tutorials/request-files.mddocs_site/src/content/docs/tutorials/request-forms-files.mddocs_site/src/content/docs/tutorials/response-model.mddocs_site/src/content/docs/tutorials/response-status-code.mddocs_site/src/content/docs/tutorials/security/first-steps/index.mddocs_site/src/content/docs/tutorials/security/get-current-user/index.mddocs_site/src/content/docs/tutorials/security/oauth2-jwt/index.mddocs_site/src/content/docs/tutorials/security/simple-oauth2/index.mddocs_site/src/content/docs/tutorials/static-files.mddocs_site/src/content/docs/tutorials/testing.md
▼docs_site/astro.config.mjs
+116-33
108
108
],
109
109
},
110
110
{
111
- label: 'Tutorials',
111
+ label: 'Tutorial — User Guide',
112
+ badge: { text: 'Start here', variant: 'success' },
112
113
items: [
113
- { label: 'Hello World', link: 'tutorials/hello-world/' },
114
- { label: 'Path & Query Parameters', link: 'tutorials/path-query-params/' },
115
- { label: 'Request Body & Validation', link: 'tutorials/request-body/' },
116
- { label: 'Dependency Injection', link: 'tutorials/dependency-injection/' },
114
+ { label: 'Python Types Intro', link: 'tutorials/python-types/' },
115
+ { label: 'Async / Await', link: 'tutorials/async-await/' },
116
+ { label: 'First Steps', link: 'tutorials/hello-world/' },
117
+ { label: 'Path Parameters', link: 'tutorials/path-params/' },
118
+ { label: 'Query Parameters', link: 'tutorials/query-params/' },
119
+ { label: 'Request Body (Pydantic)', link: 'tutorials/request-body/' },
120
+ { label: 'Query Parameters & String Validations', link: 'tutorials/query-params-str-validations/' },
121
+ { label: 'Path Parameters & Numeric Validations', link: 'tutorials/path-params-numeric-validations/' },
122
+ { label: 'Body — Multiple Parameters', link: 'tutorials/body-multiple-params/' },
123
+ { label: 'Body — Fields', link: 'tutorials/body-fields/' },
124
+ { label: 'Body — Nested Models', link: 'tutorials/body-nested-models/' },
125
+ { label: 'Extra Data Types', link: 'tutorials/extra-data-types/' },
126
+ { label: 'Cookie Parameters', link: 'tutorials/cookie-params/' },
127
+ { label: 'Header Parameters', link: 'tutorials/header-params/' },
128
+ { label: 'Response Model — Return Type', link: 'tutorials/response-model/' },
129
+ { label: 'Extra Models', link: 'tutorials/extra-models/' },
130
+ { label: 'Response Status Code', link: 'tutorials/response-status-code/' },
131
+ { label: 'Form Data', link: 'tutorials/form-data/' },
132
+ { label: 'Form Models', link: 'tutorials/form-models/' },
133
+ { label: 'Request Files', link: 'tutorials/request-files/' },
134
+ { label: 'Request Forms & Files', link: 'tutorials/request-forms-files/' },
135
+ { label: 'Handling Errors', link: 'tutorials/error-handling/' },
136
+ { label: 'Path Operation Configuration', link: 'tutorials/path-operation-config/' },
137
+ { label: 'JSON Compatible Encoder', link: 'tutorials/encoder/' },
138
+ { label: 'Body — Updates', link: 'tutorials/body-updates/' },
139
+ {
140
+ label: 'Dependencies',
141
+ collapsed: true,
142
+ items: [
143
+ { label: 'Classes as Dependencies', link: 'tutorials/dependencies/classes-as-dependencies/' },
144
+ { label: 'Sub-dependencies', link: 'tutorials/dependencies/sub-dependencies/' },
145
+ { label: 'Dependencies in Path Ops', link: 'tutorials/dependencies/dependencies-in-path-ops/' },
146
+ { label: 'Global Dependencies', link: 'tutorials/dependencies/global-dependencies/' },
147
+ { label: 'Dependencies with yield', link: 'tutorials/dependencies/dependencies-with-yield/' },
148
+ ],
149
+ },
150
+ {
151
+ label: 'Security',
152
+ collapsed: true,
153
+ items: [
154
+ { label: 'Security — First Steps', link: 'tutorials/security/first-steps/' },
155
+ { label: 'Get Current User', link: 'tutorials/security/get-current-user/' },
156
+ { label: 'Simple OAuth2', link: 'tutorials/security/simple-oauth2/' },
157
+ { label: 'OAuth2 + JWT Tokens', link: 'tutorials/security/oauth2-jwt/' },
158
+ ],
159
+ },
117
160
{ label: 'Middleware', link: 'tutorials/middleware/' },
118
- { label: 'Error Handling', link: 'tutorials/error-handling/' },
119
- { label: 'File Uploads', link: 'tutorials/file-uploads/' },
120
- { label: 'WebSockets & SSE', link: 'tutorials/websockets-sse/' },
121
- { label: 'Database Integration', link: 'tutorials/database-integration/' },
161
+ { label: 'CORS (Cross-Origin Resource Sharing)', link: 'tutorials/cors/' },
162
+ { label: 'SQL Databases', link: 'tutorials/database-integration/' },
163
+ { label: 'Bigger Applications — Multiple Files', link: 'tutorials/routing-subrouters/' },
122
164
{ label: 'Background Tasks', link: 'tutorials/background-tasks/' },
123
- { label: 'Routing & Sub-routers', link: 'tutorials/routing-subrouters/' },
165
+ { label: 'Metadata & Docs URLs', link: 'tutorials/metadata/' },
166
+ { label: 'Static Files', link: 'tutorials/static-files/' },
167
+ { label: 'Testing', link: 'tutorials/testing/' },
168
+ { label: 'Debugging', link: 'tutorials/debugging/' },
169
+ ],
170
+ },
171
+ {
172
+ label: 'Advanced User Guide',
173
+ items: [
174
+ { label: 'Zero-GIL Architecture', link: 'advanced/zero-gil-architecture/' },
175
+ { label: 'Rust Core Deep Dive', link: 'advanced/rust-core-deep-dive/' },
176
+ { label: 'PyO3 & FFI Safety', link: 'advanced/pyo3-ffi-safety/' },
177
+ { label: 'Native Fast Path', link: 'advanced/native-fast-path/' },
178
+ { label: 'Multi-Protocol APIs', link: 'advanced/multi-protocol-apis/' },
179
+ { label: 'Agent System', link: 'advanced/agent-system/' },
180
+ { label: 'Streaming Output', link: 'advanced/streaming-output/' },
181
+ { label: 'Performance Tuning', link: 'advanced/performance-tuning/' },
182
+ { label: 'Resilience Patterns', link: 'advanced/resilience-patterns/' },
183
+ { label: 'Return a Response Directly', link: 'advanced/response-directly/' },
184
+ { label: 'Custom Response Classes', link: 'advanced/custom-response/' },
185
+ { label: 'Additional Responses in OpenAPI', link: 'advanced/additional-responses/' },
186
+ { label: 'Response Cookies & Headers', link: 'advanced/response-cookies-headers/' },
187
+ { label: 'Advanced Dependencies', link: 'advanced/advanced-dependencies/' },
188
+ { label: 'Advanced Security', link: 'advanced/advanced-security/' },
189
+ { label: 'Using the Request Directly', link: 'advanced/using-request-directly/' },
190
+ { label: 'Using Dataclasses', link: 'advanced/dataclasses/' },
191
+ { label: 'Advanced Middleware', link: 'advanced/advanced-middleware/' },
192
+ { label: 'Sub Applications — Mounts', link: 'advanced/sub-applications/' },
193
+ { label: 'Behind a Proxy', link: 'advanced/behind-a-proxy/' },
194
+ { label: 'Templates (Jinja2)', link: 'advanced/templates/' },
195
+ { label: 'WebSockets', link: 'advanced/websockets-advanced/' },
196
+ { label: 'Lifespan Events', link: 'advanced/lifespan-events/' },
197
+ { label: 'Async Tests', link: 'advanced/async-tests/' },
198
+ { label: 'Settings & Environment Variables', link: 'advanced/settings/' },
199
+ { label: 'OpenAPI Callbacks & Webhooks', link: 'advanced/openapi-callbacks/' },
200
+ { label: 'Generating SDKs', link: 'advanced/generate-clients/' },
201
+ ],
202
+ },
203
+ {
204
+ label: 'How-To Recipes',
205
+ items: [
206
+ { label: 'Overview', link: 'how-to/' },
207
+ { label: 'GraphQL Integration', link: 'how-to/graphql/' },
208
+ { label: 'Custom Request & Route Classes', link: 'how-to/custom-request-route/' },
209
+ { label: 'Configure Swagger UI', link: 'how-to/configure-swagger-ui/' },
210
+ { label: 'Testing a Database', link: 'how-to/testing-database/' },
211
+ { label: 'Circuit Breaker Recipes', link: 'how-to/circuit-breaker-recipes/' },
212
+ { label: 'Background Task Patterns', link: 'how-to/background-task-patterns/' },
124
213
],
125
214
},
126
215
{
145
234
{ label: 'Database', link: 'api-reference/database/' },
146
235
],
147
236
},
148
- {
149
- label: 'Advanced Guides',
150
- items: [
151
- { label: 'Zero-GIL Architecture', link: 'advanced/zero-gil-architecture/' },
152
- { label: 'Rust Core Deep Dive', link: 'advanced/rust-core-deep-dive/' },
153
- { label: 'PyO3 & FFI Safety', link: 'advanced/pyo3-ffi-safety/' },
154
- { label: 'Native Fast Path', link: 'advanced/native-fast-path/' },
155
- { label: 'Multi-Protocol APIs', link: 'advanced/multi-protocol-apis/' },
156
- { label: 'Agent System', link: 'advanced/agent-system/' },
157
- { label: 'Streaming Output', link: 'advanced/streaming-output/' },
158
- { label: 'Performance Tuning', link: 'advanced/performance-tuning/' },
159
- { label: 'Resilience Patterns', link: 'advanced/resilience-patterns/' },
160
- ],
161
- },
162
- {
163
- label: 'Inference',
164
- items: [
165
- { label: 'Overview', link: 'inference/overview/' },
166
- { label: 'LLM Serving API', link: 'inference/llm-serving-api/' },
167
- { label: 'GPU & CUDA Setup', link: 'inference/gpu-cuda-setup/' },
168
- { label: 'Scheduling & Batching', link: 'inference/scheduling-batching/' },
169
- ],
170
- },
171
237
{
172
238
label: 'Deployment',
173
239
items: [
200
266
{ label: 'Health Checks', link: 'observability/health-checks/' },
201
267
],
202
268
},
269
+ {
270
+ label: 'Inference',
271
+ items: [
272
+ { label: 'Overview', link: 'inference/overview/' },
273
+ { label: 'LLM Serving API', link: 'inference/llm-serving-api/' },
274
+ { label: 'GPU & CUDA Setup', link: 'inference/gpu-cuda-setup/' },
275
+ { label: 'Scheduling & Batching', link: 'inference/scheduling-batching/' },
276
+ ],
277
+ },
203
278
{
204
279
label: 'Reference',
205
280
items: [
211
286
{ label: 'Glossary', link: 'reference/glossary/' },
212
287
],
213
288
},
289
+ {
290
+ label: 'Resources',
291
+ items: [
292
+ { label: 'Help & Support', link: 'resources/help/' },
293
+ { label: 'External Links & Ecosystem', link: 'resources/external-links/' },
294
+ { label: 'About JustAPI', link: 'resources/about/' },
295
+ ],
296
+ },
214
297
{
215
298
label: 'Contributing',
216
299
items: [
217
300
▼docs_site/src/content/docs/how-to/graphql.md
+95-0
1
+---
2
+title: GraphQL Integration
3
+description: Set up Strawberry GraphQL alongside REST endpoints in a JustAPI application.
4
+keywords: [JustAPI, GraphQL, Strawberry, REST, multi-protocol]
5
+---
6
+
7
+JustAPI supports running GraphQL alongside REST endpoints in the same application.
8
+
9
+## Basic Setup
10
+
11
+Install Strawberry:
12
+
13
+```bash
14
+pip install strawberry-graphql
15
+```
16
+
17
+Create your GraphQL schema:
18
+
19
+```python
20
+import strawberry
21
+from justapi import JustAPIApp
22
+
23
+@strawberry.type
24
+class User:
25
+ id: int
26
+ name: str
27
+ email: str
28
+
29
+@strawberry.type
30
+class Query:
31
+ @strawberry.field
32
+ def users(self) -> list[User]:
33
+ return [User(id=1, name="Alice", email="[email protected]")]
34
+
35
+@strawberry.type
36
+class Mutation:
37
+ @strawberry.mutation
38
+ def create_user(self, name: str, email: str) -> User:
39
+ return User(id=2, name=name, email=email)
40
+```
41
+
42
+Mount the GraphQL endpoint in your JustAPI app:
43
+
44
+```python
45
+import strawberry
46
+from strawberry.fastapi import GraphQLRouter
47
+
48
+app = JustAPIApp()
49
+
50
+schema = strawberry.Schema(query=Query, mutation=Mutation)
51
+graphql_app = GraphQLRouter(schema)
52
+
53
+app.include_router(graphql_app, prefix="/graphql")
54
+```
55
+
56
+## Testing GraphQL
57
+
58
+Start your app and visit `http://localhost:8000/graphql` for the GraphiQL interactive explorer.
59
+
60
+```graphql
61
+query {
62
+ users {
63
+ id
64
+ name
65
+ email
66
+ }
67
+}
68
+```
69
+
70
+## Shared Authentication
71
+
72
+JustAPI supports global dependencies that apply to all routes including GraphQL:
73
+
74
+```python
75
+from justapi import Depends, Security
76
+
77
+def get_current_user(request):
78
+ token = request.headers.get("Authorization", "").replace("Bearer ", "")
79
+ # validate token...
80
+ return {"user_id": 1, "name": "Alice"}
81
+
82
+app = JustAPIApp(dependencies=[Depends(get_current_user)])
83
+```
84
+
85
+## Recap
86
+
87
+- Install `strawberry-graphql` for GraphQL support.
88
+- Mount `GraphQLRouter` with a prefix using `app.include_router()`.
89
+- GraphQL runs alongside REST — no separate server needed.
90
+- Use JustAPI's dependency injection for shared auth.
91
+
92
+## See Also
93
+
94
+- [Routing & Sub-routers](/tutorials/routing-subrouters/) — multiple routers
95
+- [Security](/tutorials/security/first-steps/) — authentication
0
96
▼docs_site/src/content/docs/how-to/index.md
+24-0
1
+---
2
+title: How-To Recipes
3
+description: Standalone recipes for common tasks in JustAPI — GraphQL, custom middleware, database testing, and more.
4
+keywords: [JustAPI, how-to, recipes, tips, patterns]
5
+---
6
+
7
+These are standalone, task-oriented guides for specific JustAPI use cases. Each recipe solves a concrete problem without requiring you to read a full tutorial.
8
+
9
+## Available Recipes
10
+
11
+| Recipe | Problem Solved |
12
+|--------|----------------|
13
+| [GraphQL Integration](/how-to/graphql/) | Set up Strawberry GraphQL alongside REST endpoints |
14
+| [Custom Request & Route Classes](/how-to/custom-request-route/) | Extend `Request` and `APIRoute` for custom behavior |
15
+| [Configure Swagger UI](/how-to/configure-swagger-ui/) | Customize the interactive docs appearance |
16
+| [Testing a Database](/how-to/testing-database/) | Use in-memory SQLite for fast test isolation |
17
+| [Circuit Breaker Recipes](/how-to/circuit-breaker-recipes/) | Configure resilience patterns for real-world scenarios |
18
+| [Background Task Patterns](/how-to/background-task-patterns/) | Prioritized tasks, progress tracking, error handling |
19
+
20
+## See Also
21
+
22
+- [Tutorial — User Guide](/tutorials/hello-world/) — learn JustAPI from scratch
23
+- [Advanced User Guide](/advanced/zero-gil-architecture/) — deep-dive topics
24
+- [API Reference](/api-reference/) — complete class and function reference
0
25
▼docs_site/src/content/docs/resources/about.md
+66-0
1
+---
2
+title: About JustAPI
3
+description: The story, philosophy, and future of JustAPI — a Rust-powered Python web framework built to replace FastAPI.
4
+keywords: [JustAPI, about, history, philosophy, Rust, Python web framework]
5
+---
6
+
7
+## What is JustAPI?
8
+
9
+JustAPI is a Python web framework for building APIs, powered by a Rust core. It provides the same developer experience as FastAPI — decorator syntax, Pydantic models, dependency injection — while moving the entire networking stack out of Python.
10
+
11
+**Python writes the logic. Rust does everything else.**
12
+
13
+## Why Rust?
14
+
15
+Python is excellent for application logic. It's readable, has a rich ecosystem, and developers love it. But it has limitations for networking:
16
+
17
+- The GIL prevents true parallelism in Python handlers.
18
+- Python HTTP servers (uvicorn, gunicorn) add overhead.
19
+- TLS termination, connection management, and routing are all CPU-bound.
20
+
21
+JustAPI solves this by implementing these critical paths in Rust using:
22
+
23
+- **hyper** — HTTP/1.1 and HTTP/2 implementation
24
+- **tokio** — asynchronous runtime
25
+- **rustls** — TLS termination without OpenSSL
26
+- **matchit** — radix-trie routing
27
+- **PyO3** — seamless Python ↔ Rust boundary
28
+
29
+## Design Philosophy
30
+
31
+1. **FastAPI-compatible.** Existing FastAPI apps migrate with minimal changes.
32
+2. **Rust-first.** If it can be in Rust, it must be in Rust. Python is for application logic only.
33
+3. **Production-ready from day one.** Built-in auth, rate limiting, circuit breakers, and observability.
34
+4. **Honest performance claims.** Every benchmark number in the docs is reproducible on the documented hardware.
35
+
36
+## Performance History
37
+
38
+| Milestone | Date | Result |
39
+|-----------|------|--------|
40
+| Phase 3 | 2026-07 | 752k req/s hello-world |
41
+| Phase 20 | 2026-07 | Beat FastAPI on all benchmarks |
42
+| Phase 25 | 2026-07 | 1M+ RPS tuning |
43
+| Hot-path optimization | 2026-07-13 | 60k rps Python path, 724k rps native |
44
+| Head-to-head vs Robyn | 2026-07-13 | JustAPI ×1.54 faster |
45
+
46
+## Architecture
47
+
48
+```
49
+Kernel → epoll/io_uring
50
+ → Connection Manager (tokio)
51
+ → TLS (rustls)
52
+ → HTTP parse (hyper)
53
+ → Router (matchit)
54
+ → Middleware chain (Rust: auth, CORS, rate-limit)
55
+ → Python boundary (PyO3)
56
+ → Application code (async Python)
57
+ → Rust serializer (serde_json / simd-json)
58
+ → Response write → Socket
59
+```
60
+
61
+## See Also
62
+
63
+- [Alternatives, Inspiration & Comparisons](/resources/external-links/)
64
+- [History, Design & Future](/resources/about/)
65
+- [Benchmarks & Performance](/reference/adr-index/)
66
+- [Repository Management](/contributing/coding-standards/)
0
67
▼docs_site/src/content/docs/resources/external-links.md
+52-0
1
+---
2
+title: External Links & Ecosystem
3
+description: Related projects, community tools, and ecosystem around JustAPI.
4
+keywords: [JustAPI, ecosystem, community, related projects, integrations]
5
+---
6
+
7
+## Core Dependencies
8
+
9
+| Project | Purpose | Link |
10
+|---------|---------|------|
11
+| **Pydantic** | Data validation and serialization | [pydantic.dev](https://pydantic.dev/) |
12
+| **Starlette** | ASGI toolkit (used by JustAPI's shim) | [starlette.io](https://www.starlette.io/) |
13
+| **PyO3** | Python ↔ Rust FFI | [pyo3.rs](https://pyo3.rs/) |
14
+| **maturin** | Build Rust Python packages | [maturin.rs](https://www.maturin.rs/) |
15
+
16
+## Related Rust-Powered Frameworks
17
+
18
+| Framework | What It Does |
19
+|-----------|-------------|
20
+| **Robyn** | Rust runtime, decorator API |
21
+| **Granian** | Rust ASGI server |
22
+| **Litestar** | Python-native ASGI framework |
23
+| **FastAPI** | Python web framework (the compatibility target) |
24
+| **Typer** | CLI framework (FastAPI for CLIs) |
25
+
26
+## Community
27
+
28
+- **GitHub**: [github.com/swadhinbiswas/JustAPI](https://github.com/swadhinbiswas/JustAPI)
29
+- **Stack Overflow**: [tag: justapi](https://stackoverflow.com/questions/tagged/justapi)
30
+- **Issues**: [GitHub Issues](https://github.com/swadhinbiswas/JustAPI/issues)
31
+
32
+## Ecosystem
33
+
34
+### Database Drivers
35
+- **aiosqlite** — async SQLite
36
+- **asyncpg** — async PostgreSQL
37
+- **aiomysql** — async MySQL
38
+
39
+### Serialization
40
+- **orjson** — fast JSON (JustAPI has built-in `orjson` feature)
41
+- **simd-json** — SIMD-accelerated JSON
42
+
43
+### Testing
44
+- **pytest** — Python testing framework
45
+- **httpx** — async HTTP client for testing
46
+- **criterion** — Rust benchmarking
47
+
48
+### Deployment
49
+- **uvicorn** — ASGI server (JustAPI's ASGI shim)
50
+- **gunicorn** — pre-fork server
51
+- **Docker** — container deployment
52
+- **Kubernetes** — orchestration
0
53
▼docs_site/src/content/docs/resources/help.md
+65-0
1
+---
2
+title: Help & Support
3
+description: Where to get help with JustAPI — GitHub Issues, community support, and troubleshooting.
4
+keywords: [JustAPI, help, support, GitHub, community, troubleshooting]
5
+---
6
+
7
+## GitHub Issues
8
+
9
+For bug reports and feature requests:
10
+
11
+**[GitHub Issues](https://github.com/swadhinbiswas/JustAPI/issues)**
12
+
13
+When reporting a bug, include:
14
+- Python version (`python --version`)
15
+- JustAPI version (`python -c "import justapi; print(justapi.__version__)"`)
16
+- Operating system
17
+- Minimal code to reproduce the issue
18
+- Full error traceback
19
+
20
+## Stack Overflow
21
+
22
+For how-to questions, tag your question with `justapi`:
23
+
24
+**[Stack Overflow: justapi](https://stackoverflow.com/questions/tagged/justapi)**
25
+
26
+## GitHub Discussions
27
+
28
+For general questions, ideas, and community conversation:
29
+
30
+**[GitHub Discussions](https://github.com/swadhinbiswas/JustAPI/discussions)**
31
+
32
+## Security Vulnerabilities
33
+
34
+Do **not** open a public GitHub issue for security vulnerabilities. Instead, follow the process in the [Security Policy](/security/policy/).
35
+
36
+## Troubleshooting
37
+
38
+### ImportError: No module named '_justapi'
39
+
40
+The Rust extension wasn't built. Run `maturin develop --release`.
41
+
42
+### `pip install` fails with build error
43
+
44
+Ensure Rust is installed (`rustup --version`). Or use a pre-built wheel:
45
+
46
+```bash
47
+pip install --only-binary=justapi justapi
48
+```
49
+
50
+### Handler returns validation error for valid data
51
+
52
+Check that your Pydantic model fields match the request body exactly. All fields without defaults are required.
53
+
54
+### Port already in use
55
+
56
+```bash
57
+# Kill process on port 8000
58
+lsof -ti:8000 | xargs kill -9
59
+```
60
+
61
+Or use a different port:
62
+
63
+```python
64
+app.run("127.0.0.1", 9000)
65
+```
0
66
▼docs_site/src/content/docs/tutorials/async-await.md
+138-0
1
+---
2
+title: Concurrency and Async / Await
3
+description: Understanding async and await in Python and when to use async def vs def in JustAPI handlers.
4
+keywords: [JustAPI, async, await, concurrency, Python async, FastAPI alternative]
5
+---
6
+
7
+JustAPI supports both regular functions (`def`) and asynchronous functions (`async def`) as request handlers. This page explains the difference and when to use each.
8
+
9
+## Synchronous vs Asynchronous
10
+
11
+```python
12
+from justapi import JustAPIApp
13
+
14
+app = JustAPIApp()
15
+
16
+# Synchronous handler (def)
17
+@app.get("/sync")
18
+def sync_handler(request):
19
+ return {"message": "Synchronous"}
20
+
21
+# Asynchronous handler (async def)
22
+@app.get("/async")
23
+async def async_handler(request):
24
+ return {"message": "Asynchronous"}
25
+```
26
+
27
+Both work identically from the user's perspective. The difference is internal.
28
+
29
+### `def` — Synchronous
30
+
31
+JustAPI runs synchronous handlers in a dedicated thread pool so they don't block the Rust event loop. This is handled automatically by the framework.
32
+
33
+Use `def` when your handler does CPU-bound work or calls blocking libraries:
34
+
35
+```python
36
+import time
37
+
38
+@app.get("/cpu-work")
39
+def cpu_work(request):
40
+ # Simulate CPU-bound work
41
+ result = sum(i * i for i in range(10_000))
42
+ return {"result": result}
43
+```
44
+
45
+### `async def` — Asynchronous
46
+
47
+Async handlers run directly on the Rust async runtime. They can `await` other async operations without blocking.
48
+
49
+Use `async def` when your handler does I/O-bound work (network calls, database queries, file I/O):
50
+
51
+```python
52
+import httpx
53
+
54
+@app.get("/fetch")
55
+async def fetch_data(request):
56
+ async with httpx.AsyncClient() as client:
57
+ response = await client.get("https://api.example.com/data")
58
+ return response.json()
59
+```
60
+
61
+## When to Use Which
62
+
63
+| Scenario | Use | Why |
64
+|----------|-----|-----|
65
+| Calling a database ORM (sqlx, SQLAlchemy async) | `async def` | Avoids blocking the runtime |
66
+| HTTP calls to external APIs | `async def` | Non-blocking I/O |
67
+| Simple data transformation | `def` | Cleaner code, no async overhead |
68
+| CPU-bound computation | `def` | Thread pool handles it |
69
+| Mixed I/O + computation | `async def` | Better concurrency |
70
+| Using blocking libraries | `def` | Thread pool prevents blocking |
71
+
72
+:::tip
73
+If you're unsure, use `async def`. It's the modern default and works for all cases. Only use `def` if you're calling blocking code directly.
74
+:::
75
+
76
+## JustAPI's Concurrency Model
77
+
78
+JustAPI uses a Rust async runtime (tokio) for all request handling. The Python handler boundary is designed to avoid GIL contention:
79
+
80
+- **`async def`** handlers run on the tokio async runtime directly.
81
+- **`def`** handlers run in a dedicated Python worker thread pool (GIL pool).
82
+- The native fast path (`native=True`) bypasses Python entirely — no GIL, no threads.
83
+
84
+This means you get true concurrency for async handlers even on standard CPython, because the GIL is released during Python handler execution.
85
+
86
+## Async Database Access
87
+
88
+```python
89
+import sqlite3
90
+from justapi import JustAPIApp
91
+
92
+app = JustAPIApp()
93
+
94
+@app.get("/users")
95
+async def list_users(request):
96
+ # Use an async database client
97
+ import aiosqlite
98
+ async with aiosqlite.connect("users.db") as db:
99
+ async with db.execute("SELECT * FROM users") as cursor:
100
+ users = await cursor.fetchall()
101
+ return {"users": users}
102
+```
103
+
104
+## Parallel Async Operations
105
+
106
+Async handlers can run multiple I/O operations concurrently:
107
+
108
+```python
109
+import httpx
110
+
111
+@app.get("/aggregate")
112
+async def aggregate(request):
113
+ async with httpx.AsyncClient() as client:
114
+ # Run both requests concurrently
115
+ users_task = client.get("https://api.example.com/users")
116
+ items_task = client.get("https://api.example.com/items")
117
+
118
+ users_response = await users_task
119
+ items_response = await items_task
120
+
121
+ return {
122
+ "users": users_response.json(),
123
+ "items": items_response.json(),
124
+ }
125
+```
126
+
127
+## Summary
128
+
129
+- Use `def` for synchronous, blocking, or CPU-bound work.
130
+- Use `async def` for I/O-bound work that benefits from concurrency.
131
+- JustAPI handles both correctly — you don't need to worry about blocking the runtime.
132
+- For maximum throughput, consider the native fast path (`native=True`) which bypasses Python entirely.
133
+
134
+## See Also
135
+
136
+- [First Steps](/tutorials/hello-world/) — your first JustAPI app
137
+- [Python Types Intro](/tutorials/python-types/) — type hints in JustAPI
138
+- [Background Tasks](/tutorials/background-tasks/) — post-response async work
0
139
▼docs_site/src/content/docs/tutorials/body-updates.md
+63-0
1
+---
2
+title: Body — Updates
3
+description: Implement partial updates (PATCH) with Pydantic models in JustAPI.
4
+keywords: [JustAPI, PATCH, partial update, body updates, Pydantic]
5
+---
6
+
7
+## PUT vs PATCH
8
+
9
+- **PUT** — full replacement. The client sends the entire resource.
10
+- **PATCH** — partial update. The client sends only the fields to change.
11
+
12
+## Full Update with PUT
13
+
14
+```python
15
+from pydantic import BaseModel
16
+from justapi import JustAPIApp
17
+
18
+class Item(BaseModel):
19
+ name: str
20
+ price: float
21
+ description: str = ""
22
+
23
+app = JustAPIApp()
24
+
25
+@app.put("/items/{item_id}")
26
+def update_item(item_id: int, item: Item):
27
+ return {"item_id": item_id, **item.model_dump()}
28
+```
29
+
30
+## Partial Update with PATCH
31
+
32
+Use `exclude_unset=True` to get only the fields the client explicitly sent:
33
+
34
+```python
35
+@app.patch("/items/{item_id}")
36
+def partial_update(item_id: int, item: Item):
37
+ update_data = item.model_dump(exclude_unset=True)
38
+ # update_data only contains fields the client sent
39
+ return {"item_id": item_id, "updates": update_data}
40
+```
41
+
42
+If the client sends `{"name": "New Name"}`, only `name` is in `update_data`.
43
+
44
+## Optional Model
45
+
46
+```python
47
+from typing import Optional
48
+
49
+class ItemUpdate(BaseModel):
50
+ name: Optional[str] = None
51
+ price: Optional[float] = None
52
+ description: Optional[str] = None
53
+
54
+@app.patch("/items/{item_id}")
55
+def partial_update(item_id: int, item: ItemUpdate):
56
+ update_data = item.model_dump(exclude_unset=True)
57
+ return {"item_id": item_id, "updates": update_data}
58
+```
59
+
60
+## See Also
61
+
62
+- [Request Body](/tutorials/request-body/) — full request body usage
63
+- [Body — Multiple Parameters](/tutorials/body-multiple-params/) — mixing parameter sources
0
64
▼docs_site/src/content/docs/tutorials/cookie-params.md
+53-0
1
+---
2
+title: Cookie Parameters
3
+description: Read and validate HTTP cookies in JustAPI.
4
+keywords: [JustAPI, cookie parameters, cookies, session, HTTP cookies]
5
+---
6
+
7
+## Basic Cookie Parameters
8
+
9
+```python
10
+from justapi import JustAPIApp, Cookie
11
+
12
+app = JustAPIApp()
13
+
14
+@app.get("/check")
15
+def check_session(session_id: str = Cookie(...)):
16
+ return {"session_id": session_id}
17
+```
18
+
19
+## Required vs Optional
20
+
21
+```python
22
+@app.get("/check")
23
+def check(
24
+ session_id: str = Cookie(...), # required
25
+ language: str = Cookie("en"), # optional, defaults to "en"
26
+ theme: str = Cookie(None), # optional, defaults to None
27
+):
28
+ return {"session_id": session_id, "language": language, "theme": theme}
29
+```
30
+
31
+## Cookie Models
32
+
33
+```python
34
+from pydantic import BaseModel
35
+from justapi import JustAPIApp, Cookie
36
+
37
+class Cookies(BaseModel):
38
+ session_id: str
39
+ language: str = "en"
40
+ theme: str = "light"
41
+
42
+app = JustAPIApp()
43
+
44
+@app.get("/check")
45
+def check(cookies: Cookies = Cookie(...)):
46
+ return cookies.model_dump()
47
+```
48
+
49
+## See Also
50
+
51
+- [Header Parameters](/tutorials/header-params/) — reading HTTP headers
52
+- [Query Parameters](/tutorials/query-params/) — URL query parameters
53
+- [Security](/tutorials/security/first-steps/) — authentication with cookies
0
54
▼docs_site/src/content/docs/tutorials/cors.md
+77-0
1
+---
2
+title: CORS (Cross-Origin Resource Sharing)
3
+description: Configure CORS headers in JustAPI to allow requests from other domains.
4
+keywords: [JustAPI, CORS, cross-origin, security, browser]
5
+---
6
+
7
+## Basic CORS
8
+
9
+CORS (Cross-Origin Resource Sharing) allows browsers to make requests to your API from different domains. Without CORS, browsers block these requests.
10
+
11
+```python
12
+from justapi import JustAPIApp
13
+
14
+app = JustAPIApp()
15
+
16
+app.add_middleware(
17
+ CORSMiddleware,
18
+ allow_origins=["https://example.com"],
19
+ allow_credentials=True,
20
+ allow_methods=["*"],
21
+ allow_headers=["*"],
22
+)
23
+```
24
+
25
+## Permissive CORS (Development)
26
+
27
+For local development, allow all origins:
28
+
29
+```python
30
+from justapi import JustAPIApp
31
+from starlette.middleware.cors import CORSMiddleware
32
+
33
+app = JustAPIApp()
34
+
35
+app.add_middleware(
36
+ CORSMiddleware,
37
+ allow_origins=["*"],
38
+ allow_methods=["*"],
39
+ allow_headers=["*"],
40
+)
41
+```
42
+
43
+:::warning
44
+Never use `allow_origins=["*"]` in production. It defeats the purpose of CORS restrictions.
45
+:::
46
+
47
+## Restrictive CORS (Production)
48
+
49
+In production, specify exactly which origins are allowed:
50
+
51
+```python
52
+app.add_middleware(
53
+ CORSMiddleware,
54
+ allow_origins=[
55
+ "https://app.example.com",
56
+ "https://admin.example.com",
57
+ ],
58
+ allow_credentials=True,
59
+ allow_methods=["GET", "POST", "PUT", "DELETE"],
60
+ allow_headers=["Authorization", "Content-Type"],
61
+)
62
+```
63
+
64
+## Options
65
+
66
+| Parameter | Type | Description |
67
+|-----------|------|-------------|
68
+| `allow_origins` | `list[str]` | Allowed origins (`["*"]` for all) |
69
+| `allow_methods` | `list[str]` | HTTP methods to allow |
70
+| `allow_headers` | `list[str]` | Request headers to allow |
71
+| `allow_credentials` | `bool` | Allow cookies and auth headers |
72
+| `expose_headers` | `list[str]` | Headers the browser can read |
73
+
74
+## See Also
75
+
76
+- [Middleware](/tutorials/middleware/) — general middleware usage
77
+- [Security Policy](/security/policy/) — security best practices
0
78
▼docs_site/src/content/docs/tutorials/debugging.md
+88-0
1
+---
2
+title: Debugging
3
+description: Debug JustAPI applications with detailed error messages, logging, and common troubleshooting patterns.
4
+keywords: [JustAPI, debugging, errors, logging, troubleshooting]
5
+---
6
+
7
+## Error Messages
8
+
9
+JustAPI returns structured error responses for validation failures:
10
+
11
+```json
12
+{
13
+ "detail": [
14
+ {
15
+ "type": "int_parsing",
16
+ "loc": ["path", "item_id"],
17
+ "msg": "Input should be a valid integer",
18
+ "input": "foo"
19
+ }
20
+ ]
21
+}
22
+```
23
+
24
+The `detail` array contains every validation error with:
25
+- `type` — error category
26
+- `loc` — where the error occurred (`["path", "body", "query", "header"]`)
27
+- `msg` — human-readable message
28
+- `input` — the value that was rejected
29
+
30
+## Debug Mode
31
+
32
+```python
33
+app = JustAPIApp(debug=True)
34
+```
35
+
36
+Debug mode enables detailed tracebacks in responses.
37
+
38
+:::warning
39
+Never use `debug=True` in production. It exposes internal details.
40
+:::
41
+
42
+## Common Errors
43
+
44
+### 422 Validation Error
45
+
46
+```python
47
+@app.get("/items/{item_id}")
48
+def get_item(item_id: int):
49
+ return {"item_id": item_id}
50
+```
51
+
52
+`GET /items/foo` returns 422 because `"foo"` is not a valid `int`.
53
+
54
+**Fix:** Ensure the URL value matches the declared type.
55
+
56
+### 404 Not Found
57
+
58
+`GET /nonexistent` returns 404 with no body.
59
+
60
+**Fix:** Check your route path and method.
61
+
62
+### 405 Method Not Allowed
63
+
64
+`POST /items` when only `@app.get("/items")` is defined returns 405.
65
+
66
+**Fix:** Add the missing HTTP method decorator.
67
+
68
+## Logging
69
+
70
+Use standard Python logging with JustAPI:
71
+
72
+```python
73
+import logging
74
+
75
+logging.basicConfig(level=logging.INFO)
76
+logger = logging.getLogger(__name__)
77
+
78
+@app.get("/items")
79
+def list_items():
80
+ logger.info("Listing items")
81
+ return {"items": []}
82
+```
83
+
84
+## See Also
85
+
86
+- [Testing](/tutorials/testing/) — automated tests
87
+- [Error Codes](/reference/error-codes/) — complete error code reference
88
+- [Security Policy](/security/policy/) — reporting security issues
0
89
▼docs_site/src/content/docs/tutorials/dependencies/classes-as-dependencies/index.md
+49-0
1
+---
2
+title: Classes as Dependencies
3
+description: Use Python classes as dependencies in JustAPI for encapsulated logic with state.
4
+keywords: [JustAPI, dependencies, classes, dependency injection]
5
+---
6
+
7
+## Callable Dependencies as Classes
8
+
9
+Instead of plain functions, use classes as dependencies when you need to encapsulate state:
10
+
11
+```python
12
+from justapi import JustAPIApp, Depends
13
+
14
+class PaginationParams:
15
+ def __init__(self, skip: int = 0, limit: int = 10):
16
+ self.skip = skip
17
+ self.limit = limit
18
+ self.offset = skip * limit # computed property
19
+
20
+@app.get("/items/")
21
+def list_items(pagination: PaginationParams = Depends()):
22
+ return {"skip": pagination.skip, "limit": pagination.limit, "offset": pagination.offset}
23
+```
24
+
25
+JustAPI calls `PaginationParams(skip=..., limit=...)` automatically — the parameters come from query params.
26
+
27
+## Class with Dependencies
28
+
29
+```python
30
+class DBSession:
31
+ def __init__(self, db_url: str):
32
+ self.db_url = db_url
33
+ # In production, create connection pool
34
+
35
+ def execute(self, query: str):
36
+ return []
37
+
38
+def get_db():
39
+ return DBSession("sqlite:///app.db")
40
+
41
+@app.get("/users/")
42
+def list_users(db: DBSession = Depends(get_db)):
43
+ return db.execute("SELECT * FROM users")
44
+```
45
+
46
+## See Also
47
+
48
+- [Sub-dependencies](/tutorials/dependencies/sub-dependencies/) — dependencies of dependencies
49
+- [Dependencies with yield](/tutorials/dependencies/dependencies-with-yield/) — cleanup patterns
0
50
▼docs_site/src/content/docs/tutorials/dependencies/dependencies-in-path-ops/index.md
+38-0
1
+---
2
+title: Dependencies in Path Operation Decorators
3
+description: Declare dependencies directly in the route decorator instead of as function parameters.
4
+keywords: [JustAPI, dependencies, route decorator, dependency injection]
5
+---
6
+
7
+## Decorator-Level Dependencies
8
+
9
+You can declare dependencies in the decorator itself — they won't appear as function parameters:
10
+
11
+```python
12
+from justapi import JustAPIApp, Depends
13
+
14
+app = JustAPIApp()
15
+
16
+def verify_api_key():
17
+ api_key = request.headers.get("X-API-Key")
18
+ if api_key != "secret-key":
19
+ raise HTTPException(status_code=403)
20
+ return True
21
+
22
+@app.get("/items/", dependencies=[Depends(verify_api_key)])
23
+def list_items():
24
+ return {"items": []}
25
+```
26
+
27
+The `verify_api_key` runs before `list_items`, but `list_items` doesn't receive the result as a parameter.
28
+
29
+## Useful For
30
+
31
+- Authentication checks where the handler doesn't need the result
32
+- Logging or rate limiting
33
+- Cross-cutting concerns that apply to many routes
34
+
35
+## See Also
36
+
37
+- [Global Dependencies](/tutorials/dependencies/global-dependencies/) — app-wide deps
38
+- [Security](/tutorials/security/first-steps/) — authentication patterns
0
39
▼docs_site/src/content/docs/tutorials/dependencies/dependencies-with-yield/index.md
+57-0
1
+---
2
+title: Dependencies with yield
3
+description: Use yield dependencies in JustAPI for setup/teardown patterns like database connections.
4
+keywords: [JustAPI, dependencies, yield, setup, teardown, cleanup]
5
+---
6
+
7
+## Setup/Teardown with yield
8
+
9
+Use `yield` in dependencies to perform cleanup after the request:
10
+
11
+```python
12
+from justapi import JustAPIApp, Depends
13
+
14
+def get_db_session():
15
+ session = create_session()
16
+ try:
17
+ yield session
18
+ finally:
19
+ session.close()
20
+
21
+app = JustAPIApp()
22
+
23
+@app.get("/users/")
24
+def list_users(db = Depends(get_db_session)):
25
+ users = db.query("SELECT * FROM users")
26
+ return {"users": users}
27
+```
28
+
29
+The code before `yield` runs before the request. The code after `yield` runs after the request (even if it raises an exception).
30
+
31
+## Database Connection Pool
32
+
33
+```python
34
+def get_db():
35
+ db = DBConnection("sqlite:///app.db")
36
+ try:
37
+ yield db
38
+ finally:
39
+ db.disconnect()
40
+```
41
+
42
+## Multiple Cleanup Steps
43
+
44
+```python
45
+def get_resource():
46
+ resource = acquire_resource()
47
+ try:
48
+ yield resource
49
+ finally:
50
+ release_resource(resource)
51
+ cleanup_temp_files()
52
+```
53
+
54
+## See Also
55
+
56
+- [SQL Databases](/tutorials/database-integration/) — database integration
57
+- [Classes as Dependencies](/tutorials/dependencies/classes-as-dependencies/) — class-based deps
0
58
▼docs_site/src/content/docs/tutorials/dependencies/global-dependencies/index.md
+71-0
1
+---
2
+title: Global Dependencies
3
+description: Apply dependencies to every route in your JustAPI application.
4
+keywords: [JustAPI, global dependencies, app-wide, middleware]
5
+---
6
+
7
+## App-Level Dependencies
8
+
9
+Declare dependencies that apply to all routes:
10
+
11
+```python
12
+from justapi import JustAPIApp, Depends
13
+
14
+def verify_token():
15
+ token = request.headers.get("Authorization", "")
16
+ if not token:
17
+ raise HTTPException(status_code=401)
18
+ return True
19
+
20
+app = JustAPIApp(dependencies=[Depends(verify_token)])
21
+
22
+@app.get("/public")
23
+def public_route():
24
+ # verify_token still runs
25
+ return {"message": "This requires auth too"}
26
+```
27
+
28
+## Router-Level Dependencies
29
+
30
+Apply to all routes in a specific router:
31
+
32
+```python
33
+from justapi import APIRouter
34
+
35
+router = APIRouter(dependencies=[Depends(verify_token)])
36
+
37
+@router.get("/items/")
38
+def list_items():
39
+ return []
40
+
41
+@router.post("/items/")
42
+def create_item():
43
+ return {}
44
+
45
+app.include_router(router, prefix="/api")
46
+```
47
+
48
+## Mixing Global + Local
49
+
50
+```python
51
+def require_auth():
52
+ # authentication check
53
+ pass
54
+
55
+def require_admin():
56
+ # admin check
57
+ pass
58
+
59
+# App-wide auth
60
+app = JustAPIApp(dependencies=[Depends(require_auth)])
61
+
62
+# This route also requires admin
63
+@app.get("/admin/users", dependencies=[Depends(require_admin)])
64
+def admin_users():
65
+ return []
66
+```
67
+
68
+## See Also
69
+
70
+- [Dependencies in Path Ops](/tutorials/dependencies/dependencies-in-path-ops/) — route-level deps
71
+- [Security](/tutorials/security/first-steps/) — authentication patterns
0
72
▼docs_site/src/content/docs/tutorials/dependencies/sub-dependencies/index.md
+51-0
1
+---
2
+title: Sub-dependencies
3
+description: Chain dependencies in JustAPI where one dependency requires another.
4
+keywords: [JustAPI, sub-dependencies, dependency chain, dependency injection]
5
+---
6
+
7
+## Chaining Dependencies
8
+
9
+Dependencies can depend on other dependencies. JustAPI resolves the entire chain automatically:
10
+
11
+```python
12
+from justapi import JustAPIApp, Depends
13
+
14
+app = JustAPIApp()
15
+
16
+# Level 1: get token from header
17
+def get_token(authorization: str = Header(...)):
18
+ return authorization.replace("Bearer ", "")
19
+
20
+# Level 2: decode token to get user
21
+def get_user(token: str = Depends(get_token)):
22
+ # In production, decode JWT
23
+ return {"user_id": 1, "name": "Alice"}
24
+
25
+# Level 3: use user in handler
26
+@app.get("/profile")
27
+def profile(user: dict = Depends(get_user)):
28
+ return {"user": user["name"]}
29
+```
30
+
31
+JustAPI resolves `get_token` → `get_user` → `profile` automatically.
32
+
33
+## Sharing Dependencies
34
+
35
+```python
36
+def get_db():
37
+ return DatabaseConnection()
38
+
39
+def get_current_user(db: DBSession = Depends(get_db)):
40
+ return db.query_user()
41
+
42
+def get_admin_user(user: dict = Depends(get_current_user)):
43
+ if user.get("role") != "admin":
44
+ raise HTTPException(status_code=403)
45
+ return user
46
+```
47
+
48
+## See Also
49
+
50
+- [Classes as Dependencies](/tutorials/dependencies/classes-as-dependencies/) — class-based deps
51
+- [Global Dependencies](/tutorials/dependencies/global-dependencies/) — app-wide deps
0
52
▼docs_site/src/content/docs/tutorials/encoder.md
+47-0
1
+---
2
+title: JSON Compatible Encoder
3
+description: Convert Pydantic models and complex types to JSON-compatible dictionaries in JustAPI.
4
+keywords: [JustAPI, encoder, jsonable_encoder, serialization, Pydantic]
5
+---
6
+
7
+## The Problem
8
+
9
+When you need to convert Pydantic models to JSON-compatible dictionaries (for example, before passing to a database driver), you can't just call `model_dump()` — it doesn't handle `datetime`, `UUID`, or other non-JSON types.
10
+
11
+## JustAPI's Encoder
12
+
13
+```python
14
+from justapi import JustAPIApp
15
+from pydantic import BaseModel
16
+from datetime import datetime
17
+
18
+class Event(BaseModel):
19
+ name: str
20
+ starts: datetime
21
+
22
+app = JustAPIApp()
23
+
24
+@app.post("/events")
25
+def create_event(event: Event):
26
+ # Convert to JSON-compatible dict
27
+ data = event.model_dump()
28
+ # starts is a datetime object — need to serialize it
29
+ return {"event_name": data["name"], "starts": data["starts"].isoformat()}
30
+```
31
+
32
+## Model_dump with mode
33
+
34
+Pydantic v2 provides `model_dump()` with serialization modes:
35
+
36
+```python
37
+@app.post("/events")
38
+def create_event(event: Event):
39
+ data = event.model_dump(mode="json")
40
+ # data["starts"] is now an ISO string
41
+ return data
42
+```
43
+
44
+## See Also
45
+
46
+- [Request Body](/tutorials/request-body/) — Pydantic models
47
+- [Response Model](/tutorials/response-model/) — filtering output
0
48
▼docs_site/src/content/docs/tutorials/extra-data-types.md
+91-0
1
+---
2
+title: Extra Data Types
3
+description: Advanced Python data types supported by JustAPI — UUID, datetime, Enum, and more.
4
+keywords: [JustAPI, data types, UUID, datetime, Enum, Decimal, extra types]
5
+---
6
+
7
+Beyond basic types, JustAPI supports all standard library and Pydantic types:
8
+
9
+## UUID
10
+
11
+```python
12
+from uuid import UUID
13
+from justapi import JustAPIApp
14
+
15
+app = JustAPIApp()
16
+
17
+@app.get("/users/{user_id}")
18
+def get_user(user_id: UUID):
19
+ return {"user_id": str(user_id)}
20
+```
21
+
22
+`GET /users/550e8400-e29b-41d4-a716-446655440000` validates the UUID format.
23
+
24
+## datetime and date
25
+
26
+```python
27
+from datetime import datetime, date
28
+from justapi import JustAPIApp
29
+
30
+app = JustAPIApp()
31
+
32
+@app.post("/events")
33
+def create_event(start: datetime, end: date = None):
34
+ return {"start": start.isoformat(), "end": end.isoformat() if end else None}
35
+```
36
+
37
+Accepts ISO 8601 strings:
38
+- `datetime`: `"2026-07-25T10:30:00Z"`
39
+- `date`: `"2026-07-25"`
40
+
41
+## Enum
42
+
43
+```python
44
+from enum import Enum
45
+from justapi import JustAPIApp
46
+
47
+class Priority(str, Enum):
48
+ low = "low"
49
+ medium = "medium"
50
+ high = "high"
51
+
52
+app = JustAPIApp()
53
+
54
+@app.post("/tasks")
55
+def create_task(priority: Priority):
56
+ return {"priority": priority.value}
57
+```
58
+
59
+The docs will show a dropdown with the allowed values.
60
+
61
+## Decimal
62
+
63
+```python
64
+from decimal import Decimal
65
+from justapi import JustAPIApp
66
+
67
+app = JustAPIApp()
68
+
69
+@app.post("/payments")
70
+def process_payment(amount: Decimal):
71
+ return {"amount": str(amount)}
72
+```
73
+
74
+## Literal Types
75
+
76
+```python
77
+from typing import Literal
78
+from justapi import JustAPIApp
79
+
80
+app = JustAPIApp()
81
+
82
+@app.get("/status")
83
+def get_status(status: Literal["active", "inactive"]):
84
+ return {"status": status}
85
+```
86
+
87
+## See Also
88
+
89
+- [Python Types Intro](/tutorials/python-types/) — basic types
90
+- [Query Parameters & String Validations](/tutorials/query-params-str-validations/) — string validation
91
+- [Path Parameters & Numeric Validations](/tutorials/path-params-numeric-validations/) — numeric validation
0
92
▼docs_site/src/content/docs/tutorials/extra-models.md
+59-0
1
+---
2
+title: Extra Models
3
+description: Define multiple related Pydantic models for different API operations in JustAPI.
4
+keywords: [JustAPI, Pydantic, multiple models, inheritance, schemas]
5
+---
6
+
7
+## Why Multiple Models?
8
+
9
+Different operations need different data shapes:
10
+- **Create** — input model with required fields
11
+- **Read** — output model with ID and timestamps
12
+- **Update** — optional fields for partial updates
13
+
14
+```python
15
+from pydantic import BaseModel
16
+from typing import Optional
17
+
18
+class ItemBase(BaseModel):
19
+ name: str
20
+ description: Optional[str] = None
21
+
22
+class ItemCreate(ItemBase):
23
+ price: float
24
+
25
+class ItemRead(ItemBase):
26
+ id: int
27
+ in_stock: bool
28
+
29
+class ItemUpdate(BaseModel):
30
+ name: Optional[str] = None
31
+ price: Optional[float] = None
32
+```
33
+
34
+## Using Different Models
35
+
36
+```python
37
+from justapi import JustAPIApp
38
+
39
+app = JustAPIApp()
40
+
41
+@app.post("/items", response_model=ItemRead, status_code=201)
42
+def create_item(item: ItemCreate):
43
+ # In production, save to database
44
+ return {**item.model_dump(), "id": 1, "in_stock": True}
45
+
46
+@app.get("/items/{item_id}", response_model=ItemRead)
47
+def get_item(item_id: int):
48
+ return {"id": item_id, "name": "Widget", "price": 9.99, "in_stock": True}
49
+
50
+@app.patch("/items/{item_id}", response_model=ItemRead)
51
+def update_item(item_id: int, item: ItemUpdate):
52
+ return {**item.model_dump(), "id": item_id, "in_stock": True}
53
+```
54
+
55
+## See Also
56
+
57
+- [Response Model](/tutorials/response-model/) — filtering output
58
+- [Body — Nested Models](/tutorials/body-nested-models/) — nested Pydantic models
59
+- [Request Body](/tutorials/request-body/) — Pydantic model usage
0
60
▼docs_site/src/content/docs/tutorials/form-data.md
+51-0
1
+---
2
+title: Form Data
3
+description: Handle HTML form submissions and form data in JustAPI.
4
+keywords: [JustAPI, form data, form submission, HTML forms]
5
+---
6
+
7
+## Basic Form Data
8
+
9
+Use `Form(...)` to receive form data:
10
+
11
+```python
12
+from justapi import JustAPIApp, Form
13
+
14
+app = JustAPIApp()
15
+
16
+@app.post("/login")
17
+def login(username: str = Form(...), password: str = Form(...)):
18
+ return {"username": username}
19
+```
20
+
21
+This accepts `application/x-www-form-urlencoded` requests.
22
+
23
+## Required vs Optional
24
+
25
+```python
26
+@app.post("/items")
27
+def create_item(
28
+ name: str = Form(...), # required
29
+ description: str = Form(None), # optional
30
+):
31
+ return {"name": name, "description": description}
32
+```
33
+
34
+## Multiple Form Fields
35
+
36
+```python
37
+@app.post("/submit")
38
+def submit_form(
39
+ title: str = Form(...),
40
+ body: str = Form(...),
41
+ tags: str = Form(""),
42
+ publish: bool = Form(False),
43
+):
44
+ return {"title": title, "body": body, "tags": tags, "publish": publish}
45
+```
46
+
47
+## See Also
48
+
49
+- [Form Models](/tutorials/form-models/) — Pydantic models for forms
50
+- [Request Files](/tutorials/request-files/) — file uploads
51
+- [Request Body](/tutorials/request-body/) — JSON request bodies
0
52
▼docs_site/src/content/docs/tutorials/form-models.md
+54-0
1
+---
2
+title: Form Models
3
+description: Use Pydantic models for form data validation in JustAPI.
4
+keywords: [JustAPI, form models, Pydantic forms, form validation]
5
+---
6
+
7
+## Pydantic Models for Forms
8
+
9
+Use `Body(..., embed=True)` with Pydantic models for complex form data:
10
+
11
+```python
12
+from pydantic import BaseModel
13
+from justapi import JustAPIApp, Form
14
+
15
+class LoginForm(BaseModel):
16
+ username: str
17
+ password: str
18
+ remember_me: bool = False
19
+
20
+app = JustAPIApp()
21
+
22
+@app.post("/login")
23
+def login(form: LoginForm = Form(...)):
24
+ return {"username": form.username, "remember_me": form.remember_me}
25
+```
26
+
27
+:::tip
28
+When using Pydantic models with `Form(...)`, the `embed=True` parameter is required by Pydantic to correctly parse the form fields.
29
+:::
30
+
31
+## File + Form Combination
32
+
33
+```python
34
+from justapi import JustAPIApp, Form, UploadFile
35
+
36
+class CreateItemWithImage(BaseModel):
37
+ name: str
38
+ description: str = ""
39
+
40
+app = JustAPIApp()
41
+
42
+@app.post("/items/")
43
+async def create_item(
44
+ item: CreateItemWithImage = Form(...),
45
+ image: UploadFile = File(...),
46
+):
47
+ return {"item": item.model_dump(), "filename": image.filename}
48
+```
49
+
50
+## See Also
51
+
52
+- [Form Data](/tutorials/form-data/) — basic form fields
53
+- [Request Files](/tutorials/request-files/) — file uploads
54
+- [Request Body](/tutorials/request-body/) — JSON request bodies
0
55
▼docs_site/src/content/docs/tutorials/header-params.md
+61-0
1
+---
2
+title: Header Parameters
3
+description: Read and validate HTTP header parameters in JustAPI.
4
+keywords: [JustAPI, header parameters, HTTP headers, validation]
5
+---
6
+
7
+## Basic Header Parameters
8
+
9
+```python
10
+from justapi import JustAPIApp, Header
11
+
12
+app = JustAPIApp()
13
+
14
+@app.get("/items/")
15
+def read_items(x_token: str = Header(...)):
16
+ return {"x_token": x_token}
17
+```
18
+
19
+The header name `X-Token` is automatically converted to `x_token` (underscores).
20
+
21
+## Multiple Headers
22
+
23
+```python
24
+@app.get("/users/")
25
+def read_users(
26
+ x_token: str = Header(...),
27
+ x_request_id: str = Header(None),
28
+):
29
+ return {"x_token": x_token, "x_request_id": x_request_id}
30
+```
31
+
32
+## Required vs Optional
33
+
34
+- `Header(...)` — required (the `...` means "no default, required")
35
+- `Header(None)` — optional, defaults to `None`
36
+- `Header("default")` — optional, defaults to `"default"`
37
+
38
+## Underscore Conversion
39
+
40
+By default, JustAPI converts dashes to underscores in header names. To disable this:
41
+
42
+```python
43
+@app.get("/items/")
44
+def read_items(custom_header: str = Header(..., convert_underscores=False)):
45
+ return {"custom_header": custom_header}
46
+```
47
+
48
+## Duplicate Headers
49
+
50
+For headers that can repeat (like `Set-Cookie`):
51
+
52
+```python
53
+@app.get("/items/")
54
+def read_items(x_token_list: list[str] = Header(...)):
55
+ return {"x_token_list": x_token_list}
56
+```
57
+
58
+## See Also
59
+
60
+- [Cookie Parameters](/tutorials/cookie-params/) — reading cookies
61
+- [Request Body](/tutorials/request-body/) — JSON request bodies
0
62
▼docs_site/src/content/docs/tutorials/metadata.md
+71-0
1
+---
2
+title: Metadata and Docs URLs
3
+description: Configure OpenAPI metadata, documentation URLs, and API information in JustAPI.
4
+keywords: [JustAPI, metadata, OpenAPI, Swagger, docs URLs, API info]
5
+---
6
+
7
+## App Metadata
8
+
9
+JustAPI automatically generates OpenAPI 3.1 documentation. Configure the metadata displayed in the docs:
10
+
11
+```python
12
+from justapi import JustAPIApp
13
+
14
+app = JustAPIApp(
15
+ title="My API",
16
+ version="1.0.0",
17
+ description="A production API built with JustAPI",
18
+ summary="My API summary",
19
+ contact={"name": "Support", "email": "[email protected]"},
20
+ license_info={"name": "MIT", "url": "https://opensource.org/licenses/MIT"},
21
+)
22
+```
23
+
24
+## Documentation URLs
25
+
26
+```python
27
+app = JustAPIApp(
28
+ docs_url="/docs", # Swagger UI (default: /docs)
29
+ redoc_url="/redoc", # ReDoc (default: /redoc)
30
+ openapi_url="/openapi.json", # OpenAPI schema (default: /openapi.json)
31
+)
32
+```
33
+
34
+Set any URL to `None` to disable that endpoint:
35
+
36
+```python
37
+app = JustAPIApp(
38
+ docs_url=None, # disable Swagger UI
39
+ redoc_url=None, # disable ReDoc
40
+)
41
+```
42
+
43
+## Tags
44
+
45
+Tags group related endpoints in the docs:
46
+
47
+```python
48
+@app.get("/users/", tags=["users"])
49
+def list_users():
50
+ return []
51
+
52
+@app.post("/items/", tags=["items"])
53
+def create_item():
54
+ return {}
55
+```
56
+
57
+Configure tag descriptions in the app constructor:
58
+
59
+```python
60
+app = JustAPIApp(
61
+ openapi_tags=[
62
+ {"name": "users", "description": "User management"},
63
+ {"name": "items", "description": "Item catalog"},
64
+ ],
65
+)
66
+```
67
+
68
+## See Also
69
+
70
+- [FastAPI CLI](/getting-started/cli-scaffolder/) — project scaffolding
71
+- [Configuration Reference](/reference/configuration/) — all config options
0
72
▼docs_site/src/content/docs/tutorials/path-operation-config.md
+60-0
1
+---
2
+title: Path Operation Configuration
3
+description: Configure OpenAPI metadata for path operations — tags, summary, description, deprecated, and more.
4
+keywords: [JustAPI, path operation, OpenAPI, tags, summary, deprecated]
5
+---
6
+
7
+## Basic Configuration
8
+
9
+```python
10
+from justapi import JustAPIApp
11
+
12
+app = JustAPIApp()
13
+
14
+@app.get(
15
+ "/items/{item_id}",
16
+ summary="Get an item",
17
+ description="Retrieve a single item by its ID.",
18
+ tags=["items"],
19
+ response_description="The item",
20
+)
21
+def get_item(item_id: int):
22
+ return {"item_id": item_id}
23
+```
24
+
25
+## Tags
26
+
27
+Tags group endpoints in the docs:
28
+
29
+```python
30
+@app.get("/users/", tags=["users"])
31
+def list_users():
32
+ return []
33
+
34
+@app.post("/items/", tags=["items"])
35
+def create_item():
36
+ return {}
37
+```
38
+
39
+## Deprecating Endpoints
40
+
41
+```python
42
+@app.get("/old-endpoint", deprecated=True)
43
+def old_endpoint():
44
+ return {"message": "this endpoint is deprecated"}
45
+```
46
+
47
+## Controlling Doc Visibility
48
+
49
+```python
50
+@app.get("/internal", include_in_schema=False)
51
+def internal_only():
52
+ return {"secret": "data"}
53
+```
54
+
55
+This hides the endpoint from OpenAPI documentation entirely.
56
+
57
+## See Also
58
+
59
+- [Metadata & Docs URLs](/tutorials/metadata/) — app-level metadata
60
+- [OpenAPI Callbacks & Webhooks](/advanced/openapi-callbacks/) — advanced OpenAPI
0
61
▼docs_site/src/content/docs/tutorials/path-params.md
+123-0
1
+---
2
+title: Path Parameters
3
+description: Declare and validate path parameters in JustAPI using Python type hints.
4
+keywords: [JustAPI, path parameters, URL parameters, validation, FastAPI alternative]
5
+---
6
+
7
+Path parameters (also called URL variables) are values embedded in the URL path. JustAPI extracts them automatically based on your function signature.
8
+
9
+## Basic Path Parameters
10
+
11
+```python
12
+from justapi import JustAPIApp
13
+
14
+app = JustAPIApp()
15
+
16
+@app.get("/users/{user_id}")
17
+def get_user(user_id: int):
18
+ return {"user_id": user_id}
19
+
20
+@app.get("/items/{item_id}")
21
+def get_item(item_id: str):
22
+ return {"item_id": item_id}
23
+```
24
+
25
+The `{user_id}` and `{item_id}` in the path are matched to the function parameters. JustAPI validates the type automatically:
26
+
27
+- `/users/42` works — `user_id` is `42` (int)
28
+- `/users/foo` returns a validation error — `"foo"` is not a valid integer
29
+
30
+## Data Conversion
31
+
32
+JustAPI converts path parameters from strings to the declared Python type:
33
+
34
+```python
35
+@app.get("/items/{item_id}")
36
+def get_item(item_id: int):
37
+ # item_id is an int, not a string
38
+ return {"item_id": item_id, "double": item_id * 2}
39
+```
40
+
41
+- `/items/3` returns `{"item_id": 3, "double": 6}`
42
+- `/items/3.14` returns a validation error — `"3.14"` is not a valid `int`
43
+
44
+## Supported Types
45
+
46
+| Type | Example URL | Value |
47
+|------|-------------|-------|
48
+| `int` | `/items/42` | `42` |
49
+| `float` | `/items/3.14` | `3.14` |
50
+| `str` | `/items/foo` | `"foo"` |
51
+| `bool` | `/items/true` | `True` |
52
+| `UUID` | `/items/550e8400-...` | `UUID(...)` |
53
+| `Enum` | `/models/alexnet` | `ModelName.alexnet` |
54
+
55
+## Predefined Values with Enum
56
+
57
+Restrict a path parameter to a fixed set of values using Python `Enum`:
58
+
59
+```python
60
+from enum import Enum
61
+from justapi import JustAPIApp
62
+
63
+class ModelName(str, Enum):
64
+ alexnet = "alexnet"
65
+ resnet = "resnet"
66
+ lenet = "lenet"
67
+
68
+app = JustAPIApp()
69
+
70
+@app.get("/models/{model_name}")
71
+def get_model(model_name: ModelName):
72
+ if model_name is ModelName.alexnet:
73
+ return {"model_name": model_name, "message": "Deep Learning FTW!"}
74
+ return {"model_name": model_name, "message": "Have some residuals"}
75
+```
76
+
77
+The OpenAPI docs will show the available values in a dropdown.
78
+
79
+## Path Parameters Containing Paths
80
+
81
+For parameters that must match a full path (like file paths), use the `:path` converter:
82
+
83
+```python
84
+@app.get("/files/{file_path:path}")
85
+def read_file(file_path: str):
86
+ return {"file_path": file_path}
87
+```
88
+
89
+This matches URLs like `/files/home/user/file.txt`.
90
+
91
+:::note
92
+OpenAPI does not support the `:path` converter. The docs will not document that the parameter should contain a path, but the route will work correctly.
93
+:::
94
+
95
+## Order Matters
96
+
97
+When defining routes, put fixed paths before parameterized paths:
98
+
99
+```python
100
+@app.get("/users/me")
101
+def read_user_me():
102
+ return {"user_id": "the current user"}
103
+
104
+@app.get("/users/{user_id}")
105
+def read_user(user_id: str):
106
+ return {"user_id": user_id}
107
+```
108
+
109
+If `/users/{user_id}` were declared first, it would also match `/users/me`.
110
+
111
+## Recap
112
+
113
+- Use `{parameter_name}` in the path to declare a path parameter.
114
+- Declare the type in your function signature — JustAPI validates and converts it.
115
+- Use `Enum` for predefined values.
116
+- Use `:path` for parameters containing full paths.
117
+- Put fixed routes before parameterized routes.
118
+
119
+## See Also
120
+
121
+- [Path Parameters & Numeric Validations](/tutorials/path-params-numeric-validations/) — add `ge`, `le`, `gt`, `lt` constraints
122
+- [Query Parameters](/tutorials/query-params/) — parameters in the query string
123
+- [Request Body](/tutorials/request-body/) — JSON request bodies
0
124
▼docs_site/src/content/docs/tutorials/python-types.md
+152-0
1
+---
2
+title: Python Types Intro
3
+description: Introduction to Python type hints and how JustAPI uses them for automatic validation, serialization, and OpenAPI documentation.
4
+keywords: [JustAPI, Python types, type hints, Pydantic, validation, FastAPI alternative]
5
+---
6
+
7
+JustAPI uses standard Python type hints to power request validation, serialization, and auto-generated documentation. This page explains the types JustAPI supports and how they map to API contracts.
8
+
9
+## Basic Types
10
+
11
+JustAPI accepts all standard Python built-in types as function parameters and Pydantic model fields:
12
+
13
+```python
14
+from justapi import JustAPIApp
15
+
16
+app = JustAPIApp()
17
+
18
+@app.get("/users/{user_id}")
19
+def get_user(user_id: int):
20
+ return {"user_id": user_id}
21
+
22
+@app.post("/items")
23
+def create_item(name: str, price: float, in_stock: bool = True):
24
+ return {"name": name, "price": price, "in_stock": in_stock}
25
+```
26
+
27
+JustAPI automatically converts the path parameter `user_id` from a string to an `int`. If the value is not a valid integer, the API returns a clear validation error instead of crashing.
28
+
29
+| Type | Example | Validation |
30
+|------|---------|------------|
31
+| `int` | `42` | Rejects non-numeric strings |
32
+| `float` | `3.14` | Rejects non-numeric strings |
33
+| `str` | `"hello"` | Default string |
34
+| `bool` | `True` / `False` | Accepts `true` / `false` / `1` / `0` |
35
+| `bytes` | `b"data"` | Binary data |
36
+
37
+## Collections
38
+
39
+```python
40
+from justapi import JustAPIApp
41
+from pydantic import BaseModel
42
+
43
+app = JustAPIApp()
44
+
45
+@app.get("/filter")
46
+def filter_items(tags: list[str] = None, limit: int = 10):
47
+ return {"tags": tags, "limit": limit}
48
+```
49
+
50
+:::tip
51
+Query parameters like `tags=a&tags=b&tags=c` are automatically parsed into `["a", "b", "c"]`.
52
+:::
53
+
54
+| Type | Use Case |
55
+|------|----------|
56
+| `list[str]` | Multiple query values or JSON array |
57
+| `dict[str, str]` | JSON object with string keys and values |
58
+| `set[str]` | Unique values |
59
+| `tuple[int, str]` | Fixed-length JSON arrays |
60
+
61
+## Optional vs Required
62
+
63
+A parameter is **required** if it has no default value. It is **optional** if it has a default value or uses `Optional` / `None`:
64
+
65
+```python
66
+from typing import Optional
67
+from justapi import JustAPIApp
68
+
69
+app = JustAPIApp()
70
+
71
+@app.get("/search")
72
+def search(q: str, limit: int = 10, tag: Optional[str] = None):
73
+ return {"q": q, "limit": limit, "tag": tag}
74
+```
75
+
76
+- `q: str` — required, no default
77
+- `limit: int = 10` — optional, defaults to `10`
78
+- `tag: Optional[str] = None` — optional, defaults to `None`
79
+
80
+## Pydantic Models
81
+
82
+The most powerful way to define structured data is with Pydantic `BaseModel`:
83
+
84
+```python
85
+from justapi import JustAPIApp
86
+from pydantic import BaseModel
87
+from typing import Optional
88
+
89
+app = JustAPIApp()
90
+
91
+class Item(BaseModel):
92
+ name: str
93
+ price: float
94
+ description: Optional[str] = None
95
+ tags: list[str] = []
96
+
97
+@app.post("/items")
98
+def create_item(item: Item):
99
+ return item.model_dump()
100
+```
101
+
102
+JustAPI reads the request body as JSON, validates it against the model, and passes a typed `Item` object to your handler. Invalid data produces a clear error response with the exact field and issue.
103
+
104
+## Advanced Types
105
+
106
+JustAPI supports all Pydantic and Python standard library types:
107
+
108
+```python
109
+from datetime import datetime, date
110
+from uuid import UUID
111
+from decimal import Decimal
112
+from enum import Enum
113
+from typing import Literal
114
+
115
+class Color(str, Enum):
116
+ red = "red"
117
+ green = "green"
118
+ blue = "blue"
119
+
120
+@app.post("/orders")
121
+def create_order(
122
+ order_id: UUID,
123
+ created_at: datetime,
124
+ color: Color,
125
+ amount: Decimal,
126
+):
127
+ return {"order_id": str(order_id), "color": color.value}
128
+```
129
+
130
+| Type | Wire Format | Example |
131
+|------|-------------|---------|
132
+| `datetime` | ISO 8601 string | `"2026-07-25T10:30:00"` |
133
+| `date` | ISO 8601 date | `"2026-07-25"` |
134
+| `UUID` | String | `"550e8400-e29b-41d4-a716-446655440000"` |
135
+| `Decimal` | String or number | `"3.14"` |
136
+| `Enum` | String (value) | `"red"` |
137
+| `Literal["a", "b"]` | One of the literals | `"a"` |
138
+
139
+## Recap
140
+
141
+- JustAPI uses standard Python type hints for validation, serialization, and documentation.
142
+- Built-in types (`int`, `str`, `float`, `bool`) are auto-converted from strings in path/query params.
143
+- `Optional[T]` or `T = None` makes a parameter optional.
144
+- Pydantic `BaseModel` provides structured request body validation with nested models.
145
+- Advanced types (`datetime`, `UUID`, `Decimal`, `Enum`) are fully supported.
146
+
147
+## See Also
148
+
149
+- [First Steps](/tutorials/hello-world/) — your first JustAPI app
150
+- [Path Parameters](/tutorials/path-params/) — working with path parameters
151
+- [Query Parameters](/tutorials/query-params/) — working with query parameters
152
+- [Request Body](/tutorials/request-body/) — request body with Pydantic
0
153
▼docs_site/src/content/docs/tutorials/query-params.md
+106-0
1
+---
2
+title: Query Parameters
3
+description: Declare and validate query parameters in JustAPI.
4
+keywords: [JustAPI, query parameters, URL parameters, FastAPI alternative]
5
+---
6
+
7
+Query parameters are the key-value pairs after `?` in a URL. JustAPI extracts them automatically from function parameters.
8
+
9
+## Basic Query Parameters
10
+
11
+```python
12
+from justapi import JustAPIApp
13
+
14
+app = JustAPIApp()
15
+
16
+@app.get("/items/")
17
+def list_items(skip: int = 0, limit: int = 10):
18
+ return {"skip": skip, "limit": limit}
19
+```
20
+
21
+- `/items/` returns `{"skip": 0, "limit": 10}`
22
+- `/items/?skip=20&limit=50` returns `{"skip": 20, "limit": 50}`
23
+
24
+Any parameter that has a default value is treated as a query parameter.
25
+
26
+## Optional vs Required
27
+
28
+A query parameter is **required** if it has no default value:
29
+
30
+```python
31
+@app.get("/search")
32
+def search(q: str): # required — no default value
33
+ return {"query": q}
34
+```
35
+
36
+`/search` without `?q=...` returns a validation error.
37
+
38
+A query parameter is **optional** if it has a default value:
39
+
40
+```python
41
+@app.get("/search")
42
+def search(q: str = None): # optional — defaults to None
43
+ return {"query": q}
44
+```
45
+
46
+`/search` without `?q=...` works and returns `{"query": null}`.
47
+
48
+## Default Values
49
+
50
+```python
51
+@app.get("/products")
52
+def list_products(
53
+ category: str = "all",
54
+ min_price: float = 0.0,
55
+ max_price: float = 1000.0,
56
+ sort: str = "name",
57
+ order: str = "asc",
58
+):
59
+ return {
60
+ "category": category,
61
+ "price_range": [min_price, max_price],
62
+ "sort": sort,
63
+ "order": order,
64
+ }
65
+```
66
+
67
+All parameters are optional with their defaults. Clients only need to override the ones they care about.
68
+
69
+## Type Conversion
70
+
71
+JustAPI converts query parameters from strings to the declared Python type:
72
+
73
+```python
74
+@app.get("/items/{item_id}")
75
+def get_item(item_id: int, verbose: bool = False):
76
+ return {"item_id": item_id, "verbose": verbose}
77
+```
78
+
79
+- `/items/5?verbose=true` — `verbose` is `True`
80
+- `/items/5?verbose=1` — `verbose` is `True`
81
+- `/items/5?verbose=false` — `verbose` is `False`
82
+
83
+## Multiple Values for One Parameter
84
+
85
+Query parameters can repeat:
86
+
87
+```python
88
+@app.get("/filter")
89
+def filter_items(tags: list[str] = None):
90
+ return {"tags": tags}
91
+```
92
+
93
+`/filter?tags=a&tags=b&tags=c` returns `{"tags": ["a", "b", "c"]}`.
94
+
95
+## Recap
96
+
97
+- Parameters with default values are query parameters.
98
+- Parameters without default values are required.
99
+- JustAPI converts types automatically (`str` to `int`, `"true"` to `True`, etc.).
100
+- Use `list[T]` for repeating query parameters.
101
+
102
+## See Also
103
+
104
+- [Query Parameters & String Validations](/tutorials/query-params-str-validations/) — add `min_length`, `max_length`, `pattern` constraints
105
+- [Path Parameters](/tutorials/path-params/) — URL path parameters
106
+- [Request Body](/tutorials/request-body/) — JSON request bodies
0
107
▼docs_site/src/content/docs/tutorials/request-files.md
+67-0
1
+---
2
+title: Request Files
3
+description: Handle file uploads with UploadFile in JustAPI.
4
+keywords: [JustAPI, file upload, UploadFile, multipart, binary]
5
+---
6
+
7
+## Basic File Upload
8
+
9
+```python
10
+from justapi import JustAPIApp, UploadFile, File
11
+
12
+app = JustAPIApp()
13
+
14
+@app.post("/upload")
15
+async def upload_file(file: UploadFile = File(...)):
16
+ content = await file.read()
17
+ return {
18
+ "filename": file.filename,
19
+ "content_type": file.content_type,
20
+ "size": len(content),
21
+ }
22
+```
23
+
24
+## Multiple File Upload
25
+
26
+```python
27
+@app.post("/upload-multiple")
28
+async def upload_multiple(files: list[UploadFile] = File(...)):
29
+ results = []
30
+ for file in files:
31
+ content = await file.read()
32
+ results.append({"filename": file.filename, "size": len(content)})
33
+ return {"files": results}
34
+```
35
+
36
+## Save File to Disk
37
+
38
+```python
39
+import shutil
40
+from pathlib import Path
41
+
42
+UPLOAD_DIR = Path("uploads")
43
+UPLOAD_DIR.mkdir(exist_ok=True)
44
+
45
+@app.post("/upload-save")
46
+async def upload_and_save(file: UploadFile = File(...)):
47
+ file_path = UPLOAD_DIR / file.filename
48
+ with open(file_path, "wb") as buffer:
49
+ shutil.copyfileobj(file.file, buffer)
50
+ return {"saved_to": str(file_path)}
51
+```
52
+
53
+## UploadFile Properties
54
+
55
+| Property | Type | Description |
56
+|----------|------|-------------|
57
+| `filename` | `str` | Original filename |
58
+| `content_type` | `str` | MIME type |
59
+| `file` | `BinaryIO` | File-like object |
60
+| `read()` | `await bytes` | Read entire file |
61
+| `write()` | `await None` | Write data |
62
+
63
+## See Also
64
+
65
+- [Request Forms & Files](/tutorials/request-forms-files/) — forms with files
66
+- [Form Data](/tutorials/form-data/) — form-only submissions
67
+- [UploadFile Reference](/api-reference/uploadfile/) — full API reference
0
68
▼docs_site/src/content/docs/tutorials/request-forms-files.md
+48-0
1
+---
2
+title: Request Forms and Files
3
+description: Handle both form data and file uploads in a single request in JustAPI.
4
+keywords: [JustAPI, form data, file upload, combined, multipart]
5
+---
6
+
7
+## Combined Form + File
8
+
9
+```python
10
+from justapi import JustAPIApp, UploadFile, File, Form
11
+
12
+app = JustAPIApp()
13
+
14
+@app.post("/items/")
15
+async def create_item(
16
+ name: str = Form(...),
17
+ description: str = Form(""),
18
+ image: UploadFile = File(None),
19
+):
20
+ result = {"name": name, "description": description}
21
+ if image:
22
+ content = await image.read()
23
+ result["image_size"] = len(content)
24
+ return result
25
+```
26
+
27
+## Multiple Files + Form Data
28
+
29
+```python
30
+@app.post("/upload/")
31
+async def upload_with_metadata(
32
+ title: str = Form(...),
33
+ tags: list[str] = Form([]),
34
+ files: list[UploadFile] = File(...),
35
+):
36
+ return {
37
+ "title": title,
38
+ "tags": tags,
39
+ "file_count": len(files),
40
+ "filenames": [f.filename for f in files],
41
+ }
42
+```
43
+
44
+## See Also
45
+
46
+- [Form Data](/tutorials/form-data/) — forms without files
47
+- [Request Files](/tutorials/request-files/) — files without forms
48
+- [Form Models](/tutorials/form-models/) — Pydantic models for forms
0
49
▼docs_site/src/content/docs/tutorials/response-model.md
+76-0
1
+---
2
+title: Response Model — Return Type
3
+description: Control the response schema and filter output fields in JustAPI with response_model.
4
+keywords: [JustAPI, response model, return type, filtering, schema]
5
+---
6
+
7
+## Basic Response Model
8
+
9
+Declare the response type with `response_model`:
10
+
11
+```python
12
+from pydantic import BaseModel
13
+from justapi import JustAPIApp
14
+
15
+class User(BaseModel):
16
+ id: int
17
+ name: str
18
+ email: str
19
+ password: str
20
+
21
+app = JustAPIApp()
22
+
23
+@app.get("/users/{user_id}", response_model=User)
24
+def get_user(user_id: int):
25
+ return {"id": user_id, "name": "Alice", "email": "[email protected]", "password": "secret"}
26
+```
27
+
28
+The `password` field is automatically excluded from the response because it's not declared in the response model.
29
+
30
+## Filtering Response Fields
31
+
32
+```python
33
+class UserOut(BaseModel):
34
+ id: int
35
+ name: str
36
+
37
+@app.get("/users/{user_id}", response_model=UserOut)
38
+def get_user(user_id: int):
39
+ return {"id": user_id, "name": "Alice", "email": "[email protected]"}
40
+```
41
+
42
+Only `id` and `name` appear in the response.
43
+
44
+## Exclude Fields
45
+
46
+```python
47
+@app.get(
48
+ "/users/{user_id}",
49
+ response_model=User,
50
+ response_model_exclude={"password"},
51
+)
52
+def get_user(user_id: int):
53
+ return {"id": user_id, "name": "Alice", "email": "[email protected]", "password": "secret"}
54
+```
55
+
56
+## Multiple Response Models
57
+
58
+```python
59
+from typing import Union
60
+
61
+@app.get(
62
+ "/users/{user_id}",
63
+ response_model=Union[UserOut, dict],
64
+ responses={404: {"model": dict}},
65
+)
66
+def get_user(user_id: int):
67
+ if user_id == 0:
68
+ return {"error": "not found"}
69
+ return {"id": user_id, "name": "Alice"}
70
+```
71
+
72
+## See Also
73
+
74
+- [Extra Models](/tutorials/extra-models/) — multiple related models
75
+- [Response Status Code](/tutorials/response-status-code/) — custom status codes
76
+- [Custom Response Classes](/advanced/custom-response/) — HTML, stream, file responses
0
77
▼docs_site/src/content/docs/tutorials/response-status-code.md
+59-0
1
+---
2
+title: Response Status Code
3
+description: Set custom HTTP response status codes in JustAPI handlers.
4
+keywords: [JustAPI, status code, HTTP status, response, error handling]
5
+---
6
+
7
+## Default Status Code
8
+
9
+JustAPI returns `200 OK` by default for successful responses.
10
+
11
+## Custom Status Code
12
+
13
+Use `status_code` in the decorator:
14
+
15
+```python
16
+from justapi import JustAPIApp
17
+
18
+app = JustAPIApp()
19
+
20
+@app.post("/items", status_code=201)
21
+def create_item():
22
+ return {"message": "created"}
23
+```
24
+
25
+## Common Status Codes
26
+
27
+| Code | Meaning | Use When |
28
+|------|---------|----------|
29
+| 200 | OK | GET, successful PUT/PATCH |
30
+| 201 | Created | Successful POST |
31
+| 204 | No Content | Successful DELETE |
32
+| 301 | Moved Permanently | Redirect |
33
+| 400 | Bad Request | Client error |
34
+| 401 | Unauthorized | Authentication required |
35
+| 403 | Forbidden | Authenticated but not authorized |
36
+| 404 | Not Found | Resource doesn't exist |
37
+| 422 | Unprocessable Entity | Validation error |
38
+
39
+## Returning a Response Directly
40
+
41
+For full control, return a `Response` object:
42
+
43
+```python
44
+from starlette.responses import JSONResponse
45
+
46
+@app.get("/custom")
47
+def custom_response():
48
+ return JSONResponse(
49
+ content={"message": "custom"},
50
+ status_code=202,
51
+ headers={"X-Custom": "header"},
52
+ )
53
+```
54
+
55
+## See Also
56
+
57
+- [Handling Errors](/tutorials/error-handling/) — exception handlers
58
+- [Additional Status Codes](/advanced/response-directly/) — advanced status code patterns
59
+- [Status Codes Reference](/api-reference/routing/) — all available status codes
0
60
▼docs_site/src/content/docs/tutorials/security/first-steps/index.md
+36-0
1
+---
2
+title: Security — First Steps
3
+description: Add basic authentication to a JustAPI route.
4
+keywords: [JustAPI, security, authentication, OAuth2, bearer token]
5
+---
6
+
7
+## Adding Security to a Route
8
+
9
+Use `Security()` (like `Depends()` but for security) to protect routes:
10
+
11
+```python
12
+from justapi import JustAPIApp, Security, Depends
13
+from fastapi.security import OAuth2PasswordBearer
14
+
15
+app = JustAPIApp()
16
+
17
+oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
18
+
19
+def get_current_user(token: str = Security(oauth2_scheme)):
20
+ # In production, decode the token
21
+ return {"user_id": 1, "name": "Alice"}
22
+
23
+@app.get("/users/me")
24
+def read_users_me(current_user: dict = Security(get_current_user)):
25
+ return current_user
26
+```
27
+
28
+## OAuth2PasswordBearer
29
+
30
+The `tokenUrl` tells the docs where to send login requests. It appears as an "Authorize" button in Swagger UI.
31
+
32
+## See Also
33
+
34
+- [Get Current User](/tutorials/security/get-current-user/) — extracting user from token
35
+- [Simple OAuth2](/tutorials/security/simple-oauth2/) — password-based OAuth2
36
+- [OAuth2 + JWT](/tutorials/security/oauth2-jwt/) — full JWT implementation
0
37
▼docs_site/src/content/docs/tutorials/security/get-current-user/index.md
+32-0
1
+---
2
+title: Get Current User
3
+description: Extract and validate the current authenticated user from a JWT token in JustAPI.
4
+keywords: [JustAPI, security, current user, JWT, token]
5
+---
6
+
7
+## Extracting User from Token
8
+
9
+```python
10
+from justapi import JustAPIApp, Security
11
+from fastapi.security import OAuth2PasswordBearer
12
+
13
+app = JustAPIApp()
14
+oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
15
+
16
+async def get_current_user(token: str = Security(oauth2_scheme)):
17
+ # Decode JWT and return user
18
+ user = decode_token(token)
19
+ if user is None:
20
+ raise HTTPException(status_code=401, detail="Invalid token")
21
+ return user
22
+
23
+@app.get("/users/me")
24
+def read_me(current_user: dict = Security(get_current_user)):
25
+ return current_user
26
+```
27
+
28
+## See Also
29
+
30
+- [Security — First Steps](/tutorials/security/first-steps/) — basic security setup
31
+- [Simple OAuth2](/tutorials/security/simple-oauth2/) — password-based auth
32
+- [OAuth2 + JWT](/tutorials/security/oauth2-jwt/) — full JWT implementation
0
33
▼docs_site/src/content/docs/tutorials/security/oauth2-jwt/index.md
+61-0
1
+---
2
+title: OAuth2 with Password Hashing and JWT Tokens
3
+description: Implement production-ready OAuth2 with password hashing and JWT tokens in JustAPI.
4
+keywords: [JustAPI, OAuth2, JWT, password hashing, security, authentication]
5
+---
6
+
7
+## Production OAuth2 with JWT
8
+
9
+```python
10
+import jwt
11
+from datetime import datetime, timedelta
12
+from justapi import JustAPIApp, Depends, HTTPException
13
+from fastapi.security import OAuth2PasswordBearer
14
+from passlib.context import CryptContext
15
+
16
+SECRET_KEY = "your-secret-key"
17
+ALGORITHM = "HS256"
18
+ACCESS_TOKEN_EXPIRE_MINUTES = 30
19
+
20
+app = JustAPIApp()
21
+oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
22
+pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
23
+
24
+def verify_password(plain: str, hashed: str) -> bool:
25
+ return pwd_context.verify(plain, hashed)
26
+
27
+def hash_password(password: str) -> str:
28
+ return pwd_context.hash(password)
29
+
30
+def create_access_token(data: dict) -> str:
31
+ to_encode = data.copy()
32
+ expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
33
+ to_encode.update({"exp": expire})
34
+ return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
35
+
36
+def get_current_user(token: str = Security(oauth2_scheme)):
37
+ try:
38
+ payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
39
+ user_id = payload.get("user_id")
40
+ if user_id is None:
41
+ raise HTTPException(status_code=401)
42
+ return {"user_id": user_id}
43
+ except jwt.PyJWTError:
44
+ raise HTTPException(status_code=401, detail="Invalid token")
45
+
46
+@app.post("/token")
47
+def login(username: str, password: str):
48
+ # Verify credentials and return JWT
49
+ token = create_access_token({"user_id": 1, "sub": username})
50
+ return {"access_token": token, "token_type": "bearer"}
51
+
52
+@app.get("/users/me")
53
+def read_me(current_user: dict = Security(get_current_user)):
54
+ return current_user
55
+```
56
+
57
+## See Also
58
+
59
+- [Security — First Steps](/tutorials/security/first-steps/) — basic setup
60
+- [Get Current User](/tutorials/security/get-current-user/) — extracting user
61
+- [Simple OAuth2](/tutorials/security/simple-oauth2/) — simpler setup
0
62
▼docs_site/src/content/docs/tutorials/security/simple-oauth2/index.md
+40-0
1
+---
2
+title: Simple OAuth2 with Password and Bearer
3
+description: Implement password-based OAuth2 authentication in JustAPI.
4
+keywords: [JustAPI, OAuth2, password, bearer, authentication, security]
5
+---
6
+
7
+## Basic OAuth2 Setup
8
+
9
+```python
10
+from justapi import JustAPIApp, Depends, HTTPException
11
+from fastapi.security import OAuth2PasswordBearer
12
+
13
+app = JustAPIApp()
14
+
15
+oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
16
+
17
+# Fake user database
18
+fake_users_db = {
19
+ "alice": {"user_id": 1, "name": "Alice", "password": "secret"},
20
+}
21
+
22
+def authenticate_user(username: str, password: str):
23
+ user = fake_users_db.get(username)
24
+ if user and user["password"] == password:
25
+ return user
26
+ return None
27
+
28
+@app.post("/token")
29
+def login(username: str, password: str):
30
+ user = authenticate_user(username, password)
31
+ if not user:
32
+ raise HTTPException(status_code=401, detail="Invalid credentials")
33
+ return {"access_token": "fake-jwt-token", "token_type": "bearer"}
34
+```
35
+
36
+## See Also
37
+
38
+- [Security — First Steps](/tutorials/security/first-steps/) — basic setup
39
+- [Get Current User](/tutorials/security/get-current-user/) — extracting user
40
+- [OAuth2 + JWT](/tutorials/security/oauth2-jwt/) — production JWT
0
41
▼docs_site/src/content/docs/tutorials/static-files.md
+55-0
1
+---
2
+title: Static Files
3
+description: Serve static files (HTML, CSS, JS, images) from a JustAPI application.
4
+keywords: [JustAPI, static files, assets, mount, CSS, JavaScript]
5
+---
6
+
7
+## Basic Static Files
8
+
9
+Mount a directory to serve static files:
10
+
11
+```python
12
+from justapi import JustAPIApp
13
+
14
+app = JustAPIApp()
15
+
16
+app.mount("/static", directory="static", name="static")
17
+```
18
+
19
+This serves files from the `static/` directory at `/static/`. For example, `static/style.css` is accessible at `/static/style.css`.
20
+
21
+## Custom Mount Point
22
+
23
+```python
24
+# Serve at /assets/*
25
+app.mount("/assets", directory="public/assets", name="assets")
26
+```
27
+
28
+## Multiple Static Directories
29
+
30
+```python
31
+app.mount("/css", directory="static/css", name="css")
32
+app.mount("/js", directory="static/js", name="js")
33
+app.mount("/images", directory="static/images", name="images")
34
+```
35
+
36
+## Mounting a Sub-application
37
+
38
+JustAPI supports mounting sub-applications (like Starlette or ASGI apps) at a path:
39
+
40
+```python
41
+from justapi import JustAPIApp
42
+
43
+app = JustAPIApp()
44
+
45
+# Mount a sub-application
46
+from starlette.applications import Starlette
47
+sub_app = Starlette()
48
+app.mount("/api/v2", sub_app, name="v2")
49
+```
50
+
51
+## See Also
52
+
53
+- [Response Classes](/api-reference/responses/) — response types
54
+- [Custom Response Classes](/advanced/custom-response/) — HTML, stream, file responses
55
+- [Deployment — Docker](/deployment/docker/) — containerized static files
0
56
▼docs_site/src/content/docs/tutorials/testing.md
+94-0
1
+---
2
+title: Testing
3
+description: Test JustAPI applications using the built-in JustAPITestClient for unit and integration tests.
4
+keywords: [JustAPI, testing, test client, pytest, assertions]
5
+---
6
+
7
+## JustAPITestClient
8
+
9
+JustAPI includes `JustAPITestClient` for testing without running a server. It processes requests in-process using the Rust runtime.
10
+
11
+```python
12
+from justapi import JustAPIApp, JustAPITestClient
13
+
14
+app = JustAPIApp()
15
+
16
+@app.get("/hello")
17
+def hello():
18
+ return {"message": "Hello!"}
19
+
20
+client = JustAPITestClient(app)
21
+
22
+def test_hello():
23
+ response = client.get("/hello")
24
+ assert response.status == 200
25
+ assert response.json() == {"message": "Hello!"}
26
+```
27
+
28
+## Testing POST Endpoints
29
+
30
+```python
31
+def test_create_item():
32
+ response = client.post("/items", body={"name": "Widget", "price": 9.99})
33
+ assert response.status == 200
34
+ assert response.json()["name"] == "Widget"
35
+```
36
+
37
+## Testing Error Responses
38
+
39
+```python
40
+def test_not_found():
41
+ response = client.get("/nonexistent")
42
+ assert response.status == 404
43
+
44
+def test_validation_error():
45
+ response = client.post("/items", body={"name": "x"})
46
+ assert response.status == 422 # validation error
47
+```
48
+
49
+## Testing with Path Parameters
50
+
51
+```python
52
+@app.get("/users/{user_id}")
53
+def get_user(user_id: int):
54
+ return {"user_id": user_id}
55
+
56
+def test_get_user():
57
+ response = client.get("/users/42")
58
+ assert response.json() == {"user_id": 42}
59
+```
60
+
61
+## Testing with Query Parameters
62
+
63
+```python
64
+@app.get("/search")
65
+def search(q: str, limit: int = 10):
66
+ return {"q": q, "limit": limit}
67
+
68
+def test_search_with_defaults():
69
+ response = client.get("/search?q=foo")
70
+ assert response.json() == {"q": "foo", "limit": 10}
71
+
72
+def test_search_with_custom_limit():
73
+ response = client.get("/search?q=foo&limit=5")
74
+ assert response.json() == {"q": "foo", "limit": 5}
75
+```
76
+
77
+## Running Tests
78
+
79
+```bash
80
+# Run with pytest
81
+pytest tests/ -v
82
+
83
+# Run with uv
84
+uv run pytest tests/ -v
85
+
86
+# Single test file
87
+pytest tests/test_app.py -v
88
+```
89
+
90
+## See Also
91
+
92
+- [Testing Client API](/api-reference/testing-client/) — full client reference
93
+- [Debugging](/tutorials/debugging/) — debugging techniques
94
+- [Testing Guide](/contributing/testing-guide/) — writing and running tests
0
95