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//! | `testing` | **yes** | TestClient, assertion macros, and deterministic in-process testing helpers |
75//! | `output-plain` | no | Plain-text-only output (smaller binary, no ANSI codes) |
76//! | `full` | no | All output features including every theme and component |
77//!
78//! ## Sub-crate Feature Flags
79//!
80//! **`fastapi-core`:**
81//!
82//! | Feature | Description |
83//! |---------|-------------|
84//! | `regex` | Regex support in testing assertions |
85//! | `testing` | TestClient and assertion helpers backed by asupersync test internals |
86//! | `compression` | Response compression middleware (gzip via flate2) |
87//! | `proptest` | Property-based testing support |
88
89//!
90//! # Cookbook
91//!
92//! Common patterns for building APIs with fastapi_rust.
93//!
94//! ## JSON CRUD Handler
95//!
96//! ```ignore
97//! use fastapi_rust::prelude::*;
98//!
99//! #[get("/items/{id}")]
100//! async fn get_item(cx: &Cx, id: Path<i64>, state: State<AppState>) -> Result<Json<Item>, HttpError> {
101//! let item = state.db.find(id.0).await?;
102//! Ok(Json(item))
103//! }
104//! ```
105//!
106//! ## Pagination
107//!
108//! ```ignore
109//! use fastapi_rust::prelude::*;
110//!
111//! #[get("/items")]
112//! async fn list_items(cx: &Cx, page: Pagination) -> Json<Page<Item>> {
113//! // page.page() returns current page (default: 1)
114//! // page.per_page() returns items per page (default: 20, max: 100)
115//! let items = db.list(page.offset(), page.limit()).await;
116//! Json(Page::new(items, total_count, page.page(), page.per_page()))
117//! }
118//! ```
119//!
120//! ## Bearer Token Authentication
121//!
122//! ```ignore
123//! use fastapi_rust::prelude::*;
124//!
125//! #[get("/protected")]
126//! async fn protected(cx: &Cx, token: BearerToken) -> Json<UserInfo> {
127//! let user = verify_jwt(token.token()).await?;
128//! Json(user)
129//! }
130//! ```
131//!
132//! ## Background Tasks
133//!
134//! ```ignore
135//! use fastapi_rust::prelude::*;
136//!
137//! #[post("/send-email")]
138//! async fn send_email(cx: &Cx, body: Json<EmailRequest>, tasks: BackgroundTasks) -> StatusCode {
139//! tasks.add(move || {
140//! // Runs after response is sent
141//! email_service::send(&body.to, &body.subject, &body.body);
142//! });
143//! StatusCode::ACCEPTED
144//! }
145//! ```
146//!
147//! ## CORS + Rate Limiting Middleware
148//!
149//! ```ignore
150//! use fastapi_rust::prelude::*;
151//!
152//! let app = App::new()
153//! .middleware(Cors::new().allow_any_origin(true).allow_credentials(true))
154//! .middleware(RateLimitBuilder::new().max_requests(100).window_secs(60).build());
155//! ```
156//!
157//! ## Error Handling
158//!
159//! ```ignore
160//! use fastapi_rust::prelude::*;
161//!
162//! // Custom errors implement IntoResponse automatically via HttpError
163//! fn not_found(resource: &str, id: u64) -> HttpError {
164//! HttpError::not_found(format!("{} {} not found", resource, id))
165//! }
166//! ```
167//!
168//! # Migrating from Python FastAPI
169//!
170//! ## Key Differences
171//!
172//! | Python FastAPI | fastapi_rust | Notes |
173//! |----------------|--------------|-------|
174//! | `@app.get("/")` | `#[get("/")]` | Proc macro instead of decorator |
175//! | `async def handler(item: Item)` | `async fn handler(cx: &Cx, item: Json<Item>)` | Explicit `Cx` context + typed extractors |
176//! | `Depends(get_db)` | `Depends<DbPool>` | Type-based DI, not function-based |
177//! | `HTTPException(404)` | `HttpError::not_found(msg)` | Typed error constructors |
178//! | `BackgroundTasks` | `BackgroundTasks` | Same concept, different API |
179//! | `Query(q: str)` | `Query<SearchParams>` | Struct-based query extraction |
180//! | `Path(item_id: int)` | `Path<i64>` | Type-safe path parameters |
181//! | `Body(...)` | `Json<T>` | Explicit JSON extraction |
182//! | `Response(status_code=201)` | `StatusCode::CREATED` | Type-safe status codes |
183//!
184//! ## Async Runtime
185//!
186//! Python FastAPI uses `asyncio`. fastapi_rust uses `asupersync`, which provides:
187//! - **Structured concurrency**: Request handlers run in regions
188//! - **Cancel-correctness**: Graceful cancellation via checkpoints
189//! - **Budgeted timeouts**: Request timeouts via budget exhaustion
190//!
191//! Every handler receives `&Cx` as its first parameter for async context.
192//!
193//! ## Dependency Injection
194//!
195//! Python uses function-based DI with `Depends(func)`. Rust uses trait-based DI:
196//!
197//! ```ignore
198//! // Python:
199//! // async def get_db():
200//! // yield db_session
201//! //
202//! // @app.get("/")
203//! // async def handler(db: Session = Depends(get_db)):
204//!
205//! // Rust:
206//! impl FromDependency for DbPool {
207//! async fn from_dependency(cx: &Cx, cache: &DependencyCache) -> Result<Self, HttpError> {
208//! Ok(DbPool::acquire(cx).await?)
209//! }
210//! }
211//!
212//! #[get("/")]
213//! async fn handler(cx: &Cx, db: Depends<DbPool>) -> Json<Data> { ... }
214//! ```
215//!
216//! ## Validation
217//!
218//! Python uses Pydantic models. Rust uses `#[derive(Validate)]`:
219//!
220//! ```ignore
221//! // Python:
222//! // class Item(BaseModel):
223//! // name: str = Field(..., min_length=1, max_length=100)
224//! // price: float = Field(..., gt=0)
225//!
226//! // Rust:
227//! #[derive(Validate)]
228//! struct Item {
229//! #[validate(min_length = 1, max_length = 100)]
230//! name: String,
231//! #[validate(range(min = 0.01))]
232//! price: f64,
233//! }
234//! ```
235
236#![forbid(unsafe_code)]
237// Design doc at PROPOSED_RUST_ARCHITECTURE.md (not embedded - too many conceptual code examples)
238
239// Re-export crates
240pub use fastapi_core as core;
241pub use fastapi_http as http;
242pub use fastapi_macros as macros;
243pub use fastapi_openapi as openapi;
244pub use fastapi_router as router;
245
246// The attribute/derive macros (`#[get]`, `JsonSchema`, `Validate`, ...) expand to
247// unanchored `fastapi_core::` / `fastapi_router::` / `fastapi_openapi::` paths, so
248// those crate names must be resolvable at the call site. Consumers that depend only
249// on `fastapi-rust` get them from here (the `prelude` glob-imports them); consumers
250// that depend on the component crates directly already have them in scope.
251#[doc(hidden)]
252pub use fastapi_core;
253#[doc(hidden)]
254pub use fastapi_openapi;
255#[doc(hidden)]
256pub use fastapi_router;
257
258// Re-export commonly used types
259pub use fastapi_core::{
260 App, AppBuilder, AppConfig, Cors, CorsConfig, Cx, DefaultConfig, DefaultDependencyConfig,
261 DependencyOverrides, DependencyScope, Depends, DependsConfig, FromDependency, FromRequest,
262 HttpError, IntoResponse, Method, NoCache, Request, RequestId, RequestIdConfig,
263 RequestIdMiddleware, Response, ResponseBody, StateContainer, StatusCode, ValidationError,
264 ValidationErrors,
265};
266
267// Re-export extractors
268pub use fastapi_core::{
269 // Common header types
270 Accept,
271 AddResponseHeader,
272 AppState,
273 Authorization,
274 // Background tasks
275 BackgroundTasks,
276 // Auth extractors
277 BasicAuth,
278 BasicAuthError,
279 BearerToken,
280 BearerTokenError,
281 ContentType,
282 // Cookies
283 Cookie,
284 DEFAULT_PAGE,
285 DEFAULT_PER_PAGE,
286 // Headers
287 Header,
288 HeaderExtractError,
289 HeaderValues,
290 Host,
291 // Body extractors
292 Json,
293 JsonConfig,
294 JsonExtractError,
295 MAX_PER_PAGE,
296 NamedHeader,
297 OAuth2BearerError,
298 OAuth2PasswordBearer,
299 OAuth2PasswordBearerConfig,
300 Page,
301 // Pagination
302 Pagination,
303 PaginationConfig,
304 // Path parameters
305 Path,
306 PathExtractError,
307 PathParams,
308 // Query string
309 Query,
310 QueryExtractError,
311 QueryParams,
312 RequestContext,
313 SameSite,
314 // State
315 State,
316 UserAgent,
317 XRequestId,
318};
319
320// Re-export testing utilities
321#[cfg(feature = "testing")]
322pub use fastapi_core::{CookieJar, RequestBuilder, TestClient, TestResponse};
323pub use fastapi_macros::{JsonSchema, Validate, delete, get, head, options, patch, post, put};
324pub use fastapi_openapi::{OpenApi, OpenApiBuilder, SchemaRegistry};
325pub use fastapi_router::{
326 // Route matching
327 AllowedMethods,
328 ConversionError,
329 // Path parameter types
330 Converter,
331 // Error types
332 InvalidRouteError,
333 ParamInfo,
334 ParamValue,
335 // Core router types
336 Route,
337 RouteAddError,
338 RouteConflictError,
339 RouteLookup,
340 RouteMatch,
341 Router,
342};
343
344// Re-export HTTP server types
345pub use fastapi_http::{
346 GracefulOutcome, ServeError, Server, ServerConfig, ServerError, ShutdownController,
347 ShutdownReceiver, TcpServer, serve, serve_with_config,
348};
349
350/// Prelude module for convenient imports.
351pub mod prelude {
352 pub use crate::{
353 // Core types
354 App,
355 AppBuilder,
356 AppConfig,
357 // Auth
358 BasicAuth,
359 BearerToken,
360 Cookie,
361 Cors,
362 CorsConfig,
363 // asupersync context
364 Cx,
365 DefaultConfig,
366 DefaultDependencyConfig,
367 DependencyOverrides,
368 DependencyScope,
369 Depends,
370 DependsConfig,
371 FromDependency,
372 FromRequest,
373 Header,
374 HttpError,
375 IntoResponse,
376 // Extractors
377 Json,
378 // Macros
379 JsonSchema,
380 Method,
381 NoCache,
382 OAuth2PasswordBearer,
383 // OpenAPI
384 OpenApi,
385 OpenApiBuilder,
386 Page,
387 // Pagination
388 Pagination,
389 Path,
390 Query,
391 Request,
392 RequestContext,
393 RequestId,
394 RequestIdMiddleware,
395 Response,
396 Route,
397 Router,
398 // Server
399 Server,
400 ServerConfig,
401 State,
402 StatusCode,
403 Validate,
404 ValidationError,
405 ValidationErrors,
406 delete,
407 get,
408 head,
409 options,
410 patch,
411 post,
412 put,
413 serve,
414 };
415 // Crate names the proc-macro expansions refer to; see the note at the crate root.
416 #[doc(hidden)]
417 pub use crate::{fastapi_core, fastapi_openapi, fastapi_router};
418 pub use serde::{Deserialize, Serialize};
419}
420
421/// Testing utilities module.
422#[cfg(feature = "testing")]
423pub mod testing {
424 pub use fastapi_core::testing::{CookieJar, RequestBuilder, TestClient, TestResponse};
425}
426
427/// Extractors module for type-safe request data extraction.
428pub mod extractors {
429 pub use fastapi_core::{
430 Accept, AppState, Authorization, BackgroundTasks, BasicAuth, BearerToken, ContentType,
431 Cookie, Header, HeaderValues, Host, Json, JsonConfig, NamedHeader, OAuth2PasswordBearer,
432 Page, Pagination, PaginationConfig, Path, PathParams, Query, QueryParams, State, UserAgent,
433 XRequestId,
434 };
435}
436
437/// Extractors module for request data extraction (extended).
438pub mod extract {
439 pub use fastapi_core::{
440 Accept, AppState, Authorization, ContentType, FromHeaderValue, Header, HeaderExtractError,
441 HeaderName, HeaderValues, Host, Json, JsonConfig, JsonExtractError, NamedHeader,
442 OAuth2BearerError, OAuth2BearerErrorKind, OAuth2PasswordBearer, OAuth2PasswordBearerConfig,
443 Path, PathExtractError, PathParams, Query, QueryExtractError, QueryParams, State,
444 StateExtractError, UserAgent, XRequestId,
445 };
446}
447
448/// HTTP server module with server types and configuration.
449pub mod server {
450 pub use fastapi_http::{
451 // Configuration constants
452 DEFAULT_DRAIN_TIMEOUT_SECS,
453 DEFAULT_KEEP_ALIVE_TIMEOUT_SECS,
454 DEFAULT_MAX_CONNECTIONS,
455 DEFAULT_MAX_REQUESTS_PER_CONNECTION,
456 DEFAULT_READ_BUFFER_SIZE,
457 DEFAULT_REQUEST_TIMEOUT_SECS,
458 // Shutdown coordination
459 GracefulOutcome,
460 // Error types
461 ServeError,
462 // Server types
463 Server,
464 ServerConfig,
465 ServerError,
466 ShutdownController,
467 ShutdownReceiver,
468 TcpServer,
469 // Server functions
470 serve,
471 serve_with_config,
472 };
473}
474
475/// Extension trait for generating OpenAPI specifications from applications.
476pub trait OpenApiExt {
477 /// Generate an OpenAPI specification from the application.
478 ///
479 /// This creates an OpenAPI 3.1 document based on the application's
480 /// configuration and registered routes.
481 ///
482 /// # Example
483 ///
484 /// ```ignore
485 /// use fastapi::prelude::*;
486 /// use fastapi::OpenApiExt;
487 ///
488 /// let app = App::builder()
489 /// .config(AppConfig::new().name("My API").version("1.0.0"))
490 /// .build();
491 ///
492 /// let spec = app.openapi();
493 /// println!("{}", serde_json::to_string_pretty(&spec).unwrap());
494 /// ```
495 fn openapi(&self) -> OpenApi;
496
497 /// Generate an OpenAPI specification with custom configuration.
498 fn openapi_with<F>(&self, configure: F) -> OpenApi
499 where
500 F: FnOnce(OpenApiBuilder) -> OpenApiBuilder;
501}
502
503impl OpenApiExt for App {
504 fn openapi(&self) -> OpenApi {
505 self.openapi_with(|b| b)
506 }
507
508 fn openapi_with<F>(&self, configure: F) -> OpenApi
509 where
510 F: FnOnce(OpenApiBuilder) -> OpenApiBuilder,
511 {
512 let mut builder = OpenApiBuilder::new(&self.config().name, &self.config().version);
513 builder = configure(builder);
514 builder.build()
515 }
516}
517
518// Note: operation_id generation lives in `fastapi-router::Route` and in the
519// OpenAPI builder layer; keep the facade crate lean.