pub struct App { /* private fields */ }Expand description
Builder for an Aro web application.
Wraps an Axum Router and applies framework defaults (tracing middleware,
JSON error fallbacks) while allowing full customization.
§Example
use aro_web::App;
use axum::Router;
use axum::routing::get;
App::new()
.routes(Router::new().route("/", get(|| async { "ok" })))
.serve("0.0.0.0:3000".parse()?)
.await?;Implementations§
Source§impl App
impl App
Sourcepub fn routes(self, router: Router) -> Self
pub fn routes(self, router: Router) -> Self
Merge the given router’s routes into the application.
Can be called multiple times to compose routes from different modules.
Sourcepub fn register<T: ?Sized + 'static>(self, service: Arc<T>) -> Self
pub fn register<T: ?Sized + 'static>(self, service: Arc<T>) -> Self
Register a dependency for injection.
The type parameter T should be a trait object type (e.g. dyn UserRepo).
Registered dependencies are accumulated internally and merged into the
final AroState at build time. When combined with
state, registrations from this method take precedence
over entries with the same type from external state.
§Example
// Domain types (`UserRepo`, `UserRepoImpl`) come from your app.
App::new()
.register::<dyn UserRepo>(Arc::new(UserRepoImpl::new(pool)))
.routes_with_state(routes)
.build()Sourcepub fn routes_with_state(self, router: Router<AroState>) -> Self
pub fn routes_with_state(self, router: Router<AroState>) -> Self
Sourcepub fn layer<L>(self, layer: L) -> Selfwhere
L: Layer<Route> + Clone + Send + Sync + 'static,
L::Service: Service<Request<Body>> + Clone + Send + Sync + 'static,
<L::Service as Service<Request<Body>>>::Response: IntoResponse + 'static,
<L::Service as Service<Request<Body>>>::Error: Into<Infallible> + 'static,
<L::Service as Service<Request<Body>>>::Future: Send + 'static,
pub fn layer<L>(self, layer: L) -> Selfwhere
L: Layer<Route> + Clone + Send + Sync + 'static,
L::Service: Service<Request<Body>> + Clone + Send + Sync + 'static,
<L::Service as Service<Request<Body>>>::Response: IntoResponse + 'static,
<L::Service as Service<Request<Body>>>::Error: Into<Infallible> + 'static,
<L::Service as Service<Request<Body>>>::Future: Send + 'static,
Add a tower Layer to the application.
User layers wrap the default middleware stack (outermost).
They execute in the order they are added and apply to all routes,
including those registered via routes_with_state.
Each call wraps the stack, so the last layer added is outermost on inbound
requests.
Sourcepub fn body_limit(self, max_bytes: usize) -> Self
pub fn body_limit(self, max_bytes: usize) -> Self
Set the maximum allowed request body size in bytes.
Applies DefaultBodyLimit::max as a global layer, overriding
Axum’s default 2 MB limit for extractors like Json, Form, and
Bytes. Individual routes can still override this with a
per-route DefaultBodyLimit layer.
§Example
use aro_web::App;
use axum::Router;
let _app = App::new()
.body_limit(10 * 1024 * 1024) // 10 MB
.routes(Router::new())
.build();Sourcepub fn config<T: DeserializeOwned + Send + Sync + 'static>(
self,
) -> Result<Self, ConfigError>
pub fn config<T: DeserializeOwned + Send + Sync + 'static>( self, ) -> Result<Self, ConfigError>
Load layered configuration and register it as a dependency.
Uses load_config to build T
from environment variables and config files, then registers the result
so it can be extracted with Dep<T>.
§Errors
Returns an error if configuration loading or deserialization fails.
Sourcepub fn on_startup<F>(self, hook: F) -> Self
pub fn on_startup<F>(self, hook: F) -> Self
Register an async startup hook.
Hooks run in the order they are registered during serve()
or build_with_hooks(), before the TCP
listener is bound. Each hook receives a shared reference to the resolved
AroState. If any hook returns an error, the server will not start
and serve() will return the error.
§Example
use aro_web::App;
use axum::Router;
App::new()
.on_startup(|_state| Box::pin(async move { Ok(()) }))
.routes(Router::new())
.serve("127.0.0.1:0".parse()?)
.await?;Sourcepub fn timeout(self, duration: Duration) -> Self
pub fn timeout(self, duration: Duration) -> Self
Apply a request timeout to all routes.
Requests that are not completed within the given duration will receive
a 408 Request Timeout response.
This is the canonical builder entry point: the timeout applies to all
routes, including stateful DI routes added via
routes_with_state.
For manual Router::layer(...) composition, see
crate::middleware::timeout.
§Example
use std::time::Duration;
use aro_web::App;
use axum::Router;
let _app = App::new()
.routes(Router::new())
.timeout(Duration::from_secs(30))
.build();Sourcepub fn without_defaults(self) -> Self
pub fn without_defaults(self) -> Self
Disable the default middleware stack (TraceLayer).
Call this if you want full control over the middleware pipeline. The JSON fallback handler is always applied.
Sourcepub fn build(self) -> Router
pub fn build(self) -> Router
Build the final Router, applying default middleware.
The default middleware stack includes:
- JSON 404/405 fallback handlers for unmatched routes and unsupported methods
TraceLayerfrom tower-http for request/response logging
Note: Startup hooks registered via on_startup
are not executed by build(). Use build_with_hooks()
or serve() to run them.
Sourcepub async fn build_with_hooks(self) -> Result<Router, Box<dyn Error>>
pub async fn build_with_hooks(self) -> Result<Router, Box<dyn Error>>
Build the final Router, running all startup hooks first.
This is the same as build() but runs all registered
startup hooks in order before returning the router. Use this in tests
when your app relies on startup hooks (e.g., database migrations).
§Errors
Returns an error if any startup hook fails.
§Example
use aro_web::App;
use axum::Router;
let _router = App::new()
.on_startup(|_state| Box::pin(async move { Ok(()) }))
.routes(Router::new())
.build_with_hooks()
.await?;Sourcepub async fn serve(self, addr: SocketAddr) -> Result<(), Box<dyn Error>>
pub async fn serve(self, addr: SocketAddr) -> Result<(), Box<dyn Error>>
Build and start serving the application.
Runs all registered startup hooks in order, then binds to the given address and serves with graceful shutdown (handles SIGINT/Ctrl+C and SIGTERM).
Note: Like build_with_hooks, this runs
startup hooks. build alone does not.
If any startup hook returns an error, the server is not started and the error is returned.
Trait Implementations§
Auto Trait Implementations§
impl !RefUnwindSafe for App
impl !Sync for App
impl !UnwindSafe for App
impl Freeze for App
impl Send for App
impl Unpin for App
impl UnsafeUnpin for App
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> ServiceExt for T
impl<T> ServiceExt for T
Source§fn trace_for_http(self) -> Trace<Self, SharedClassifier<ServerErrorsAsFailures>>where
Self: Sized,
fn trace_for_http(self) -> Trace<Self, SharedClassifier<ServerErrorsAsFailures>>where
Self: Sized,
Source§fn trace_for_grpc(self) -> Trace<Self, SharedClassifier<GrpcErrorsAsFailures>>where
Self: Sized,
fn trace_for_grpc(self) -> Trace<Self, SharedClassifier<GrpcErrorsAsFailures>>where
Self: Sized,
Source§fn override_request_header<M>(
self,
header_name: HeaderName,
make: M,
) -> SetRequestHeader<Self, M>where
Self: Sized,
fn override_request_header<M>(
self,
header_name: HeaderName,
make: M,
) -> SetRequestHeader<Self, M>where
Self: Sized,
Source§fn append_request_header<M>(
self,
header_name: HeaderName,
make: M,
) -> SetRequestHeader<Self, M>where
Self: Sized,
fn append_request_header<M>(
self,
header_name: HeaderName,
make: M,
) -> SetRequestHeader<Self, M>where
Self: Sized,
Source§fn insert_request_header_if_not_present<M>(
self,
header_name: HeaderName,
make: M,
) -> SetRequestHeader<Self, M>where
Self: Sized,
fn insert_request_header_if_not_present<M>(
self,
header_name: HeaderName,
make: M,
) -> SetRequestHeader<Self, M>where
Self: Sized,
Source§fn override_response_header<M>(
self,
header_name: HeaderName,
make: M,
) -> SetResponseHeader<Self, M>where
Self: Sized,
fn override_response_header<M>(
self,
header_name: HeaderName,
make: M,
) -> SetResponseHeader<Self, M>where
Self: Sized,
Source§fn append_response_header<M>(
self,
header_name: HeaderName,
make: M,
) -> SetResponseHeader<Self, M>where
Self: Sized,
fn append_response_header<M>(
self,
header_name: HeaderName,
make: M,
) -> SetResponseHeader<Self, M>where
Self: Sized,
Source§fn insert_response_header_if_not_present<M>(
self,
header_name: HeaderName,
make: M,
) -> SetResponseHeader<Self, M>where
Self: Sized,
fn insert_response_header_if_not_present<M>(
self,
header_name: HeaderName,
make: M,
) -> SetResponseHeader<Self, M>where
Self: Sized,
Source§fn set_request_id<M>(
self,
header_name: HeaderName,
make_request_id: M,
) -> SetRequestId<Self, M>where
Self: Sized,
M: MakeRequestId,
fn set_request_id<M>(
self,
header_name: HeaderName,
make_request_id: M,
) -> SetRequestId<Self, M>where
Self: Sized,
M: MakeRequestId,
Source§fn set_x_request_id<M>(self, make_request_id: M) -> SetRequestId<Self, M>where
Self: Sized,
M: MakeRequestId,
fn set_x_request_id<M>(self, make_request_id: M) -> SetRequestId<Self, M>where
Self: Sized,
M: MakeRequestId,
x-request-id as the header name. Read moreSource§fn propagate_request_id(
self,
header_name: HeaderName,
) -> PropagateRequestId<Self>where
Self: Sized,
fn propagate_request_id(
self,
header_name: HeaderName,
) -> PropagateRequestId<Self>where
Self: Sized,
Source§fn propagate_x_request_id(self) -> PropagateRequestId<Self>where
Self: Sized,
fn propagate_x_request_id(self) -> PropagateRequestId<Self>where
Self: Sized,
x-request-id as the header name. Read more