Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Quick start

This guide builds a process-local limiter around a plain Tower service. It is the shortest path to the first working layer; the same layer can later be applied to an Axum router.

1. Add the dependency

The default feature enables the in-memory Store:

[dependencies]
tower-rate-limiter = "0.1"

The crate requires Rust 1.96 or newer.

2. Choose a client key

Define how a request becomes an application-owned client key and compose the Layer:

use std::{convert::Infallible, error::Error, future::ready, time::Duration};

use http::{Request, Response};
use tower::{Layer, service_fn};
use tower_rate_limiter::{KeyExtractor, MemoryStore, RateLimitLayer};

#[derive(Clone, Copy)]
struct StaticClient;

impl KeyExtractor for StaticClient {
    type Key = String;

    fn extract<B>(&self, _request: &Request<B>) -> Result<Self::Key, tower_rate_limiter::RateLimitError> {
        Ok(String::from("example-client"))
    }
}

fn main() -> Result<(), Box<dyn Error>> {
    dotenvy::dotenv().ok();

    let limiter = RateLimitLayer::builder(StaticClient)
        .policy_name("tower-example")
        .limit(100)
        .window(Duration::from_secs(60))
        .with_store(MemoryStore::new())
        .build()?;

    let _service = limiter.layer(service_fn(|_request: Request<()>| {
        ready(Ok::<Response<()>, Infallible>(Response::new(())))
    }));

    println!("constructed a Tower MemoryStore rate limiter");
    Ok(())
}

Run the complete example from the repository root:

cargo run --example tower_memory --features memory

The example uses one static key so its construction is easy to see. In a real service, extract a validated account ID, API-client ID, peer address, or another stable identity. Different callers must produce different keys; repeated requests from one caller must produce the same key.

3. Configure the policy deliberately

The minimal production-shaped builder is:

use std::time::Duration;
use tower_rate_limiter::{IpKeyExtractor, MemoryStore, RateLimitLayer};

let layer = RateLimitLayer::builder(IpKeyExtractor::new())
    .policy_name("public-api")
    .limit(100)
    .window(Duration::from_secs(60))
    .with_store(MemoryStore::new())
    .build()?;
Ok::<(), tower_rate_limiter::ConfigError>(())

build() validates configuration. A policy name cannot be empty and a window must be at least one millisecond. The typed builder also prevents build() until a Store has been supplied.

Builder defaults

SettingDefault
Limit1 request
Window60 seconds
Policy namedefault-policy
Store errorsReject with 503 Service Unavailable
Response fieldsIETF draft 11

A real application should set a stable policy name, a quota, and a window deliberately. Layers that share a Store, policy name, and extracted key intentionally share usage.

What happens on each request

The middleware first extracts a key and resolves the quota. Only after both steps succeed does it increment the Store. A provider failure therefore consumes no quota, while a charged request is not refunded based on the downstream response.

The first limit requests are allowed. Request limit + 1 is rate limited, and rejected requests continue to increment usage without extending the active fixed window.

For a limit of 2, the sequence is:

RequestUsage::usedRemainingResult
111inner service called
220inner service called
330429 Too Many Requests

The response from the second request has zero remaining quota but is still allowed. Exhaustion is enforced only when used > limit.

Next steps

  • Read How it works for the public extension points.
  • Browse the complete examples for Tower, Axum, dynamic quotas, proxy handling, and Redis.
  • Review every builder option in Configuration.
  • Use Axum and Redis when the service needs framework or shared-store integration.
  • Browse the repository’s examples/ for dynamic quotas, nested policies, proxy handling, and custom error responses.