pub struct ServiceBootstrap { /* private fields */ }Expand description
Builder for a microservice runtime.
use socle::{ServiceBootstrap, BootstrapCtx, Result};
use axum::{Router, routing::get};
ServiceBootstrap::new("my-service")
.with_telemetry()
.with_router(|_ctx: &BootstrapCtx| Router::new().route("/health", get(|| async { "ok" })))
.serve("0.0.0.0:8080")
.awaitImplementations§
Source§impl ServiceBootstrap
impl ServiceBootstrap
Sourcepub fn with_dotenv(self) -> Self
pub fn with_dotenv(self) -> Self
Load .env file if present. Call this before any with_* methods that
read from environment variables.
Sourcepub fn from_config(
service_name: impl Into<Arc<str>>,
cfg: BootstrapConfig,
) -> Result<Self>
pub fn from_config( service_name: impl Into<Arc<str>>, cfg: BootstrapConfig, ) -> Result<Self>
Build from a BootstrapConfig.
Sourcepub async fn run(self) -> Result<()>
pub async fn run(self) -> Result<()>
Run using the bind address loaded from BootstrapConfig.
Sourcepub fn with_body_limit(self, bytes: usize) -> Self
pub fn with_body_limit(self, bytes: usize) -> Self
Override the maximum request body size in bytes. Defaults to 2 MiB.
Sourcepub fn with_shutdown_timeout(self, timeout: Duration) -> Self
pub fn with_shutdown_timeout(self, timeout: Duration) -> Self
Hard deadline on graceful shutdown drain. Defaults to 30 seconds.
Sourcepub fn with_shutdown_hook<F, Fut>(
self,
name: impl Into<String>,
timeout: Duration,
hook: F,
) -> Self
pub fn with_shutdown_hook<F, Fut>( self, name: impl Into<String>, timeout: Duration, hook: F, ) -> Self
Register an async drain callback that runs after the HTTP server stops.
Sourcepub fn with_readiness_check<F, Fut>(
self,
name: impl Into<String>,
check: F,
) -> Self
pub fn with_readiness_check<F, Fut>( self, name: impl Into<String>, check: F, ) -> Self
Register a readiness check.
Sourcepub fn with_health_probe(self, probe: impl HealthProbe + 'static) -> Self
pub fn with_health_probe(self, probe: impl HealthProbe + 'static) -> Self
Register a typed HealthProbe as a readiness check.
Sourcepub fn with_version(self, version: impl Into<String>) -> Self
pub fn with_version(self, version: impl Into<String>) -> Self
Override the version reported by the liveness endpoint.
Sourcepub fn with_health_path(self, path: impl Into<String>) -> Self
pub fn with_health_path(self, path: impl Into<String>) -> Self
Override the base path for health endpoints. Defaults to /health.
Sourcepub fn with_telemetry(self) -> Self
pub fn with_telemetry(self) -> Self
Enable basic tracing via tracing_subscriber.
Sourcepub fn with_telemetry_init<F>(self, f: F) -> Self
pub fn with_telemetry_init<F>(self, f: F) -> Self
Override the telemetry initialisation function.
When set, called instead of the built-in tracing_subscriber setup.
Use this to wire in a full OTel SDK (e.g. otel-bootstrap) from a
wrapper crate without forking serve().
The callback receives the service name and must return Ok(()) on
success or an Error that aborts startup.
Implies ServiceBootstrap::with_telemetry — no need to call both.
Prefer with_telemetry_provider for new code; it also handles
shutdown (span/metric flush) automatically.
Sourcepub fn with_telemetry_provider<P: TelemetryProvider>(self, provider: P) -> Self
pub fn with_telemetry_provider<P: TelemetryProvider>(self, provider: P) -> Self
Plug in a TelemetryProvider implementation.
The provider’s TelemetryProvider::init is called at startup and its
TelemetryProvider::on_shutdown is registered as a drain hook that
runs after the HTTP server stops (with a 30-second timeout).
Takes priority over with_telemetry_init and
ServiceBootstrap::with_telemetry when all are called. Implies ServiceBootstrap::with_telemetry.
Use this to wire in otel-bootstrap or any other OTel SDK from a
wrapper crate:
use socle::{ServiceBootstrap, ports::telemetry::TelemetryProvider, Result};
struct MyOtelProvider;
impl TelemetryProvider for MyOtelProvider {
fn init(&self, _: &str) -> Result<()> { Ok(()) }
}
ServiceBootstrap::new("svc").with_telemetry_provider(MyOtelProvider);Sourcepub fn with_database(self, url: impl Into<String>) -> Self
pub fn with_database(self, url: impl Into<String>) -> Self
Connect to a Postgres database and build a sqlx::PgPool.
Sourcepub fn with_db_pool(self, pool: PgPool) -> Self
pub fn with_db_pool(self, pool: PgPool) -> Self
Provide a pre-built sqlx::PgPool instead of a connection URL.
Use this when the pool is constructed externally — for example by
sqlx-switchboard’s PoolConfig — so that serve() skips its own
pool construction. Takes precedence over ServiceBootstrap::with_database if both are
called.
Sourcepub fn with_migrations(self, migrator: Migrator) -> Self
pub fn with_migrations(self, migrator: Migrator) -> Self
Run sqlx migrations at startup.
Sourcepub fn with_rate_limit(self, config: RateLimitBackend) -> Self
pub fn with_rate_limit(self, config: RateLimitBackend) -> Self
Enable the in-process GCRA rate limiter.
The limiter is applied as a tower layer inside serve(). By default it
keys on the remote IP address — see with_rate_limit_extractor to
change the extraction strategy.
Reverse-proxy note: the default RateLimitExtractor::Ip reads the
L4 peer address, which is the proxy IP in production. All clients will
share one rate-limit bucket. Use
with_rate_limit_extractor(RateLimitExtractor::Header("x-forwarded-for"))
when the service runs behind a trusted reverse proxy.
Memory note: the keyed limiter stores one entry per unique key with no
TTL or size cap. Avoid RateLimitExtractor::Header with attacker-controlled
headers in production; prefer Ip or a header with bounded cardinality.
Sourcepub fn with_rate_limit_extractor(self, extractor: RateLimitExtractor) -> Self
pub fn with_rate_limit_extractor(self, extractor: RateLimitExtractor) -> Self
Override the key extractor used by the rate limiter.
Sourcepub fn with_rate_limit_provider<P: RateLimitProvider>(self, provider: P) -> Self
pub fn with_rate_limit_provider<P: RateLimitProvider>(self, provider: P) -> Self
Plug in a RateLimitProvider implementation.
Takes priority over with_rate_limit when both are called. The
provider receives the assembled router and returns it with the
rate-limit tower layer applied.
Use this to inject distributed backends (Postgres, Redis, gossip) from
a wrapper crate without calling with_layer directly:
use axum::Router;
use socle::{ServiceBootstrap, ports::rate_limit::RateLimitProvider};
struct MyDistributedRl;
impl RateLimitProvider for MyDistributedRl {
fn apply(&self, router: Router) -> Router {
router // .layer(my_distributed_rl_layer)
}
}
ServiceBootstrap::new("svc").with_rate_limit_provider(MyDistributedRl);Sourcepub fn with_auth_provider<P: AuthProvider>(self, provider: P) -> Self
pub fn with_auth_provider<P: AuthProvider>(self, provider: P) -> Self
Plug in an AuthProvider implementation.
Groundwork ships no built-in auth backend — the provider is supplied
by the caller (typically a wrapper crate such as service-kit) and
owns its configuration, JWKS cache, API-key validator, etc.
The provider receives the assembled router (already wrapped by the
rate-limit layer when one is configured) and returns it with the
auth tower layer applied. Auth is applied after rate-limit so
unauthenticated requests are still rate-counted, and before any
extra layers registered via with_layer.
use axum::Router;
use socle::{ServiceBootstrap, ports::auth::AuthProvider};
struct MyJwtAuth;
impl AuthProvider for MyJwtAuth {
fn apply(&self, router: Router) -> Router {
router // .layer(my_auth_layer)
}
}
ServiceBootstrap::new("svc").with_auth_provider(MyJwtAuth);Sourcepub fn with_audit_sink(self, sink: Arc<dyn AuditSink>) -> Self
pub fn with_audit_sink(self, sink: Arc<dyn AuditSink>) -> Self
Attach a pluggable audit sink.
When set, AuditLayer is applied after auth and before any
with_layer extensions. Pass any type that implements AuditSink.
Sourcepub fn with_audit_filter(self, filter: AuditFilter) -> Self
pub fn with_audit_filter(self, filter: AuditFilter) -> Self
Override the default AuditFilter.
Has no effect unless with_audit_sink is also called.
Sourcepub fn with_cors_config(self, cfg: CorsConfig) -> Result<Self>
pub fn with_cors_config(self, cfg: CorsConfig) -> Result<Self>
Configure CORS from a structured CorsConfig.
Sourcepub fn with_router<F>(self, f: F) -> Self
pub fn with_router<F>(self, f: F) -> Self
Provide the router builder closure.
Sourcepub fn with_layer<F>(self, f: F) -> Self
pub fn with_layer<F>(self, f: F) -> Self
Inject an arbitrary tower layer into the middleware stack.
Layers are applied in registration order, innermost first (i.e. the
first call to with_layer wraps closest to the user router).
This is the primary extension point for wrapper crates. Example — adding
the distributed-ratelimit layer from service-kit:
bootstrap.with_layer(|router| router.layer(my_distributed_rl_layer))Source§impl ServiceBootstrap
impl ServiceBootstrap
Sourcepub async fn serve(self, addr: impl Into<String>) -> Result<()>
pub async fn serve(self, addr: impl Into<String>) -> Result<()>
Run the service. Initialises every enabled integration in dependency order, binds the listener, serves until SIGINT/SIGTERM, then drains.
Sourcepub async fn serve_with_shutdown(
self,
listener: TcpListener,
shutdown: impl Future<Output = ()> + Send + 'static,
) -> Result<()>
pub async fn serve_with_shutdown( self, listener: TcpListener, shutdown: impl Future<Output = ()> + Send + 'static, ) -> Result<()>
Run the service using a pre-bound listener and a caller-supplied shutdown future. Useful for integration tests where you need to bind on port 0 and control when the server stops.
use axum::{Router, routing::get};
use socle::{BootstrapCtx, ServiceBootstrap};
use tokio::net::TcpListener;
ServiceBootstrap::new("my-service")
.with_router(|_: &BootstrapCtx| Router::new().route("/", get(|| async { "ok" })))
.serve_with_shutdown(listener, std::future::pending())
.await
.unwrap()Auto Trait Implementations§
impl !Freeze for ServiceBootstrap
impl !RefUnwindSafe for ServiceBootstrap
impl Send for ServiceBootstrap
impl !Sync for ServiceBootstrap
impl Unpin for ServiceBootstrap
impl UnsafeUnpin for ServiceBootstrap
impl !UnwindSafe for ServiceBootstrap
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
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> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Paint for Twhere
T: ?Sized,
impl<T> Paint for Twhere
T: ?Sized,
Source§fn fg(&self, value: Color) -> Painted<&T>
fn fg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the foreground set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like red() and
green(), which have the same functionality but are
pithier.
§Example
Set foreground color to white using fg():
use yansi::{Paint, Color};
painted.fg(Color::White);Set foreground color to white using white().
use yansi::Paint;
painted.white();Source§fn bright_black(&self) -> Painted<&T>
fn bright_black(&self) -> Painted<&T>
Source§fn bright_red(&self) -> Painted<&T>
fn bright_red(&self) -> Painted<&T>
Source§fn bright_green(&self) -> Painted<&T>
fn bright_green(&self) -> Painted<&T>
Source§fn bright_yellow(&self) -> Painted<&T>
fn bright_yellow(&self) -> Painted<&T>
Source§fn bright_blue(&self) -> Painted<&T>
fn bright_blue(&self) -> Painted<&T>
Source§fn bright_magenta(&self) -> Painted<&T>
fn bright_magenta(&self) -> Painted<&T>
Source§fn bright_cyan(&self) -> Painted<&T>
fn bright_cyan(&self) -> Painted<&T>
Source§fn bright_white(&self) -> Painted<&T>
fn bright_white(&self) -> Painted<&T>
Source§fn bg(&self, value: Color) -> Painted<&T>
fn bg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the background set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like on_red() and
on_green(), which have the same functionality but
are pithier.
§Example
Set background color to red using fg():
use yansi::{Paint, Color};
painted.bg(Color::Red);Set background color to red using on_red().
use yansi::Paint;
painted.on_red();Source§fn on_primary(&self) -> Painted<&T>
fn on_primary(&self) -> Painted<&T>
Source§fn on_magenta(&self) -> Painted<&T>
fn on_magenta(&self) -> Painted<&T>
Source§fn on_bright_black(&self) -> Painted<&T>
fn on_bright_black(&self) -> Painted<&T>
Source§fn on_bright_red(&self) -> Painted<&T>
fn on_bright_red(&self) -> Painted<&T>
Source§fn on_bright_green(&self) -> Painted<&T>
fn on_bright_green(&self) -> Painted<&T>
Source§fn on_bright_yellow(&self) -> Painted<&T>
fn on_bright_yellow(&self) -> Painted<&T>
Source§fn on_bright_blue(&self) -> Painted<&T>
fn on_bright_blue(&self) -> Painted<&T>
Source§fn on_bright_magenta(&self) -> Painted<&T>
fn on_bright_magenta(&self) -> Painted<&T>
Source§fn on_bright_cyan(&self) -> Painted<&T>
fn on_bright_cyan(&self) -> Painted<&T>
Source§fn on_bright_white(&self) -> Painted<&T>
fn on_bright_white(&self) -> Painted<&T>
Source§fn attr(&self, value: Attribute) -> Painted<&T>
fn attr(&self, value: Attribute) -> Painted<&T>
Enables the styling Attribute value.
This method should be used rarely. Instead, prefer to use
attribute-specific builder methods like bold() and
underline(), which have the same functionality
but are pithier.
§Example
Make text bold using attr():
use yansi::{Paint, Attribute};
painted.attr(Attribute::Bold);Make text bold using using bold().
use yansi::Paint;
painted.bold();Source§fn rapid_blink(&self) -> Painted<&T>
fn rapid_blink(&self) -> Painted<&T>
Source§fn quirk(&self, value: Quirk) -> Painted<&T>
fn quirk(&self, value: Quirk) -> Painted<&T>
Enables the yansi Quirk value.
This method should be used rarely. Instead, prefer to use quirk-specific
builder methods like mask() and
wrap(), which have the same functionality but are
pithier.
§Example
Enable wrapping using .quirk():
use yansi::{Paint, Quirk};
painted.quirk(Quirk::Wrap);Enable wrapping using wrap().
use yansi::Paint;
painted.wrap();Source§fn clear(&self) -> Painted<&T>
👎Deprecated since 1.0.1: renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
fn clear(&self) -> Painted<&T>
renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
Source§fn whenever(&self, value: Condition) -> Painted<&T>
fn whenever(&self, value: Condition) -> Painted<&T>
Conditionally enable styling based on whether the Condition value
applies. Replaces any previous condition.
See the crate level docs for more details.
§Example
Enable styling painted only when both stdout and stderr are TTYs:
use yansi::{Paint, Condition};
painted.red().on_yellow().whenever(Condition::STDOUTERR_ARE_TTY);Source§impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> PolicyExt 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,
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 follow_redirects(self) -> FollowRedirect<Self>where
Self: Sized,
fn follow_redirects(self) -> FollowRedirect<Self>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 moreSource§fn catch_panic(self) -> CatchPanic<Self, DefaultResponseForPanic>where
Self: Sized,
fn catch_panic(self) -> CatchPanic<Self, DefaultResponseForPanic>where
Self: Sized,
500 Internal Server responses. Read moreSource§fn request_body_limit(self, limit: usize) -> RequestBodyLimit<Self>where
Self: Sized,
fn request_body_limit(self, limit: usize) -> RequestBodyLimit<Self>where
Self: Sized,
413 Payload Too Large responses. Read more