{"id":50844564,"url":"https://github.com/godaddy/rusty-gasket","last_synced_at":"2026-06-14T08:33:48.869Z","repository":{"id":362120203,"uuid":"1257391339","full_name":"godaddy/rusty-gasket","owner":"godaddy","description":"A plugin-based Rust framework for backend HTTP services - lifecycle-driven plugins, a pluggable middleware pipeline, and route groups — designed to keep generated API code readable for engineers who aren't Rust specialists.","archived":false,"fork":false,"pushed_at":"2026-06-09T08:05:46.000Z","size":848,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-14T08:33:45.609Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/godaddy.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","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":"2026-06-02T16:26:25.000Z","updated_at":"2026-06-09T08:04:17.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/godaddy/rusty-gasket","commit_stats":null,"previous_names":["godaddy/rusty-gasket"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/godaddy/rusty-gasket","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/godaddy%2Frusty-gasket","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/godaddy%2Frusty-gasket/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/godaddy%2Frusty-gasket/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/godaddy%2Frusty-gasket/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/godaddy","download_url":"https://codeload.github.com/godaddy/rusty-gasket/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/godaddy%2Frusty-gasket/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34315072,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-14T02:00:07.365Z","response_time":62,"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":"2026-06-14T08:33:48.708Z","updated_at":"2026-06-14T08:33:48.862Z","avatar_url":"https://github.com/godaddy.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"Rusty Gasket\" src=\"/docs/images/rusty-gasket.svg\" width=\"496\" /\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\nFramework for Rust API Services\n\u003c/p\u003e\n\n---\n\nA plugin-based Rust framework for backend HTTP services. The architecture — lifecycle phases, plugin composition, and route groups with stacked middleware — is borrowed from [GoDaddy's Gasket](https://github.com/godaddy/gasket), which targets full-stack JavaScript apps and pairs a Node backend with a React frontend. Rusty Gasket adapts the same lifecycle-driven plugin model for Rust backends only; the frontend half of the analogy is out of scope.\n\nBuild production HTTP APIs with a lifecycle-driven plugin system, pluggable middleware pipeline, and clean separation between open-source core and organization-specific overlays.\n\nRusty Gasket is designed for teams that want Rust's deployment, runtime, and safety benefits without requiring every API owner to become a Rust specialist first. The primary use case is agentic code generation: a software engineer describes or evolves an API, an agent generates the Rust implementation, and the engineer still needs to read, review, debug, and maintain that code in production.\n\nThat means generated application code must be approachable to engineers who are experienced in backend systems but new to Rust. The framework uses modern Rust internally, but keeps boxing, object-safety adapters, lifetime-heavy signatures, and ownership-heavy syntax behind named framework types wherever practical.\n\nReadability is part of the framework contract. Public APIs, framework handles,\nand non-obvious internal adapters should be documented in domain language so\ndevelopers can understand the code without first decoding Rust's lower-level\ntype machinery.\n\n## Design Priorities\n\n1. **Readable generated APIs** -- route handlers, plugins, auth backends, and configuration should look like ordinary service code.\n2. **Modern Rust under the hood** -- advanced Rust is allowed inside the framework when it buys safety, correctness, or performance, but it should be wrapped in named concepts.\n3. **Production ownership by non-Rust specialists** -- comments, rustdoc, examples, and type names should help backend engineers understand generated code well enough to operate it.\n4. **Expert-defensible tradeoffs** -- novice ergonomics matter most, but not by committing to obsolete patterns, unsound shortcuts, or dead-end abstractions.\n\n## Quick Start\n\n```rust\nuse axum::{Json, Router, routing::get};\nuse rusty_gasket::prelude::*;\nuse serde::{Deserialize, Serialize};\n\n// A plugin is the unit of API composition.\n// It can contribute routes, middleware, config changes, health checks,\n// and startup/shutdown work.\n#[derive(Debug)]\nstruct MathPlugin;\n\n// Request inputs are ordinary typed structs.\n// Serde fills this from query parameters: /v1/add?a=2\u0026b=3\n#[derive(Deserialize)]\nstruct AddQuery {\n    a: f64,\n    b: f64,\n}\n\n// Response bodies are ordinary typed structs too.\n// Serde turns this into JSON for the HTTP response.\n#[derive(Serialize)]\nstruct AddResponse {\n    a: f64,\n    b: f64,\n    sum: f64,\n}\n\n// A second request type for a string-processing endpoint.\n// Example: /v1/upper?text=hello\n#[derive(Deserialize)]\nstruct UpperQuery {\n    text: String,\n}\n\n#[derive(Serialize)]\nstruct UpperResponse {\n    original: String,\n    upper: String,\n}\n\n// Public endpoints can return small status payloads without requiring auth.\n#[derive(Serialize)]\nstruct StatusResponse {\n    service: \u0026'static str,\n    status: \u0026'static str,\n}\n\nimpl Plugin for MathPlugin {\n    // Stable plugin names are used in logs, startup ordering, and diagnostics.\n    fn name(\u0026self) -\u003e \u0026'static str {\n        \"math\"\n    }\n\n    // Register this plugin's HTTP routes.\n    //\n    // Routes are grouped by the middleware they should receive.\n    //\n    // RouteGroup::Public is for unauthenticated endpoints such as status,\n    // documentation, or public metadata. It still receives request logging\n    // and body limits.\n    //\n    // RouteGroup::Protected is for normal API functionality. It receives\n    // the full production middleware stack: request logging, body limits,\n    // auth, rate limiting, transactions, and any application middleware.\n    fn routes(\u0026self, _ctx: \u0026RouteContext) -\u003e Vec\u003cTaggedRoute\u003e {\n        let public_routes = Router::new().route(\"/status\", get(status));\n\n        let protected_routes = Router::new()\n            .route(\"/v1/add\", get(add_numbers))\n            .route(\"/v1/upper\", get(to_uppercase));\n\n        vec![\n            TaggedRoute::new(RouteGroup::Public, public_routes),\n            TaggedRoute::new(RouteGroup::Protected, protected_routes),\n        ]\n    }\n}\n\n// Handlers are just async functions.\n// No boxed futures, no lifetime annotations, no framework-specific ceremony.\nasync fn add_numbers(QueryParams(query): QueryParams\u003cAddQuery\u003e) -\u003e Json\u003cAddResponse\u003e {\n    Json(AddResponse {\n        a: query.a,\n        b: query.b,\n        sum: query.a + query.b,\n    })\n}\n\nasync fn to_uppercase(QueryParams(query): QueryParams\u003cUpperQuery\u003e) -\u003e Json\u003cUpperResponse\u003e {\n    Json(UpperResponse {\n        upper: query.text.to_uppercase(),\n        original: query.text,\n    })\n}\n\nasync fn status() -\u003e Json\u003cStatusResponse\u003e {\n    Json(StatusResponse {\n        service: \"my-api\",\n        status: \"ok\",\n    })\n}\n\n#[tokio::main]\nasync fn main() -\u003e Result\u003c(), BoxError\u003e {\n    // Initialize structured logging (pretty locally, JSON in production).\n    rusty_gasket::observability::init_tracing(Environment::Local);\n\n    // Load gasket.toml if it exists; fall back to in-code defaults if\n    // the file is missing. Parse errors are propagated.\n    let config = AppConfigDefinition::from_file_optional(\"gasket.toml\")?\n        .unwrap_or_else(|| AppConfigDefinition::new(\"my-api\"));\n\n    // Build the app through the plugin lifecycle:\n    // init -\u003e configure -\u003e prepare. The server will call ready after binding.\n    let app = GasketApp::builder()\n        // Bundles the framework's health and server plugins.\n        .preset(rusty_gasket::presets::api())\n        // Production API middleware with readable plugin names.\n        .plugin(CorsPlugin::default())\n        .plugin(CompressionPlugin)\n        .plugin(SecureHeadersPlugin)\n        .plugin(TimeoutPlugin::from_secs(30))\n        // Adds this API's routes.\n        .plugin(MathPlugin)\n        .config(config)\n        .build()\n        .await?;\n\n    // Start the server. Blocks until SIGTERM or Ctrl+C, then shuts down gracefully.\n    ServerPlugin::run(std::sync::Arc::new(app)).await\n}\n```\n\n```bash\ncargo run\ncurl \"http://localhost:8443/status\"                 # unauthenticated\ncurl \"http://localhost:8443/v1/add?a=2\u0026b=3\"         # protected\ncurl \"http://localhost:8443/v1/upper?text=hello\"    # protected\ncurl \"http://localhost:8443/healthcheck\"            # built-in health endpoint\n```\n\n\u003e **Swagger UI**: The `openapi` feature is enabled by default for API projects.\n\u003e Add `OpenApiPlugin` to serve interactive API docs at `/swagger-ui/`. Minimal\n\u003e consumers can opt out with `default-features = false`.\n\n## Crates\n\n| Crate | Description |\n|-------|-------------|\n| `rusty-gasket` | The framework: plugins, config, error handling, server, health, observability, rate limiting, OpenAPI, and caching — plus optional batteries (auth, AWS, SQL via `db`, DynamoDB, testing) behind feature flags. |\n| `rusty-gasket-macros` | `#[derive(ApiError)]` proc macro for ergonomic error types. |\n\nApplication code depends only on `rusty-gasket`; optional functionality is turned on with feature flags (see below).\n\n## Documentation\n\n| Guide | Description |\n|-------|-------------|\n| [Getting Started](docs/getting-started.md) | Installation, first project, first route |\n| [API Ergonomics](docs/api-ergonomics.md) | Novice-readable handlers, policy guards, context, generators, OpenAPI |\n| [Architecture](docs/architecture.md) | Plugin lifecycle, middleware pipeline, route groups |\n| [Plugin Guide](docs/plugin-guide.md) | Writing plugins: ordering, routes, layers, health |\n| [Authentication](docs/authentication.md) | JWT, API keys, auth chain, extractors, policies |\n| [Database](docs/database.md) | SQLx, transactions, request ID correlation |\n| [Caching](docs/caching.md) | ObjectCache, response caching, Redis, Memcached |\n| [Configuration](docs/configuration.md) | Config files, env vars, secrets, environments |\n| [Error Handling](docs/error-handling.md) | ApiError trait, derive macro, structured errors |\n| [Testing](docs/testing.md) | TestApp, mocks, containers, benchmarks |\n| [Observability](docs/observability.md) | Tracing, request IDs, OpenTelemetry |\n| [Middleware](docs/middleware.md) | Pipeline slots, custom middleware, route groups |\n| [Changelog](CHANGELOG.md) | Release history |\n| [Contributing](CONTRIBUTING.md) | Development setup, testing, PR process |\n\n## Feature Flags\n\n### `rusty-gasket`\n\n| Feature | Default | Description |\n|---------|---------|-------------|\n| `json-log` | Yes | JSON structured logging |\n| `health` | Yes | Health check endpoints |\n| `rate-limit` | Yes | Governor rate limiting |\n| `openapi` | Yes | utoipa + Swagger UI at `/swagger-ui/`; disable with `default-features = false` |\n| `cache` | Yes | ObjectCache, response caching, and in-process Moka backend |\n| `cache-redis` | No | Redis/Valkey object-cache backend |\n| `cache-memcached` | No | Memcached object-cache backend |\n| `otlp` | No | OpenTelemetry OTLP span + metric export |\n| `auth` | No | JWT auth backends, auth chain, middleware, and policy extractors |\n| `auth-api-key` | No | API-key auth backend in addition to JWT auth |\n| `aws` | No | AWS integration helpers (Secrets Manager) |\n| `s3` | No | `S3ObjectStore`: get/put/head/list/presign + a streaming `download_response` helper |\n| `db` | No | Default SQL database integration (`db-postgres`) |\n| `db-postgres` | No | PostgreSQL SQLx integration, transaction middleware, `DbTx` extractor |\n| `db-mysql` | No | MySQL SQLx integration, transaction middleware, `DbTx` extractor |\n| `dynamodb` | No | DynamoDB lifecycle plugin and extractor |\n| `templates` | No | minijinja HTML templating: render to a `String` or an axum HTML response, autoescaped |\n| `testing` | No | `TestApp`, `TestResponse`, and in-process auth test helpers |\n\n## Development\n\n```bash\ncargo fmt --all --check\ncargo clippy --workspace --all-targets --all-features -- -D warnings\ncargo test --workspace --all-targets --all-features\ncargo bench -p bench-api          # criterion benchmarks\n```\n\n### Creating a New Project\n\n```bash\ncargo generate --git https://github.com/godaddy/rusty-gasket --path templates/oss\n```\n\n## License\n\nLicensed under the Apache License, Version 2.0. See [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgodaddy%2Frusty-gasket","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgodaddy%2Frusty-gasket","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgodaddy%2Frusty-gasket/lists"}