{"id":33918464,"url":"https://github.com/rustworthy/tower-redis-cell","last_synced_at":"2026-04-06T07:02:52.554Z","repository":{"id":322838457,"uuid":"1091029151","full_name":"rustworthy/tower-redis-cell","owner":"rustworthy","description":"Rate-limiting Tower service and layer backed by the Redis Cell module","archived":false,"fork":false,"pushed_at":"2026-03-01T21:17:03.000Z","size":217,"stargazers_count":1,"open_issues_count":4,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-11T19:56:10.509Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rustworthy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-06T13:13:41.000Z","updated_at":"2025-12-18T21:34:52.000Z","dependencies_parsed_at":null,"dependency_job_id":"e37f5b46-424e-4e8e-89d9-3620205826a0","html_url":"https://github.com/rustworthy/tower-redis-cell","commit_stats":null,"previous_names":["rustworthy/tower-redis-cell"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rustworthy/tower-redis-cell","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rustworthy%2Ftower-redis-cell","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rustworthy%2Ftower-redis-cell/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rustworthy%2Ftower-redis-cell/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rustworthy%2Ftower-redis-cell/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rustworthy","download_url":"https://codeload.github.com/rustworthy/tower-redis-cell/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rustworthy%2Ftower-redis-cell/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31463015,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-05T21:22:52.476Z","status":"online","status_checked_at":"2026-04-06T02:00:07.287Z","response_time":112,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-12-12T08:23:44.780Z","updated_at":"2026-04-06T07:02:52.549Z","avatar_url":"https://github.com/rustworthy.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tower-redis-cell\n\n[![Crates.io](https://img.shields.io/crates/v/tower-redis-cell.svg)](https://crates.io/crates/tower-redis-cell)\n[![Documentation](https://img.shields.io/docsrs/tower-redis-cell/latest)](https://docs.rs/tower-redis-cell/)\n[![dependency status](https://deps.rs/repo/github/rustworthy/tower-redis-cell/status.svg)](https://deps.rs/repo/github/rustworthy/tower-redis-cell)\n\n## Description\n\nThis crate provides a `Tower` service with rate-limiting functionality\nbacked by `Valkey` or `Redis` deployed with the [Redis Cell](https://github.com/brandur/redis-cell)\nmodule.\n\nThe constucts this crate provides are transport agnostic (just like\n[`Tower`](https://github.com/tower-rs/tower) itself), but here is a basic\nexample using [`axum`](https://github.com/tokio-rs/axum).\n\nFirst, let's define a `Rule` provider: the rule is defined per request, and\ncontains `Key` (e.g. IP, API key, user ID), a `Policy`, and - optionally -\na resource name (useful for tracing, debugging, audit).\n\n```rust\nuse axum::http::Request;\nuse tower_redis_cell::redis_cell::Policy;\nuse tower_redis_cell::{ProvideRule, ProvideRuleResult, Rule};\n\nconst BASIC_POLICY: Policy = Policy::from_tokens_per_second(1);\n\n#[derive(Clone)]\nstruct RuleProvider;\n\nimpl\u003cT\u003e ProvideRule\u003cRequest\u003cT\u003e\u003e for RuleProvider {\n    fn provide\u003c'a\u003e(\u0026self, req: \u0026'a Request\u003cT\u003e) -\u003e ProvideRuleResult\u003c'a\u003e {\n        let key = req\n            .headers()\n            .get(\"x-api-key\")\n            .and_then(|val| val.to_str().ok())\n            .ok_or(\"cannot define key, since 'x-api-key' header is missing\")?;\n        Ok(Some(Rule::new(key, BASIC_POLICY)))\n  }\n}\n```\n\nWe now need to instantiate `RateLimitConfig` (which expects a rule provider\nand an error handler), procure a Valkey/Redis client and use those to create\n`RateLimitLayer`. Note that we are using a `ConnectionManager` in this example,\nbut by default anything [`ConnectionLike`](https://docs.rs/redis/latest/redis/aio/trait.ConnectionLike.html)\nwill do. There is also an option to use a pool, but you will need to enable\na corresponding feature for that (currently, `deadpool` is supported).\n\n```rust\nuse axum::http::{StatusCode, header};\nuse axum::response::{AppendHeaders, IntoResponse, Response};\nuse axum::{Router, body::Body, routing::get};\nuse tower_redis_cell::{Error, RateLimitLayer, RateLimitConfig};\n\n#[tokio::main]\nasync fn main() {\n    tracing_subscriber::fmt()\n        .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())\n        .init();\n\n    let client = redis::Client::open(\"redis://127.0.0.1/\").unwrap();\n    let config = redis::aio::ConnectionManagerConfig::new();\n    let connection = redis::aio::ConnectionManager::new_with_config(client, config)\n        .await\n        .unwrap();\n\n    let config = RateLimitConfig::new(RuleProvider, |err, _req| {\n        match err {\n            Error::ProvideRule(err) =\u003e {\n                tracing::warn!(\n                    key = ?err.key,\n                    detail = err.detail.as_deref(),\n                    \"failed to define rule for request\"\n                );\n                (StatusCode::UNAUTHORIZED, err.to_string()).into_response()\n            }\n            Error::RateLimit(err) =\u003e {\n                tracing::warn!(\n                    key = %err.rule.key,\n                    policy = err.rule.policy.name,\n                    \"request throttled\"\n                );\n                (\n                    StatusCode::TOO_MANY_REQUESTS,\n                    AppendHeaders([(header::RETRY_AFTER, err.details.retry_after)]),\n                    Body::from(\"too many requests\"),\n                )\n                    .into_response()\n            }\n            err =\u003e {\n                tracing::error!(err = %err, \"unexpected error\");\n                (StatusCode::INTERNAL_SERVER_ERROR).into_response()\n            }\n        }\n    });\n\n    let app = Router::new()\n        .route(\"/\", get(|| async { \"Hello, World!\" }))\n        .layer(RateLimitLayer::new(config, connection));\n\n    let listener = tokio::net::TcpListener::bind(\"0.0.0.0:3000\").await.unwrap();\n    axum::serve(listener, app).await.unwrap();\n}\n```\n\nNote that we are in-lining the error handler above, but this can be a free\nstanding function. Also, you can optionally provide `RateLimitConfig::on_success`\nand `RateLimitConfig::on_unruled` handlers, which both provide a mutable access\nto the response, and so - if needed - you can set any additional headers.\n\n## Examples\n\nExamples are located [here](./tower-redis-cell/examples). To run an `axum` example,\nmake sure you got Rust and [docker](https://docs.docker.com/engine/install/) installed:\nwe are using test containers to launch a `Valkey` server with `Redis Cell` module\nloaded.\n\nTo run the `axum` example, hit:\n\n```console\ncargo run --example axum\n```\n\nTo run the same example with a `Redis` connection pool:\n\n```console\ncargo run --example axum --features deadpool\n```\n\n## Development \u0026 Contributing\n\nPlease find utility commands in [`Makefile`](./Makefile).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frustworthy%2Ftower-redis-cell","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frustworthy%2Ftower-redis-cell","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frustworthy%2Ftower-redis-cell/lists"}