pub struct App<S = ()> { /* private fields */ }Expand description
A Kerniq application: routes, state, middleware, configuration and lifecycle in one builder.
use kerniq_server::prelude::*;
#[tokio::main]
async fn main() -> Result<()> {
App::new()
.get("/", || async { "Hello from Kerniq" })
.run("127.0.0.1:3000")
.await
}§State
App<S> is parameterized by its state type. App::new creates an
App<()>; App::state switches to App<T>, after which handlers can
extract State<T>. Routes registered before state (which cannot use
State<T>) are kept.
§Finalization
Nothing is assembled until the app is run or converted with
App::into_axum_router. At that point:
- routes are combined into a single
axum::Router(no runtime nesting), - the JSON 404/405 fallbacks are installed,
- the state is provided,
- middleware is applied in declaration order (first = outermost),
followed by the middleware implied by
ServerConfig(request_timeout→max_concurrent_requests→max_body_size, outermost to innermost).
Implementations§
Source§impl App<()>
impl App<()>
Sourcepub fn new() -> Self
pub fn new() -> Self
Creates an application with no routes, no state and default configuration.
Sourcepub fn state<T>(self, state: T) -> App<T>
pub fn state<T>(self, state: T) -> App<T>
Sets the application state, available to handlers via State<T>.
The state is cloned for each request that extracts it, so it should
be cheap to clone: wrap expensive resources in Arc (connection
pools usually already are). Sub-states can be extracted with Axum’s
FromRef.
use std::sync::Arc;
use kerniq_server::prelude::*;
#[derive(Clone)]
struct AppState {
service_name: Arc<str>,
}
async fn name(State(state): State<AppState>) -> String {
state.service_name.to_string()
}
let app = App::new()
.state(AppState { service_name: "customer-api".into() })
.get("/name", name);Source§impl<S> App<S>
impl<S> App<S>
Sourcepub fn get<H, T>(self, path: &str, handler: H) -> Selfwhere
H: Handler<T, S>,
T: 'static,
pub fn get<H, T>(self, path: &str, handler: H) -> Selfwhere
H: Handler<T, S>,
T: 'static,
Registers a GET handler (also answers HEAD).
Paths use {name} captures; :name is accepted and converted.
Sourcepub fn post<H, T>(self, path: &str, handler: H) -> Selfwhere
H: Handler<T, S>,
T: 'static,
pub fn post<H, T>(self, path: &str, handler: H) -> Selfwhere
H: Handler<T, S>,
T: 'static,
Registers a POST handler.
Sourcepub fn put<H, T>(self, path: &str, handler: H) -> Selfwhere
H: Handler<T, S>,
T: 'static,
pub fn put<H, T>(self, path: &str, handler: H) -> Selfwhere
H: Handler<T, S>,
T: 'static,
Registers a PUT handler.
Sourcepub fn patch<H, T>(self, path: &str, handler: H) -> Selfwhere
H: Handler<T, S>,
T: 'static,
pub fn patch<H, T>(self, path: &str, handler: H) -> Selfwhere
H: Handler<T, S>,
T: 'static,
Registers a PATCH handler.
Sourcepub fn delete<H, T>(self, path: &str, handler: H) -> Selfwhere
H: Handler<T, S>,
T: 'static,
pub fn delete<H, T>(self, path: &str, handler: H) -> Selfwhere
H: Handler<T, S>,
T: 'static,
Registers a DELETE handler.
Sourcepub fn options<H, T>(self, path: &str, handler: H) -> Selfwhere
H: Handler<T, S>,
T: 'static,
pub fn options<H, T>(self, path: &str, handler: H) -> Selfwhere
H: Handler<T, S>,
T: 'static,
Registers an OPTIONS handler.
Sourcepub fn head<H, T>(self, path: &str, handler: H) -> Selfwhere
H: Handler<T, S>,
T: 'static,
pub fn head<H, T>(self, path: &str, handler: H) -> Selfwhere
H: Handler<T, S>,
T: 'static,
Registers a HEAD handler.
Sourcepub fn trace<H, T>(self, path: &str, handler: H) -> Selfwhere
H: Handler<T, S>,
T: 'static,
pub fn trace<H, T>(self, path: &str, handler: H) -> Selfwhere
H: Handler<T, S>,
T: 'static,
Registers a TRACE handler.
Sourcepub fn any<H, T>(self, path: &str, handler: H) -> Selfwhere
H: Handler<T, S>,
T: 'static,
pub fn any<H, T>(self, path: &str, handler: H) -> Selfwhere
H: Handler<T, S>,
T: 'static,
Registers a handler for every method.
Sourcepub fn on<H, T>(self, method: Method, path: &str, handler: H) -> Selfwhere
H: Handler<T, S>,
T: 'static,
pub fn on<H, T>(self, method: Method, path: &str, handler: H) -> Selfwhere
H: Handler<T, S>,
T: 'static,
Registers a handler for an arbitrary standard method.
See Router::on.
Sourcepub fn route(self, path: &str, method_router: MethodRouter<S>) -> Self
pub fn route(self, path: &str, method_router: MethodRouter<S>) -> Self
Registers an Axum MethodRouter (advanced use: per-route layers,
several methods at once). See Router::route.
Sourcepub fn group(self, f: impl FnOnce(&mut Group<'_, S>)) -> Self
pub fn group(self, f: impl FnOnce(&mut Group<'_, S>)) -> Self
Registers several routes in a block. See Router::group.
Sourcepub fn mount(self, path: &str, router: Router<S>) -> Self
pub fn mount(self, path: &str, router: Router<S>) -> Self
Mounts a Kerniq Router under path (combined with the router’s
own prefix).
use kerniq_server::prelude::*;
let users = Router::new().get("/", list).post("/", create);
let app = App::new()
.mount("/users", users)
.mount("/accounts", Router::new().get("/", accounts));Sourcepub fn mount_axum(self, path: &str, router: Router<S>) -> Self
pub fn mount_axum(self, path: &str, router: Router<S>) -> Self
Mounts an existing axum::Router under path ("/" merges it at
the root). Its routes, layers and handlers are used as-is.
Sourcepub fn middleware<M: Middleware>(self, middleware: M) -> Self
pub fn middleware<M: Middleware>(self, middleware: M) -> Self
Adds application-wide middleware. The first middleware added is the outermost. Applies to every route and to the 404 fallback, regardless of whether routes are registered before or after this call.
Sourcepub fn layer<L>(self, layer: L) -> Selfwhere
TowerLayer<L>: Middleware,
pub fn layer<L>(self, layer: L) -> Selfwhere
TowerLayer<L>: Middleware,
Adds an application-wide Tower layer. Shares the ordered stack with
middleware.
Sourcepub fn fallback<H, T>(self, handler: H) -> Selfwhere
H: Handler<T, S>,
T: 'static,
pub fn fallback<H, T>(self, handler: H) -> Selfwhere
H: Handler<T, S>,
T: 'static,
Replaces the default JSON 404 NOT_FOUND fallback.
Sourcepub fn observability(self, observability: Observability) -> Self
Available on crate feature observability only.
pub fn observability(self, observability: Observability) -> Self
observability only.Enables observability (feature observability).
use kerniq_server::prelude::*;
use kerniq_server::observability::prelude::*;
#[tokio::main]
async fn main() -> Result<()> {
let observability = Observability::builder()
.service_name("payment-api")
.metrics(MetricsConfig::new())
.build()
.await?;
App::new()
.observability(observability)
.get("/health", || async { "OK" })
.run("0.0.0.0:3000")
.await
}At finalization:
- HTTP instrumentation (metrics, a span per request, W3C context extraction) becomes the outermost middleware, so it measures everything inside it, including timeouts, rate limiting and the 404 fallback;
- a Prometheus endpoint configured on the application listener is added outside all middleware, so scrapes are never instrumented, rate-limited or timed out; one configured with its own listener is served by a background task bound when the app starts.
Under run/serve, telemetry is flushed
and shut down after connections and background tasks have drained,
bounded by the observability shutdown timeout. With
into_axum_router the application owns the
handle and calls shutdown itself.
Sourcepub fn config(self, config: ServerConfig) -> Self
pub fn config(self, config: ServerConfig) -> Self
Sets the server configuration.
Sourcepub fn server_config(&self) -> &ServerConfig
pub fn server_config(&self) -> &ServerConfig
The current server configuration.
Sourcepub fn background_task<F, Fut>(self, name: &'static str, task: F) -> Self
pub fn background_task<F, Fut>(self, name: &'static str, task: F) -> Self
Registers a background task started when the server starts. It
receives a Shutdown handle and should return promptly once it
triggers; the server waits up to shutdown_timeout for it.
use std::time::Duration;
use kerniq_server::prelude::*;
let app = App::new().background_task("heartbeat", |shutdown: Shutdown| async move {
let mut tick = tokio::time::interval(Duration::from_secs(30));
loop {
tokio::select! {
_ = tick.tick() => tracing::info!("still alive"),
() = shutdown.wait() => break,
}
}
});Sourcepub fn shutdown_signal<F>(self, signal: F) -> Self
pub fn shutdown_signal<F>(self, signal: F) -> Self
Replaces the default shutdown trigger (Ctrl+C / SIGTERM) with
signal. Shutdown can also be triggered at any time through
shutdown_handle.
Sourcepub fn shutdown_handle(&self) -> Shutdown
pub fn shutdown_handle(&self) -> Shutdown
A handle that triggers or observes this app’s shutdown. Store it in state so long-lived handlers (WebSockets, streams) can finish when the server stops.
Sourcepub fn routes(&self) -> &[RouteInfo]
pub fn routes(&self) -> &[RouteInfo]
The registered routes (for diagnostics; mounted Axum routers appear as a single opaque entry).
Sourcepub fn into_axum_router(self) -> Router
pub fn into_axum_router(self) -> Router
Finalizes the application into a plain axum::Router, with
fallbacks, state and all middleware applied.
Use this to run Kerniq under axum::serve, embed it into a larger
Axum application, or test it with tower::ServiceExt::oneshot.
Lifecycle features (background tasks, shutdown signal, connection
limits) belong to App::run and are not part of the router.
Sourcepub async fn run(self, address: impl AsRef<str>) -> Result<()>
pub async fn run(self, address: impl AsRef<str>) -> Result<()>
Binds address and serves until shutdown. Requires a Tokio runtime
(for example #[tokio::main]).
address is anything tokio::net::TcpListener::bind accepts as a
string: "127.0.0.1:3000", "0.0.0.0:8080", "localhost:3000",
"[::1]:3000". To use the configured address, pass
config.address().
Sourcepub async fn serve(self, listener: TcpListener) -> Result<()>
pub async fn serve(self, listener: TcpListener) -> Result<()>
Serves on an already-bound listener (useful for tests with port 0, socket activation, or binding before dropping privileges).
Sourcepub fn run_blocking(self, address: impl AsRef<str>) -> Result<()>
pub fn run_blocking(self, address: impl AsRef<str>) -> Result<()>
Like run, but creates and owns a multi-threaded Tokio
runtime (with workers threads, if configured).
use kerniq_server::prelude::*;
fn main() -> Result<()> {
App::new()
.get("/", || async { "OK" })
.run_blocking("0.0.0.0:3000")
}§Errors
Returns an error (and does not start) if called from inside a Tokio
runtime: nested runtimes are not allowed. Use .run(addr).await there.
Trait Implementations§
Auto Trait Implementations§
impl<S = ()> !RefUnwindSafe for App<S>
impl<S = ()> !Sync for App<S>
impl<S = ()> !UnwindSafe for App<S>
impl<S> Forget for App<S>
impl<S> Freeze for App<S>
impl<S> Send for App<S>
impl<S> Unpin for App<S>
impl<S> UnsafeUnpin for App<S>
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<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> FutureExt for T
impl<T> FutureExt for T
Source§fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ
fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ
Source§fn with_current_context(self) -> WithContext<Self> ⓘ
fn with_current_context(self) -> WithContext<Self> ⓘ
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> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::RequestSource§impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<T> ServiceExt for T
impl<T> ServiceExt for T
Source§fn compression(self) -> Compression<Self>where
Self: Sized,
fn compression(self) -> Compression<Self>where
Self: Sized,
compression-br or compression-deflate or compression-gzip or compression-zstd only.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,
trace only.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,
trace only.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,
request-id only.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,
request-id only.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,
request-id only.Source§fn propagate_x_request_id(self) -> PropagateRequestId<Self>where
Self: Sized,
fn propagate_x_request_id(self) -> PropagateRequestId<Self>where
Self: Sized,
request-id only.x-request-id as the header name. Read more