How does tower::Layer::layer wrap services differently from ServiceBuilder::into_inner for middleware ordering?
Layer::layer wraps services by applying middleware from inside-out (the innermost service is wrapped first), while ServiceBuilder::into_inner applies middleware in the order they're added (outermost middleware processes requests first)āmaking the ordering reversed between the two approaches and critical to understand for correct middleware composition. The difference lies in when the wrapping occurs: Layer::layer is an imperative operation that immediately wraps a service, while ServiceBuilder is a declarative chain that builds the complete middleware stack only when into_inner or into_service is called.
Basic Layer::layer Usage
use tower::{Service, ServiceBuilder};
use tower::layer::layer_fn;
use std::task::{Context, Poll};
use pin_project_lite::pin_project;
use std::future::Future;
use std::pin::Pin;
// A simple middleware that logs requests
struct LoggingLayer;
impl<S> tower::Layer<S> for LoggingLayer {
type Service = LoggingService<S>;
fn layer(&self, inner: S) -> Self::Service {
LoggingService { inner }
}
}
struct LoggingService<S> {
inner: S,
}
impl<S, Request> Service<Request> for LoggingService<S>
where
S: Service<Request>,
{
type Response = S::Response;
type Error = S::Error;
type Future = S::Future;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}
fn call(&mut self, req: Request) -> Self::Future {
println!("Logging middleware: processing request");
self.inner.call(req)
}
}
// Using Layer::layer directly
fn basic_layer_usage() {
// Start with innermost service
let inner = MyService;
// Apply layers from inside to outside
let service = LoggingLayer.layer(inner);
// Result: LoggingService(MyService)
}Layer::layer immediately wraps the provided service.
Basic ServiceBuilder Usage
use tower::ServiceBuilder;
fn basic_service_builder() {
// ServiceBuilder collects layers and applies them later
let service = ServiceBuilder::new()
.layer(LoggingLayer)
.service(MyService);
// This is equivalent to:
// LoggingLayer.layer(MyService)
}ServiceBuilder collects layers and applies them when service is called.
The Critical Ordering Difference
use tower::ServiceBuilder;
// Ordering demonstration
fn ordering_difference() {
// Using Layer::layer directly (imperative):
// Wrap from inside-out
let service = AuthLayer.layer(
RateLimitLayer.layer(
TimeoutLayer.layer(
MyService
)
)
);
// Order: MyService -> TimeoutLayer -> RateLimitLayer -> AuthLayer
// Request flow: AuthLayer -> RateLimitLayer -> TimeoutLayer -> MyService
// Response flow: MyService -> TimeoutLayer -> RateLimitLayer -> AuthLayer
// Using ServiceBuilder (declarative):
let service = ServiceBuilder::new()
.layer(AuthLayer)
.layer(RateLimitLayer)
.layer(TimeoutLayer)
.service(MyService);
// SAME ORDER! Request flow: AuthLayer -> RateLimitLayer -> TimeoutLayer -> MyService
// ServiceBuilder reverses the layer application internally
}Both approaches result in the same ordering when written correctly.
Understanding the Reversal
use tower::ServiceBuilder;
fn understanding_reversal() {
// When you write ServiceBuilder::new()
// .layer(A)
// .layer(B)
// .layer(C)
// .service(inner)
//
// Internally, it builds:
// A.layer(B.layer(C.layer(inner)))
//
// So request flow is: A -> B -> C -> inner
// And layers are applied "inside-out" but written "outside-in"
// The key insight: layers are stored in order, then applied in reverse
// If you were to use Layer::layer directly:
let service = A.layer(
B.layer(
C.layer(inner)
)
);
// Same result, but the nesting shows the actual application order
}ServiceBuilder stores layers in order and applies them from the innermost outward.
Manual Layer Composition
use tower::Layer;
fn manual_composition() {
// Manual composition shows the nesting clearly
struct ServiceA;
struct ServiceB;
struct ServiceC;
// If we want order: A -> B -> C -> Inner
// We need to nest: A(B(C(Inner)))
// Start with innermost
let with_c = LayerC.layer(InnerService);
let with_b = LayerB.layer(with_c);
let with_a = LayerA.layer(with_b);
// Result: A wraps B wraps C wraps Inner
// Request flow: A (outermost) -> B -> C -> Inner (innermost)
}Manual composition requires nesting from inside to outside.
ServiceBuilder as Collection
use tower::ServiceBuilder;
fn service_builder_collection() {
// ServiceBuilder collects layers
let builder = ServiceBuilder::new()
.layer(LayerA)
.layer(LayerB)
.layer(LayerC);
// Layers are NOT applied yet
// They're stored in a Vec-like structure internally
// .service(inner) applies all layers to inner
let service = builder.service(InnerService);
// Equivalent to:
// LayerA.layer(LayerB.layer(LayerC.layer(InnerService)))
// The builder "knows" to apply layers in reverse storage order
}ServiceBuilder defers layer application until service is called.
into_inner vs into_service
use tower::ServiceBuilder;
fn into_inner_vs_service() {
// ServiceBuilder has two main methods:
// 1. into_service(inner) - creates the full service
let service = ServiceBuilder::new()
.layer(LayerA)
.layer(LayerB)
.service(InnerService);
// 2. into_inner() - returns the composed layer, NOT a service
let layer = ServiceBuilder::new()
.layer(LayerA)
.layer(LayerB)
.into_inner();
// into_inner() returns a Layer, not a Service
// You then apply it to a service:
let service = layer.layer(InnerService);
// This is useful when you want to reuse a layer composition
}into_inner returns a composed Layer; service returns a Service.
Reusing Layer Compositions
use tower::ServiceBuilder;
fn reusing_layers() {
// Create a reusable layer composition
let common_layers = ServiceBuilder::new()
.layer(TimeoutLayer::new(Duration::from_secs(30)))
.layer(RateLimitLayer::new(100, Duration::from_secs(1)))
.layer(LoggingLayer)
.into_inner();
// This is a Layer, not a Service
// Apply to multiple services
let user_service = common_layers.layer(UserService);
let admin_service = common_layers.layer(AdminService);
let public_service = common_layers.layer(PublicService);
// All three services have the same middleware stack
// Request flow for all: Logging -> RateLimit -> Timeout -> InnerService
}into_inner enables reuse of layer compositions across multiple services.
Layer Ordering Visualization
use tower::ServiceBuilder;
// Request flow visualization
fn visualize_ordering() {
// Given layers A, B, C applied in order:
let service = ServiceBuilder::new()
.layer(LayerA) // Applied third (outermost)
.layer(LayerB) // Applied second
.layer(LayerC) // Applied first (innermost)
.service(InnerService);
// Request processing order:
// 1. LayerA::call (first to see request)
// 2. LayerB::call
// 3. LayerC::call
// 4. InnerService::call
// Response processing order (reverse):
// 4. InnerService produces response
// 3. LayerC processes response
// 2. LayerB processes response
// 1. LayerA processes response (last to see response)
// This is the "onion" model - layers wrap around the inner service
}Layers form an onion around the inner service.
Debugging Layer Order
use tower::ServiceBuilder;
// Helper to visualize layer application
fn debug_layer_order() {
struct DebugLayer(&'static str);
impl<S> Layer<S> for DebugLayer {
type Service = DebugService<S>;
fn layer(&self, inner: S) -> Self::Service {
println!("Wrapping with {}", self.0);
DebugService { name: self.0, inner }
}
}
// Using Layer::layer directly:
println!("Direct layer application:");
let _ = DebugLayer("A").layer(
DebugLayer("B").layer(
DebugLayer("C").layer(InnerService)
)
);
// Output:
// Wrapping with C
// Wrapping with B
// Wrapping with A
// Using ServiceBuilder:
println!("ServiceBuilder:");
let _ = ServiceBuilder::new()
.layer(DebugLayer("A"))
.layer(DebugLayer("B"))
.layer(DebugLayer("C"))
.service(InnerService);
// Output:
// Wrapping with C
// Wrapping with B
// Wrapping with A
// SAME ORDER!
}Both approaches apply layers in the same order internally.
Common Ordering Mistakes
use tower::ServiceBuilder;
fn common_mistakes() {
// MISTAKE 1: Thinking layers are applied in written order
// Written order: A, B, C
// Application order: C first, then B, then A
// Request flow: A -> B -> C -> Inner
// MISTAKE 2: Confusing layer order with middleware execution order
// Layer A is outermost, executes first on request
// Layer C is innermost, executes last before inner service
// MISTAKE 3: Using into_inner when you need a service
let layer = ServiceBuilder::new()
.layer(A)
.into_inner(); // Returns Layer, not Service
// You can't call this layer directly - it needs a service
// Correct: let service = layer.layer(InnerService);
// MISTAKE 4: Using service when you need a layer
let service = ServiceBuilder::new()
.layer(A)
.service(InnerService); // Returns Service
// You can't apply this to another service
// If you need to reuse, use into_inner first
}Understanding the distinction prevents common errors.
Practical Example: API Middleware Stack
use tower::ServiceBuilder;
use std::time::Duration;
fn api_middleware_stack() {
// Typical API middleware stack
let service = ServiceBuilder::new()
// Outermost: Global error handling
.layer(ErrorHandlingLayer)
// Request ID for tracing
.layer(RequestIdLayer)
// Auth check
.layer(AuthLayer::new())
// Rate limiting
.layer(RateLimitLayer::per_second(100))
// Request timeout
.layer(TimeoutLayer::new(Duration::from_secs(30)))
// Innermost: Logging
.layer(LoggingLayer)
// The actual service
.service(ApiHandler);
// Request flow:
// 1. ErrorHandlingLayer catches panics
// 2. RequestIdLayer assigns ID
// 3. AuthLayer validates credentials
// 4. RateLimitLayer checks quota
// 5. TimeoutLayer enforces deadline
// 6. LoggingLayer logs request
// 7. ApiHandler processes
// Response flow is reversed
}ServiceBuilder makes complex middleware stacks readable.
Creating Reusable Middleware Presets
use tower::ServiceBuilder;
// Reusable middleware configuration
fn production_middleware() -> impl Layer<MyService> {
ServiceBuilder::new()
.layer(TimeoutLayer::new(Duration::from_secs(30)))
.layer(RateLimitLayer::new(1000, Duration::from_secs(1)))
.layer(ConcurrencyLimitLayer::new(100))
.layer(LoggingLayer)
.into_inner() // Return the composed layer
}
fn development_middleware() -> impl Layer<MyService> {
ServiceBuilder::new()
.layer(LoggingLayer) // Minimal for development
.into_inner()
}
fn use_presets() {
let prod_layer = production_middleware();
let dev_layer = development_middleware();
// Apply to different services
let prod_service = prod_layer.layer(ProductionApiService);
let dev_service = dev_layer.layer(DevelopmentApiService);
}into_inner enables reusable middleware presets.
Conditional Layer Application
use tower::ServiceBuilder;
fn conditional_layers(enable_auth: bool, enable_rate_limit: bool) {
// Using ServiceBuilder conditionally
let mut builder = ServiceBuilder::new()
.layer(TimeoutLayer::new(Duration::from_secs(30)));
if enable_auth {
builder = builder.layer(AuthLayer::new());
}
if enable_rate_limit {
builder = builder.layer(RateLimitLayer::per_second(100));
}
builder = builder.layer(LoggingLayer);
let service = builder.service(InnerService);
// Alternative: Using into_inner and conditional layer
let base_layers = ServiceBuilder::new()
.layer(TimeoutLayer::new(Duration::from_secs(30)))
.into_inner();
let auth_layers = if enable_auth {
ServiceBuilder::new()
.layer(AuthLayer::new())
.layer(base_layers)
.into_inner()
} else {
base_layers
};
let service = auth_layers.layer(InnerService);
}Both approaches support conditional middleware.
Layer Transformers
use tower::ServiceBuilder;
// ServiceBuilder can transform existing services
fn transform_existing_service() {
let existing_service = MyService;
// Add middleware to existing service
let enhanced = ServiceBuilder::new()
.layer(LoggingLayer)
.layer(MetricsLayer)
.service(existing_service);
// Or use into_inner and apply to multiple existing services
let middleware = ServiceBuilder::new()
.layer(LoggingLayer)
.layer(MetricsLayer)
.into_inner();
let service1 = middleware.layer(Service1);
let service2 = middleware.layer(Service2);
let service3 = middleware.layer(Service3);
}Transform existing services or create reusable middleware.
Ordering Summary Table
use tower::{Layer, ServiceBuilder};
fn summary() {
// | Approach | Written Order | Application Order | Request Flow |
// |-------------------|---------------|-------------------|-------------------|
// | Layer::layer | A.layer( | C first, then B, | A -> B -> C -> |
// | | B.layer( | then A | Inner |
// | | C.layer( | | |
// | | Inner)))| | |
// | ServiceBuilder | .layer(A) | C first, then B, | A -> B -> C -> |
// | | .layer(B) | then A | Inner |
// | | .layer(C) | | |
// | | .service(...) | | |
// Both produce identical ordering!
// The key is understanding that written order = outermost first
}Both approaches produce identical middleware ordering.
Synthesis
Quick reference:
use tower::ServiceBuilder;
// Layer::layer: Direct, imperative wrapping
let service = LayerA.layer(
LayerB.layer(
LayerC.layer(InnerService)
)
);
// Explicit nesting shows order clearly
// ServiceBuilder: Declarative, cleaner syntax
let service = ServiceBuilder::new()
.layer(LayerA)
.layer(LayerB)
.layer(LayerC)
.service(InnerService);
// Same order, more readable
// into_inner: Reusable layer composition
let layer = ServiceBuilder::new()
.layer(LayerA)
.layer(LayerB)
.into_inner(); // Returns Layer, not Service
let service1 = layer.layer(Service1);
let service2 = layer.layer(Service2);When to use each:
use tower::{Layer, ServiceBuilder};
// Use Layer::layer when:
// - Simple, single-service wrapping
// - Need explicit control over wrapping order
// - Working with a single layer
// Use ServiceBuilder when:
// - Multiple layers to apply
// - Need readability and maintainability
// - Want to compose layers declaratively
// Use into_inner when:
// - Reusing layer composition across services
// - Creating configurable middleware presets
// - Building layers dynamicallyKey insight: Layer::layer and ServiceBuilder achieve identical middleware orderingāthey just present different interfaces for the same underlying composition. Layer::layer is the primitive: given an inner service, it returns a wrapped service. When you nest multiple layer calls, you're building the middleware stack from inside-out (innermost service first, then wrapped by successive layers). ServiceBuilder provides a cleaner declarative API: you write layers in the order requests flow through them (outermost first), and internally it applies them correctly. The critical realization is that the "last layer written" becomes the "innermost layer" in the final stackāthe first layer you see in a ServiceBuilder chain is the first layer a request encounters. into_inner returns the composed Layer itself rather than a Service, enabling reuse of the middleware configuration across multiple services. This separation of "layer composition" from "service binding" is powerful for creating shared middleware presets that can be applied consistently across an application.
