6 releases
Uses new Rust 2024
| 0.1.0-alpha.5 | Jul 30, 2025 |
|---|---|
| 0.1.0-alpha.4 | Jul 25, 2025 |
| 0.1.0-alpha.2 | Jul 24, 2025 |
#701 in HTTP server
259 downloads per month
45KB
208 lines
Experimental: Alpha Release
This crate is in early development. APIs are unstable and may change without notice. Not recommended for production use yet.
HTTP Cache Tags: Framework Integrations for Actix and Axum
This crate provides seamless integration of HTTP cache tagging and validation features into popular Rust web frameworks, including Actix-web and Axum.
It builds on the core http_cache_tags_core library to deliver middleware, extractors, API handlers, and runtime components that enable tag-based cache invalidation and metadata resolution.
Features
- Axum Integration (
axum): Middleware, extractors, and runtime for the Axum framework. - Actix Integration (
actix): Middleware, extractors, and runtime for Actix-web framework. - Middleware for injecting cache metadata headers like
ETagandLast-Modified. - Extractors for validated JSON payloads and cache metadata.
- API controllers for cache invalidation and validation routes.
- Support for custom cache stores and seeders.
- Optional features mirroring the core crate (
etag,last_modified,redis, etc.).
By default, the axum integration and last_modified support are enabled.
At least one server integration (axum or actix) and one validation header feature (etag or last_modified) must be activated for proper operation.
| Feature | Description |
|---|---|
axum |
Enable Axum framework middleware and extractors |
actix |
Enable Actix-web middleware and extractors |
config_file |
Load configuration from TOML file |
last_modified |
Support Last-Modified timestamps |
etag |
Generate and handle ETags |
redis |
Redis backend for cache metadata persistence |
Getting Started
1. Define Your Cache Config
The cache config maps URL routes to cache tags, and configures invalidation endpoints.
use http_cache_tags::axum::prelude::*;
let config = CacheConfig::builder()
.invalidation_api_route("/_invalidate")
.invalidation_api_secret("123abc")
.add_route_mapping("/blog/*", vec!["blog"])
.add_route_mapping("/media/*", vec!["media"])
.add_ignore_mapping("/blog/i-am-no-blog", vec!["blog"])
.redis_uri("redis://127.0.0.1/")
.build();
2. Build a Runtime
The CacheRuntime composes the cache config with stores, API controllers, and middleware components.
use http_cache_tags::axum::prelude::*;
let config = CacheConfig::builder().build();
let runtime = CacheRuntime::builder()
.config(config)
// Optionally add a custom seeder to pre-populate cache tags
// .seeder(Box::new(MyCustomSeeder {}))
.build();
Example Setup
Axum Example
use http_cache_tags::axum::prelude::*;
use axum::{Router, routing::get};
use std::{net::SocketAddr, sync::Arc};
#[tokio::main]
async fn main() {
let config = CacheConfig::builder()
.invalidation_api_route("/_invalidate")
.add_route_mapping("/blog/*", vec!["blog"])
.build();
let runtime = CacheRuntime::builder().config(config).build();
let router = Router::new()
.route("/blog/{slug}", get(blog_handler));
let app = runtime.attach_to(router);
let addr = SocketAddr::from(([0, 0, 0, 0], 3000));
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
axum::serve(listener, app).await.unwrap();
}
async fn blog_handler(CacheMeta(meta): CacheMeta) -> String {
format!("Tags: {:?}, Last Modified: {:?}", meta.tags, meta.last_modified)
}
Actix Example
use actix_web::{App, HttpServer, web, HttpResponse};
use http_cache_tags::actix::prelude::*;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let config = CacheConfig::builder()
.invalidation_api_route("/_invalidate")
.add_route_mapping("/blog/*", vec!["blog"])
.build();
let runtime = CacheRuntime::builder().config(config).build();
let server = HttpServer::new(move || {
App::new()
.app_data(web::Data::new(runtime.extractor()))
.wrap(runtime.cache_meta_middleware())
.wrap(runtime.cache_validation_middleware())
.service(runtime.invalidation_scope().unwrap())
.route("/blog/{slug}", web::get().to(blog_handler))
});
server.bind(("0.0.0.0", 3000))?.run().await
}
async fn blog_handler(meta: CacheMeta) -> HttpResponse {
HttpResponse::Ok().body(format!(
"Tags: {:?}, Last Modified: {:?}",
meta.tags, meta.last_modified
))
}
Custom Cache Store Seeder Example
Implement CacheStoreSeeder to prepopulate the cache store with tags or metadata on startup, useful for warming caches or integrating external systems.
use http_cache_tags::axum::prelude::*;
use std::{collections::HashSet, future::Future, pin::Pin};
struct MyCustomSeeder;
impl CacheStoreSeeder for MyCustomSeeder {
fn seed<'a>(
&'a self,
store: &'a (dyn CacheStore),
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
// if `etag` feature is enabled
// store.delete_etag("home");
// store.set_etag("home", "v1");
// if `last_modified` is enabled
// store.delete_last_modified("home");
// store.set_last_modified("home", chrono::Utc::now());
})
}
}
API Usage Examples
This crate supports HTTP cache tagging and invalidation with detailed control over cache metadata such as ETag and Last-Modified headers.
Below are typical HTTP request examples demonstrating how to interact with the cache system:
Conditional GET with If-Modified-Since
GET /blog/test HTTP/1.1
If-Modified-Since: Sun, 20 Jul 2025 23:12:31 +0000
Conditional GET with If-None-Match (ETag)
GET /blog/test HTTP/1.1
If-None-Match: W/"82f493c74aaf8d7557ee619db325d6948f9cfb19"
Invalidate with update of last_modified and etag (with auth header)
POST /_invalidate HTTP/1.1
Content-Type: application/json
Authorization: Bearer my-secret
{
"tags": [
{ "tag": "blog", "last_modified": true, "etag": true }
]
}
Remove all cache metadata for a tag
POST /_invalidate HTTP/1.1
Content-Type: application/json
{
"tags": [
{ "tag": "blog", "last_modified": null, "etag": null }
]
}
Set explicit last_modified timestamp
POST /_invalidate HTTP/1.1
Content-Type: application/json
{
"tags": [
{ "tag": "blog", "last_modified": "2024-01-01T12:00:00Z" }
]
}
Remove last_modified timestamp only
POST /_invalidate HTTP/1.1
Content-Type: application/json
{
"tags": [
{ "tag": "blog", "last_modified": null }
]
}
Trigger ETag regeneration only
POST /_invalidate HTTP/1.1
Content-Type: application/json
{
"tags": [
{ "tag": "blog", "etag": true }
]
}
Remove ETag completely
POST /_invalidate HTTP/1.1
Content-Type: application/json
{
"tags": [
{ "tag": "blog", "etag": null }
]
}
Invalidate multiple tags with different operations
POST /_invalidate HTTP/1.1
Content-Type: application/json
{
"tags": [
{ "tag": "user:42", "etag": true },
{ "tag": "product:99", "last_modified": true },
{ "tag": "stale:tag", "last_modified": null, "etag": null }
]
}
For more examples, see the examples/ folder containing HTTP request files demonstrating common usage patterns and advanced configurations.
Dependencies
~0–16MB
~149K SLoC