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//! | `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.