fastapi_rust/lib.rs
1//! Ultra-optimized Rust web framework inspired by FastAPI.
2//!
3//! fastapi_rust provides a type-safe, high-performance web framework with:
4//!
5//! - **Type-driven API design** — Route handlers declare types, framework extracts/validates automatically
6//! - **Dependency injection** — Composable, testable request handling
7//! - **Automatic OpenAPI** — Schema generation from type definitions
8//! - **First-class async** — Built on asupersync for structured concurrency
9//! - **Dependency discipline** — No Tokio/Hyper/Tower/Axum; direct deps kept small
10//!
11//! # Role In The System
12//!
13//! `fastapi_rust` is the user-facing facade crate. It re-exports the framework's
14//! core types, macros, and utilities from the sub-crates so applications only
15//! need a single dependency. All real behavior lives in the sub-crates listed
16//! below; this crate exists to provide a cohesive, ergonomic API surface.
17//!
18//! # Quick Start
19//!
20//! ```ignore
21//! use fastapi_rust::prelude::*;
22//!
23//! #[derive(Serialize, Deserialize, JsonSchema)]
24//! struct Item {
25//! id: i64,
26//! name: String,
27//! }
28//!
29//! #[get("/items/{id}")]
30//! async fn get_item(cx: &Cx, id: Path<i64>) -> Json<Item> {
31//! Json(Item { id: id.0, name: "Example".into() })
32//! }
33//!
34//! fn main() {
35//! let app = App::builder()
36//! .title("My API")
37//! .route_entry(get_item_route())
38//! .build();
39//!
40//! let rt = asupersync::runtime::RuntimeBuilder::current_thread()
41//! .build()
42//! .expect("runtime must build");
43//! rt.block_on(async move {
44//! serve(app, "0.0.0.0:8000").await.expect("server must start");
45//! });
46//! }
47//! ```
48//!
49//! # Design Philosophy
50//!
51//! This framework is built with the following principles:
52//!
53//! 1. **Zero-cost abstractions** — No runtime reflection, everything at compile time
54//! 2. **Cancel-correct** — Leverages asupersync's structured concurrency
55//! 3. **Minimal allocations** — Zero-copy parsing where possible
56//! 4. **Familiar API** — FastAPI users will recognize the patterns
57//!
58//! # Crate Structure
59//!
60//! | Crate | Purpose |
61//! |-------|---------|
62//! | `fastapi_core` | Core types (Request, Response, Error), extractors, middleware, DI |
63//! | `fastapi_http` | Zero-copy HTTP/1.1 parser, TCP server, chunked encoding |
64//! | `fastapi_router` | Trie-based router with O(log n) lookups |
65//! | `fastapi_macros` | Procedural macros (`#[get]`, `#[derive(Validate)]`, `#[derive(JsonSchema)]`) |
66//! | `fastapi_openapi` | OpenAPI 3.1 schema types and generation |
67//! | `fastapi_output` | Agent-aware rich console output (optional) |
68//!
69//! # Feature Flags
70//!
71//! | Feature | Default | Description |
72//! |---------|---------|-------------|
73//! | `output` | **yes** | Rich console output with agent detection (includes `fastapi-output/rich`) |
74//! | `output-plain` | no | Plain-text-only output (smaller binary, no ANSI codes) |
75//! | `full` | no | All output features including every theme and component |
76//!
77//! ## Sub-crate Feature Flags
78//!
79//! **`fastapi-core`:**
80//!
81//! | Feature | Description |
82//! |---------|-------------|
83//! | `regex` | Regex support in testing assertions |
84//! | `compression` | Response compression middleware (gzip via flate2) |
85//! | `proptest` | Property-based testing support |
86
87//!
88//! # Cookbook
89//!
90//! Common patterns for building APIs with fastapi_rust.
91//!
92//! ## JSON CRUD Handler
93//!
94//! ```ignore
95//! use fastapi_rust::prelude::*;
96//!
97//! #[get("/items/{id}")]
98//! async fn get_item(cx: &Cx, id: Path<i64>, state: State<AppState>) -> Result<Json<Item>, HttpError> {
99//! let item = state.db.find(id.0).await?;
100//! Ok(Json(item))
101//! }
102//! ```
103//!
104//! ## Pagination
105//!
106//! ```ignore
107//! use fastapi_rust::prelude::*;
108//!
109//! #[get("/items")]
110//! async fn list_items(cx: &Cx, page: Pagination) -> Json<Page<Item>> {
111//! // page.page() returns current page (default: 1)
112//! // page.per_page() returns items per page (default: 20, max: 100)
113//! let items = db.list(page.offset(), page.limit()).await;
114//! Json(Page::new(items, total_count, page.page(), page.per_page()))
115//! }
116//! ```
117//!
118//! ## Bearer Token Authentication
119//!
120//! ```ignore
121//! use fastapi_rust::prelude::*;
122//!
123//! #[get("/protected")]
124//! async fn protected(cx: &Cx, token: BearerToken) -> Json<UserInfo> {
125//! let user = verify_jwt(token.token()).await?;
126//! Json(user)
127//! }
128//! ```
129//!
130//! ## Background Tasks
131//!
132//! ```ignore
133//! use fastapi_rust::prelude::*;
134//!
135//! #[post("/send-email")]
136//! async fn send_email(cx: &Cx, body: Json<EmailRequest>, tasks: BackgroundTasks) -> StatusCode {
137//! tasks.add(move || {
138//! // Runs after response is sent
139//! email_service::send(&body.to, &body.subject, &body.body);
140//! });
141//! StatusCode::ACCEPTED
142//! }
143//! ```
144//!
145//! ## CORS + Rate Limiting Middleware
146//!
147//! ```ignore
148//! use fastapi_rust::prelude::*;
149//!
150//! let app = App::new()
151//! .middleware(Cors::new().allow_any_origin(true).allow_credentials(true))
152//! .middleware(RateLimitBuilder::new().max_requests(100).window_secs(60).build());
153//! ```
154//!
155//! ## Error Handling
156//!
157//! ```ignore
158//! use fastapi_rust::prelude::*;
159//!
160//! // Custom errors implement IntoResponse automatically via HttpError
161//! fn not_found(resource: &str, id: u64) -> HttpError {
162//! HttpError::not_found(format!("{} {} not found", resource, id))
163//! }
164//! ```
165//!
166//! # Migrating from Python FastAPI
167//!
168//! ## Key Differences
169//!
170//! | Python FastAPI | fastapi_rust | Notes |
171//! |----------------|--------------|-------|
172//! | `@app.get("/")` | `#[get("/")]` | Proc macro instead of decorator |
173//! | `async def handler(item: Item)` | `async fn handler(cx: &Cx, item: Json<Item>)` | Explicit `Cx` context + typed extractors |
174//! | `Depends(get_db)` | `Depends<DbPool>` | Type-based DI, not function-based |
175//! | `HTTPException(404)` | `HttpError::not_found(msg)` | Typed error constructors |
176//! | `BackgroundTasks` | `BackgroundTasks` | Same concept, different API |
177//! | `Query(q: str)` | `Query<SearchParams>` | Struct-based query extraction |
178//! | `Path(item_id: int)` | `Path<i64>` | Type-safe path parameters |
179//! | `Body(...)` | `Json<T>` | Explicit JSON extraction |
180//! | `Response(status_code=201)` | `StatusCode::CREATED` | Type-safe status codes |
181//!
182//! ## Async Runtime
183//!
184//! Python FastAPI uses `asyncio`. fastapi_rust uses `asupersync`, which provides:
185//! - **Structured concurrency**: Request handlers run in regions
186//! - **Cancel-correctness**: Graceful cancellation via checkpoints
187//! - **Budgeted timeouts**: Request timeouts via budget exhaustion
188//!
189//! Every handler receives `&Cx` as its first parameter for async context.
190//!
191//! ## Dependency Injection
192//!
193//! Python uses function-based DI with `Depends(func)`. Rust uses trait-based DI:
194//!
195//! ```ignore
196//! // Python:
197//! // async def get_db():
198//! // yield db_session
199//! //
200//! // @app.get("/")
201//! // async def handler(db: Session = Depends(get_db)):
202//!
203//! // Rust:
204//! impl FromDependency for DbPool {
205//! async fn from_dependency(cx: &Cx, cache: &DependencyCache) -> Result<Self, HttpError> {
206//! Ok(DbPool::acquire(cx).await?)
207//! }
208//! }
209//!
210//! #[get("/")]
211//! async fn handler(cx: &Cx, db: Depends<DbPool>) -> Json<Data> { ... }
212//! ```
213//!
214//! ## Validation
215//!
216//! Python uses Pydantic models. Rust uses `#[derive(Validate)]`:
217//!
218//! ```ignore
219//! // Python:
220//! // class Item(BaseModel):
221//! // name: str = Field(..., min_length=1, max_length=100)
222//! // price: float = Field(..., gt=0)
223//!
224//! // Rust:
225//! #[derive(Validate)]
226//! struct Item {
227//! #[validate(min_length = 1, max_length = 100)]
228//! name: String,
229//! #[validate(range(min = 0.01))]
230//! price: f64,
231//! }
232//! ```
233
234#![forbid(unsafe_code)]
235// Design doc at PROPOSED_RUST_ARCHITECTURE.md (not embedded - too many conceptual code examples)
236
237// Re-export crates
238pub use fastapi_core as core;
239pub use fastapi_http as http;
240pub use fastapi_macros as macros;
241pub use fastapi_openapi as openapi;
242pub use fastapi_router as router;
243
244// Re-export commonly used types
245pub use fastapi_core::{
246 App, AppBuilder, AppConfig, Cors, CorsConfig, Cx, DefaultConfig, DefaultDependencyConfig,
247 DependencyOverrides, DependencyScope, Depends, DependsConfig, FromDependency, FromRequest,
248 HttpError, IntoResponse, Method, NoCache, Request, RequestId, RequestIdConfig,
249 RequestIdMiddleware, Response, ResponseBody, StateContainer, StatusCode, ValidationError,
250 ValidationErrors,
251};
252
253// Re-export extractors
254pub use fastapi_core::{
255 // Common header types
256 Accept,
257 AddResponseHeader,
258 AppState,
259 Authorization,
260 // Background tasks
261 BackgroundTasks,
262 // Auth extractors
263 BasicAuth,
264 BasicAuthError,
265 BearerToken,
266 BearerTokenError,
267 ContentType,
268 // Cookies
269 Cookie,
270 DEFAULT_PAGE,
271 DEFAULT_PER_PAGE,
272 // Headers
273 Header,
274 HeaderExtractError,
275 HeaderValues,
276 Host,
277 // Body extractors
278 Json,
279 JsonConfig,
280 JsonExtractError,
281 MAX_PER_PAGE,
282 NamedHeader,
283 OAuth2BearerError,
284 OAuth2PasswordBearer,
285 OAuth2PasswordBearerConfig,
286 Page,
287 // Pagination
288 Pagination,
289 PaginationConfig,
290 // Path parameters
291 Path,
292 PathExtractError,
293 PathParams,
294 // Query string
295 Query,
296 QueryExtractError,
297 QueryParams,
298 RequestContext,
299 SameSite,
300 // State
301 State,
302 UserAgent,
303 XRequestId,
304};
305
306// Re-export testing utilities
307pub use fastapi_core::{CookieJar, RequestBuilder, TestClient, TestResponse};
308pub use fastapi_macros::{JsonSchema, Validate, delete, get, head, options, patch, post, put};
309pub use fastapi_openapi::{OpenApi, OpenApiBuilder, SchemaRegistry};
310pub use fastapi_router::{
311 // Route matching
312 AllowedMethods,
313 ConversionError,
314 // Path parameter types
315 Converter,
316 // Error types
317 InvalidRouteError,
318 ParamInfo,
319 ParamValue,
320 // Core router types
321 Route,
322 RouteAddError,
323 RouteConflictError,
324 RouteLookup,
325 RouteMatch,
326 Router,
327};
328
329// Re-export HTTP server types
330pub use fastapi_http::{
331 GracefulOutcome, ServeError, Server, ServerConfig, ServerError, ShutdownController,
332 ShutdownReceiver, TcpServer, serve, serve_with_config,
333};
334
335/// Prelude module for convenient imports.
336pub mod prelude {
337 pub use crate::{
338 // Core types
339 App,
340 AppBuilder,
341 AppConfig,
342 // Auth
343 BasicAuth,
344 BearerToken,
345 Cookie,
346 Cors,
347 CorsConfig,
348 // asupersync context
349 Cx,
350 DefaultConfig,
351 DefaultDependencyConfig,
352 DependencyOverrides,
353 DependencyScope,
354 Depends,
355 DependsConfig,
356 FromDependency,
357 FromRequest,
358 Header,
359 HttpError,
360 IntoResponse,
361 // Extractors
362 Json,
363 // Macros
364 JsonSchema,
365 Method,
366 NoCache,
367 OAuth2PasswordBearer,
368 // OpenAPI
369 OpenApi,
370 OpenApiBuilder,
371 Page,
372 // Pagination
373 Pagination,
374 Path,
375 Query,
376 Request,
377 RequestContext,
378 RequestId,
379 RequestIdMiddleware,
380 Response,
381 Route,
382 Router,
383 // Server
384 Server,
385 ServerConfig,
386 State,
387 StatusCode,
388 Validate,
389 ValidationError,
390 ValidationErrors,
391 delete,
392 get,
393 head,
394 options,
395 patch,
396 post,
397 put,
398 serve,
399 };
400 pub use serde::{Deserialize, Serialize};
401}
402
403/// Testing utilities module.
404pub mod testing {
405 pub use fastapi_core::testing::{CookieJar, RequestBuilder, TestClient, TestResponse};
406}
407
408/// Extractors module for type-safe request data extraction.
409pub mod extractors {
410 pub use fastapi_core::{
411 Accept, AppState, Authorization, BackgroundTasks, BasicAuth, BearerToken, ContentType,
412 Cookie, Header, HeaderValues, Host, Json, JsonConfig, NamedHeader, OAuth2PasswordBearer,
413 Page, Pagination, PaginationConfig, Path, PathParams, Query, QueryParams, State, UserAgent,
414 XRequestId,
415 };
416}
417
418/// Extractors module for request data extraction (extended).
419pub mod extract {
420 pub use fastapi_core::{
421 Accept, AppState, Authorization, ContentType, FromHeaderValue, Header, HeaderExtractError,
422 HeaderName, HeaderValues, Host, Json, JsonConfig, JsonExtractError, NamedHeader,
423 OAuth2BearerError, OAuth2BearerErrorKind, OAuth2PasswordBearer, OAuth2PasswordBearerConfig,
424 Path, PathExtractError, PathParams, Query, QueryExtractError, QueryParams, State,
425 StateExtractError, UserAgent, XRequestId,
426 };
427}
428
429/// HTTP server module with server types and configuration.
430pub mod server {
431 pub use fastapi_http::{
432 // Configuration constants
433 DEFAULT_DRAIN_TIMEOUT_SECS,
434 DEFAULT_KEEP_ALIVE_TIMEOUT_SECS,
435 DEFAULT_MAX_CONNECTIONS,
436 DEFAULT_MAX_REQUESTS_PER_CONNECTION,
437 DEFAULT_READ_BUFFER_SIZE,
438 DEFAULT_REQUEST_TIMEOUT_SECS,
439 // Shutdown coordination
440 GracefulOutcome,
441 // Error types
442 ServeError,
443 // Server types
444 Server,
445 ServerConfig,
446 ServerError,
447 ShutdownController,
448 ShutdownReceiver,
449 TcpServer,
450 // Server functions
451 serve,
452 serve_with_config,
453 };
454}
455
456/// Extension trait for generating OpenAPI specifications from applications.
457pub trait OpenApiExt {
458 /// Generate an OpenAPI specification from the application.
459 ///
460 /// This creates an OpenAPI 3.1 document based on the application's
461 /// configuration and registered routes.
462 ///
463 /// # Example
464 ///
465 /// ```ignore
466 /// use fastapi::prelude::*;
467 /// use fastapi::OpenApiExt;
468 ///
469 /// let app = App::builder()
470 /// .config(AppConfig::new().name("My API").version("1.0.0"))
471 /// .build();
472 ///
473 /// let spec = app.openapi();
474 /// println!("{}", serde_json::to_string_pretty(&spec).unwrap());
475 /// ```
476 fn openapi(&self) -> OpenApi;
477
478 /// Generate an OpenAPI specification with custom configuration.
479 fn openapi_with<F>(&self, configure: F) -> OpenApi
480 where
481 F: FnOnce(OpenApiBuilder) -> OpenApiBuilder;
482}
483
484impl OpenApiExt for App {
485 fn openapi(&self) -> OpenApi {
486 self.openapi_with(|b| b)
487 }
488
489 fn openapi_with<F>(&self, configure: F) -> OpenApi
490 where
491 F: FnOnce(OpenApiBuilder) -> OpenApiBuilder,
492 {
493 let mut builder = OpenApiBuilder::new(&self.config().name, &self.config().version);
494 builder = configure(builder);
495 builder.build()
496 }
497}
498
499// Note: operation_id generation lives in `fastapi-router::Route` and in the
500// OpenAPI builder layer; keep the facade crate lean.