fraiseql_server/lib.rs
1//! FraiseQL HTTP Server
2//!
3//! HTTP server for FraiseQL v2 compiled GraphQL execution engine.
4//!
5//! # Architecture
6//!
7//! The server exposes a GraphQL HTTP endpoint that:
8//! 1. Receives GraphQL queries via POST
9//! 2. Executes queries using the runtime Executor
10//! 3. Returns GraphQL-compliant JSON responses
11//!
12//! # Features
13//!
14//! - GraphQL endpoint (`/graphql`)
15//! - Health check endpoint (`/health`)
16//! - Schema introspection endpoint (`/introspection`)
17//! - CORS support
18//! - Compression (gzip, br, zstd)
19//! - Request tracing
20//! - APQ (Automatic Persisted Queries)
21//! - Query caching
22//! - Authentication middleware (optional)
23
24#![forbid(unsafe_code)]
25#![allow(missing_docs)] // Reason: focusing on actionable warnings first; docs are a separate effort
26#![warn(clippy::all)]
27#![warn(clippy::pedantic)]
28// Reason: module-level allows for pedantic lints that are too noisy across this crate.
29// Each allow has a justification. Prefer per-function allows for new code.
30#![allow(clippy::struct_excessive_bools)] // Reason: ServerConfig uses bools for independent feature flags
31#![allow(clippy::cast_possible_truncation)] // Reason: intentional u128->u64 casts for metrics counters
32#![allow(clippy::cast_precision_loss)] // Reason: intentional f64 conversions for averaging metrics
33#![allow(clippy::doc_markdown)] // Reason: backtick-wrapping all technical terms would reduce readability
34#![allow(clippy::module_name_repetitions)] // Reason: standard Rust API style (e.g., ServerConfig in server mod)
35#![allow(clippy::must_use_candidate)] // Reason: builder methods return Self but callers chain, not inspect
36#![allow(clippy::missing_errors_doc)] // Reason: 69 functions need documentation; deferred to Phase 6 follow-up
37#![allow(clippy::missing_panics_doc)] // Reason: panics are eliminated by design; remaining are unreachable
38#![allow(clippy::needless_pass_by_value)] // Reason: axum extractors require owned types in handler signatures
39#![allow(clippy::unused_async)] // Reason: axum handler trait requires async fn even for sync operations
40#![allow(clippy::similar_names)] // Reason: domain terms (e.g., req/res, row/col) are conventional pairs
41#![allow(clippy::unused_self)] // Reason: trait implementations require &self for interface consistency
42#![allow(clippy::match_same_arms)] // Reason: explicit arms document each variant's intent in state machines
43#![allow(clippy::double_must_use)] // Reason: CorsLayer from tower-http already carries #[must_use]
44#![allow(clippy::unnecessary_wraps)] // Reason: handler signatures must return Result for axum compatibility
45#![allow(clippy::return_self_not_must_use)] // Reason: builder pattern methods are always chained, never discarded
46#![allow(clippy::too_many_lines)] // Reason: route setup and middleware composition are inherently verbose
47#![allow(clippy::cast_sign_loss)] // Reason: intentional signed->unsigned for timestamp/duration conversions
48#![allow(clippy::missing_fields_in_debug)] // Reason: connection pools and secrets excluded from Debug for safety
49#![allow(clippy::default_trait_access)] // Reason: Default::default() is clearer than type inference in structs
50#![allow(clippy::wildcard_imports)] // Reason: test modules use wildcard imports for concise test setup
51#![allow(clippy::items_after_statements)] // Reason: helper structs near point of use improves test readability
52#![allow(clippy::no_effect_underscore_binding)] // Reason: placeholder bindings for future middleware hooks
53#![allow(clippy::cast_possible_wrap)] // Reason: timestamp and duration values are positive, within i64 range
54#![allow(clippy::struct_field_names)] // Reason: field prefixes match domain terminology (e.g., auth_token)
55#![allow(clippy::single_char_pattern)] // Reason: string patterns like "/" are clearer than char '/' in routes
56#![allow(clippy::elidable_lifetime_names)] // Reason: explicit lifetimes document borrow relationships
57#![allow(clippy::manual_let_else)] // Reason: match with early return is clearer for multi-line extraction
58#![allow(clippy::redundant_closure)] // Reason: explicit closures clarify argument transformation in map chains
59#![allow(clippy::unchecked_time_subtraction)] // Reason: duration arithmetic on SystemTime is infallible for metrics
60#![allow(clippy::uninlined_format_args)] // Reason: named variables in format strings improve readability
61#![allow(clippy::unnested_or_patterns)] // Reason: flat patterns with comments are clearer for state transitions
62#![allow(clippy::used_underscore_binding)] // Reason: underscore-prefixed bindings used intentionally in destructuring
63#![allow(clippy::cast_lossless)] // Reason: explicit as casts make type conversion visible at call site
64#![allow(clippy::format_push_string)] // Reason: format!+push_str is clearer than write! for SQL query building
65#![allow(clippy::if_same_then_else)] // Reason: separate branches document distinct code paths for maintenance
66#![allow(clippy::ignored_unit_patterns)] // Reason: explicit _ in pattern matches documents intentional discard
67#![allow(clippy::map_unwrap_or)] // Reason: map().unwrap_or() reads left-to-right; clearer for chains
68#![allow(clippy::redundant_closure_for_method_calls)] // Reason: explicit closures clarify intent in higher-order functions
69#![allow(clippy::single_match_else)] // Reason: match with else is clearer than if-let for variant extraction
70#![allow(clippy::unnecessary_debug_formatting)] // Reason: Debug formatting in log messages provides diagnostic detail
71#![allow(clippy::useless_format)] // Reason: format!() used to satisfy String type requirements in some APIs
72#![allow(clippy::float_cmp)] // Reason: test assertions compare exact metric values, not computed floats
73
74// API key authentication
75pub mod api_key;
76// Token revocation
77pub mod token_revocation;
78
79// Original fraiseql-server modules
80pub mod api;
81pub mod error;
82pub mod extractors;
83pub mod federation;
84pub mod logging;
85pub mod middleware;
86pub mod performance;
87pub mod routes;
88pub mod schema;
89pub mod server;
90pub mod server_config;
91pub mod subscriptions;
92pub mod validation;
93
94// Renamed to avoid conflicts with runtime modules
95pub mod metrics_server;
96pub mod tracing_server;
97
98// fraiseql-runtime modules (merged)
99pub mod config;
100pub mod lifecycle;
101pub mod observability;
102pub mod operational;
103pub mod resilience;
104pub mod runtime_state;
105pub mod tracing_utils;
106
107// Webhooks (extracted to fraiseql-webhooks crate)
108pub use fraiseql_webhooks as webhooks;
109
110// fraiseql-files modules (merged)
111pub mod files;
112
113// Authentication (extracted to fraiseql-auth crate)
114pub use fraiseql_auth as auth;
115
116// Secrets management
117pub mod secrets;
118
119// Secrets management and encryption (extracted to fraiseql-secrets crate)
120pub use fraiseql_secrets::{encryption, secrets_manager};
121
122// Backup and disaster recovery
123pub mod backup;
124
125// TLS/SSL and encryption
126pub mod tls;
127pub mod tls_listener;
128
129// Observer management - optional
130#[cfg(feature = "observers")]
131pub mod observers;
132
133// Arrow Flight integration - optional
134#[cfg(feature = "arrow")]
135pub mod arrow;
136
137// MCP (Model Context Protocol) server - optional
138#[cfg(feature = "mcp")]
139pub mod mcp;
140
141// Trusted documents (query allowlist)
142pub mod trusted_documents;
143
144// Testing utilities
145#[cfg(any(test, feature = "testing"))]
146pub mod testing;
147
148pub use logging::{
149 ErrorDetails, LogLevel, LogMetrics, RequestContext, RequestId, RequestLogger, SourceLocation,
150 StructuredLogEntry,
151};
152pub use metrics_server::{MetricsCollector, PrometheusMetrics};
153pub use performance::{
154 OperationProfile, PerformanceMonitor, PerformanceStats, PerformanceTimer, QueryPerformance,
155};
156pub use schema::CompiledSchemaLoader;
157pub use secrets::SecretManager;
158pub use server::Server;
159pub use server_config::ServerConfig;
160pub use tls::TlsSetup;
161pub use tracing_server::{SpanStatus, TraceContext, TraceEvent, TraceParseError, TraceSpan};
162pub use validation::{RequestValidator, ValidationError};
163
164/// Server error type.
165#[derive(Debug, thiserror::Error)]
166pub enum ServerError {
167 /// Server binding error.
168 #[error("Failed to bind server: {0}")]
169 BindError(String),
170
171 /// Configuration error.
172 #[error("Configuration error: {0}")]
173 ConfigError(String),
174
175 /// Runtime error.
176 #[error("Runtime error: {0}")]
177 RuntimeError(#[from] fraiseql_core::error::FraiseQLError),
178
179 /// IO error.
180 #[error("IO error: {0}")]
181 IoError(#[from] std::io::Error),
182
183 /// Database error.
184 #[error("Database error: {0}")]
185 Database(String),
186
187 /// Validation error.
188 #[error("Validation error: {0}")]
189 Validation(String),
190
191 /// Resource conflict error.
192 #[error("Conflict: {0}")]
193 Conflict(String),
194
195 /// Resource not found error.
196 #[error("Not found: {0}")]
197 NotFound(String),
198}
199
200/// Server result type.
201pub type Result<T> = std::result::Result<T, ServerError>;