Skip to main content

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// Re-export commonly used types
247pub use fastapi_core::{
248    App, AppBuilder, AppConfig, Cors, CorsConfig, Cx, DefaultConfig, DefaultDependencyConfig,
249    DependencyOverrides, DependencyScope, Depends, DependsConfig, FromDependency, FromRequest,
250    HttpError, IntoResponse, Method, NoCache, Request, RequestId, RequestIdConfig,
251    RequestIdMiddleware, Response, ResponseBody, StateContainer, StatusCode, ValidationError,
252    ValidationErrors,
253};
254
255// Re-export extractors
256pub use fastapi_core::{
257    // Common header types
258    Accept,
259    AddResponseHeader,
260    AppState,
261    Authorization,
262    // Background tasks
263    BackgroundTasks,
264    // Auth extractors
265    BasicAuth,
266    BasicAuthError,
267    BearerToken,
268    BearerTokenError,
269    ContentType,
270    // Cookies
271    Cookie,
272    DEFAULT_PAGE,
273    DEFAULT_PER_PAGE,
274    // Headers
275    Header,
276    HeaderExtractError,
277    HeaderValues,
278    Host,
279    // Body extractors
280    Json,
281    JsonConfig,
282    JsonExtractError,
283    MAX_PER_PAGE,
284    NamedHeader,
285    OAuth2BearerError,
286    OAuth2PasswordBearer,
287    OAuth2PasswordBearerConfig,
288    Page,
289    // Pagination
290    Pagination,
291    PaginationConfig,
292    // Path parameters
293    Path,
294    PathExtractError,
295    PathParams,
296    // Query string
297    Query,
298    QueryExtractError,
299    QueryParams,
300    RequestContext,
301    SameSite,
302    // State
303    State,
304    UserAgent,
305    XRequestId,
306};
307
308// Re-export testing utilities
309#[cfg(feature = "testing")]
310pub use fastapi_core::{CookieJar, RequestBuilder, TestClient, TestResponse};
311pub use fastapi_macros::{JsonSchema, Validate, delete, get, head, options, patch, post, put};
312pub use fastapi_openapi::{OpenApi, OpenApiBuilder, SchemaRegistry};
313pub use fastapi_router::{
314    // Route matching
315    AllowedMethods,
316    ConversionError,
317    // Path parameter types
318    Converter,
319    // Error types
320    InvalidRouteError,
321    ParamInfo,
322    ParamValue,
323    // Core router types
324    Route,
325    RouteAddError,
326    RouteConflictError,
327    RouteLookup,
328    RouteMatch,
329    Router,
330};
331
332// Re-export HTTP server types
333pub use fastapi_http::{
334    GracefulOutcome, ServeError, Server, ServerConfig, ServerError, ShutdownController,
335    ShutdownReceiver, TcpServer, serve, serve_with_config,
336};
337
338/// Prelude module for convenient imports.
339pub mod prelude {
340    pub use crate::{
341        // Core types
342        App,
343        AppBuilder,
344        AppConfig,
345        // Auth
346        BasicAuth,
347        BearerToken,
348        Cookie,
349        Cors,
350        CorsConfig,
351        // asupersync context
352        Cx,
353        DefaultConfig,
354        DefaultDependencyConfig,
355        DependencyOverrides,
356        DependencyScope,
357        Depends,
358        DependsConfig,
359        FromDependency,
360        FromRequest,
361        Header,
362        HttpError,
363        IntoResponse,
364        // Extractors
365        Json,
366        // Macros
367        JsonSchema,
368        Method,
369        NoCache,
370        OAuth2PasswordBearer,
371        // OpenAPI
372        OpenApi,
373        OpenApiBuilder,
374        Page,
375        // Pagination
376        Pagination,
377        Path,
378        Query,
379        Request,
380        RequestContext,
381        RequestId,
382        RequestIdMiddleware,
383        Response,
384        Route,
385        Router,
386        // Server
387        Server,
388        ServerConfig,
389        State,
390        StatusCode,
391        Validate,
392        ValidationError,
393        ValidationErrors,
394        delete,
395        get,
396        head,
397        options,
398        patch,
399        post,
400        put,
401        serve,
402    };
403    pub use serde::{Deserialize, Serialize};
404}
405
406/// Testing utilities module.
407#[cfg(feature = "testing")]
408pub mod testing {
409    pub use fastapi_core::testing::{CookieJar, RequestBuilder, TestClient, TestResponse};
410}
411
412/// Extractors module for type-safe request data extraction.
413pub mod extractors {
414    pub use fastapi_core::{
415        Accept, AppState, Authorization, BackgroundTasks, BasicAuth, BearerToken, ContentType,
416        Cookie, Header, HeaderValues, Host, Json, JsonConfig, NamedHeader, OAuth2PasswordBearer,
417        Page, Pagination, PaginationConfig, Path, PathParams, Query, QueryParams, State, UserAgent,
418        XRequestId,
419    };
420}
421
422/// Extractors module for request data extraction (extended).
423pub mod extract {
424    pub use fastapi_core::{
425        Accept, AppState, Authorization, ContentType, FromHeaderValue, Header, HeaderExtractError,
426        HeaderName, HeaderValues, Host, Json, JsonConfig, JsonExtractError, NamedHeader,
427        OAuth2BearerError, OAuth2BearerErrorKind, OAuth2PasswordBearer, OAuth2PasswordBearerConfig,
428        Path, PathExtractError, PathParams, Query, QueryExtractError, QueryParams, State,
429        StateExtractError, UserAgent, XRequestId,
430    };
431}
432
433/// HTTP server module with server types and configuration.
434pub mod server {
435    pub use fastapi_http::{
436        // Configuration constants
437        DEFAULT_DRAIN_TIMEOUT_SECS,
438        DEFAULT_KEEP_ALIVE_TIMEOUT_SECS,
439        DEFAULT_MAX_CONNECTIONS,
440        DEFAULT_MAX_REQUESTS_PER_CONNECTION,
441        DEFAULT_READ_BUFFER_SIZE,
442        DEFAULT_REQUEST_TIMEOUT_SECS,
443        // Shutdown coordination
444        GracefulOutcome,
445        // Error types
446        ServeError,
447        // Server types
448        Server,
449        ServerConfig,
450        ServerError,
451        ShutdownController,
452        ShutdownReceiver,
453        TcpServer,
454        // Server functions
455        serve,
456        serve_with_config,
457    };
458}
459
460/// Extension trait for generating OpenAPI specifications from applications.
461pub trait OpenApiExt {
462    /// Generate an OpenAPI specification from the application.
463    ///
464    /// This creates an OpenAPI 3.1 document based on the application's
465    /// configuration and registered routes.
466    ///
467    /// # Example
468    ///
469    /// ```ignore
470    /// use fastapi::prelude::*;
471    /// use fastapi::OpenApiExt;
472    ///
473    /// let app = App::builder()
474    ///     .config(AppConfig::new().name("My API").version("1.0.0"))
475    ///     .build();
476    ///
477    /// let spec = app.openapi();
478    /// println!("{}", serde_json::to_string_pretty(&spec).unwrap());
479    /// ```
480    fn openapi(&self) -> OpenApi;
481
482    /// Generate an OpenAPI specification with custom configuration.
483    fn openapi_with<F>(&self, configure: F) -> OpenApi
484    where
485        F: FnOnce(OpenApiBuilder) -> OpenApiBuilder;
486}
487
488impl OpenApiExt for App {
489    fn openapi(&self) -> OpenApi {
490        self.openapi_with(|b| b)
491    }
492
493    fn openapi_with<F>(&self, configure: F) -> OpenApi
494    where
495        F: FnOnce(OpenApiBuilder) -> OpenApiBuilder,
496    {
497        let mut builder = OpenApiBuilder::new(&self.config().name, &self.config().version);
498        builder = configure(builder);
499        builder.build()
500    }
501}
502
503// Note: operation_id generation lives in `fastapi-router::Route` and in the
504// OpenAPI builder layer; keep the facade crate lean.