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