#dependency-injection #routing #gateway

cardinal-base

Dependency injection and routing primitives for the Cardinal gateway

25 releases

new 0.2.41 Nov 28, 2025
0.2.40 Nov 27, 2025
0.2.39 Oct 27, 2025
0.2.23 Sep 30, 2025
0.1.0 Sep 26, 2025

#1367 in Development tools

Download history 631/week @ 2025-09-26 738/week @ 2025-10-03 806/week @ 2025-10-10 296/week @ 2025-10-17 585/week @ 2025-10-24 5/week @ 2025-10-31

595 downloads per month
Used in 4 crates (3 directly)

Apache-2.0

92KB
2K SLoC

cardinal-base

cardinal-base underpins the gateway’s dependency graph. It hosts the DI container, routing utilities, and destination resolver used everywhere else.

Components

  • CardinalContext – owns the active CardinalConfig, tracks provider registrations, and constructs providers on demand. Scope-aware (Singleton vs Transient) with cycle detection to keep async factories honest.
  • Provider traits – implement Provider for any type you want to resolve later. Register with register, register_with_factory, or register_singleton_instance.
  • CardinalRouter – small wrapper around matchit::Router; used by destinations to match HTTP method + path and extract path parameters.
  • DestinationContainer – builds DestinationWrappers from config, supplies per-destination middleware lists, and picks a backend by path segment or subdomain.

How it fits

At runtime the flow looks like this:

CardinalBuilder
   └─ registers providers in CardinalContext
        ├─ DestinationContainer (maps host/path → backend + middleware)
        ├─ PluginContainer (middleware registry)
        └─ any user-provided services

When cardinal-proxy handles a request it grabs the DestinationContainer and (if routes were declared) queries the CardinalRouter to validate the path and extract parameters. Middleware lists are pulled from each DestinationWrapper and fed to the plugin runner.

Adding new providers

struct Metrics;

#[async_trait::async_trait]
impl Provider for Metrics {
    async fn provide(ctx: &CardinalContext) -> Result<Self, CardinalError> {
        // optional: use ctx.config to decide setup
        Ok(Metrics)
    }
}

context.register::<Metrics>(ProviderScope::Singleton);

Later, middleware or infrastructure code can ask for context.get::<Metrics>().await? and work with the shared instance.

Dependencies

~34–51MB
~1M SLoC