{"id":13580807,"url":"https://github.com/thruster-rs/Thruster","last_synced_at":"2025-04-06T02:32:50.917Z","repository":{"id":37390156,"uuid":"108700097","full_name":"thruster-rs/Thruster","owner":"thruster-rs","description":"A fast, middleware based, web framework written in Rust","archived":false,"fork":false,"pushed_at":"2024-10-16T20:35:18.000Z","size":875,"stargazers_count":1071,"open_issues_count":2,"forks_count":46,"subscribers_count":20,"default_branch":"master","last_synced_at":"2025-03-30T11:04:47.816Z","etag":null,"topics":["hacktoberfest","rust","thruster","thruster-rs","web","web-development","web-framework"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thruster-rs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":"ROADMAP_2023.md","authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-10-29T02:49:28.000Z","updated_at":"2025-03-26T08:38:30.000Z","dependencies_parsed_at":"2024-01-07T21:04:54.042Z","dependency_job_id":"3e1336c1-4412-406b-ab22-b038c79c0047","html_url":"https://github.com/thruster-rs/Thruster","commit_stats":{"total_commits":233,"total_committers":20,"mean_commits":11.65,"dds":"0.11158798283261806","last_synced_commit":"e87cb35cb7f14ce2cb4857d2b6255e6a98393671"},"previous_names":["trezm/thruster","trezm/fanta"],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thruster-rs%2FThruster","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thruster-rs%2FThruster/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thruster-rs%2FThruster/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thruster-rs%2FThruster/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thruster-rs","download_url":"https://codeload.github.com/thruster-rs/Thruster/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246752628,"owners_count":20827987,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["hacktoberfest","rust","thruster","thruster-rs","web","web-development","web-framework"],"created_at":"2024-08-01T15:01:55.240Z","updated_at":"2025-04-06T02:32:50.502Z","avatar_url":"https://github.com/thruster-rs.png","language":"Rust","funding_links":[],"categories":["Rust"],"sub_categories":[],"readme":"# Thruster [![Latest Version]][crates.io] [![Downloads]][crates.io] [![Online]][discord]\n\n[Latest Version]: https://img.shields.io/crates/v/thruster.svg\n[Downloads]: https://img.shields.io/crates/d/thruster.svg\n[Online]: https://img.shields.io/discord/658730946211610643\n[crates.io]: https://crates.io/crates/thruster\n[discord]: https://discord.gg/m9JrPRds\n\n[Get started with examples and walkthroughs on our website!](https://mertz.gitbook.io/thruster/)\n\n## A fast and intuitive rust web framework\n\nDon't have time to read the docs? Check out\n\n- [Why you should use Thruster](#why-you-should-use-thruster)\n- [Why you shouldn't use Thruster (definitely read this one)](#why-you-shouldnt-use-thruster)\n\n✅ Runs in stable\n✅ Runs fast\n✅ Doesn't use unsafe\n\n[Documentation](https://docs.rs/thruster)\n\n## Features\n\n- [built with async/await in mind](https://github.com/thruster-rs/Thruster/blob/master/thruster/examples/profiling.rs#L11)\n- [hyper compatible](https://github.com/thruster-rs/Thruster/blob/master/thruster/examples/hyper_most_basic.rs)\n- [ssl ready](https://github.com/thruster-rs/Thruster/tree/master/thruster/examples/hyper_most_basic_ssl)\n- [testable](#testing)\n- [static file serving](https://github.com/thruster-rs/Thruster/tree/master/thruster/examples/static_file)\n- [socketio](https://github.com/thruster-rs/thruster-socketio)\n- [gRPC](https://github.com/thruster-rs/Thruster/tree/master/thruster/examples/grpc), and more experimental [non-tonic based gRPC](https://github.com/thruster-rs/thruster-grpc)\n- [dependency injection](https://github.com/thruster-rs/thruster-jab)\n\n## Motivation\n\nThruster is a web framework that aims for developers to be productive and consistent across projects and teams. Its goals are to be:\n- Performant\n- Simple\n- Intuitive\n\nThruster also\n- Does not use `unsafe`\n- Works in stable rust\n\n## Fast\n\nThruster can be run with different server backends and represents a nicely packaged layer over them. This means that it can keep up with the latest and greatest changes from the likes of Hyper, Actix, or even ThrusterServer, a home-grown http engine.\n\n## Intuitive\n\nBased on frameworks like Koa, and Express, Thruster aims to be a pleasure to develop with.\n\n## Example\n\nTo run the example `cargo run --example \u003cexample-name\u003e`.\nFor example, `cargo run --example hello_world` and open [http://localhost:4321/](http://localhost:4321/)\n\n### Middleware Based\n\nThe core parts that make the new async await code work is designating middleware functions with the `#[middleware_fn]` attribute (which marks the middleware so that it's compatible with the stable futures version that Thruster is built on,) and then the `m!` macro in the actual routes.\n\nA simple example for using async await is:\n\n```rust\nuse std::boxed::Box;\nuse std::future::Future;\nuse std::pin::Pin;\nuse std::time::Instant;\n\nuse thruster::{App, BasicContext as Ctx, Request};\nuse thruster::{m, middleware_fn, MiddlewareNext, MiddlewareResult, Server, ThrusterServer};\n\n#[middleware_fn]\nasync fn profile(context: Ctx, next: MiddlewareNext\u003cCtx\u003e) -\u003e MiddlewareResult\u003cCtx\u003e {\n    let start_time = Instant::now();\n\n    context = next(context).await;\n\n    let elapsed_time = start_time.elapsed();\n    println!(\n        \"[{}μs] {} -- {}\",\n        elapsed_time.as_micros(),\n        context.request.method(),\n        context.request.path()\n    );\n\n    Ok(context)\n}\n\n#[middleware_fn]\nasync fn plaintext(mut context: Ctx, _next: MiddlewareNext\u003cCtx\u003e) -\u003e MiddlewareResult\u003cCtx\u003e {\n    let val = \"Hello, World!\";\n    context.body(val);\n    Ok(context)\n}\n\n#[middleware_fn]\nasync fn four_oh_four(mut context: Ctx, _next: MiddlewareNext\u003cCtx\u003e) -\u003e MiddlewareResult\u003cCtx\u003e {\n    context.status(404);\n    context.body(\"Whoops! That route doesn't exist!\");\n    Ok(context)\n}\n\n#[tokio::main]\nfn main() {\n    println!(\"Starting server...\");\n\n    let mut app = App::\u003cRequest, Ctx, ()\u003e::new_basic();\n\n    app.get(\"/plaintext\", m![profile, plaintext]);\n    app.set404(m![four_oh_four]);\n\n    let server = Server::new(app);\n    server.build(\"0.0.0.0\", 4321).await;\n}\n```\n\n### Error handling\n\nHere's a nice example\n\n```rust\nuse thruster::errors::ThrusterError as Error;\nuse thruster::proc::{m, middleware_fn};\nuse thruster::{map_try, App, BasicContext as Ctx, Request};\nuse thruster::{MiddlewareNext, MiddlewareResult, MiddlewareReturnValue, Server, ThrusterServer};\n\n#[middleware_fn]\nasync fn plaintext(mut context: Ctx, _next: MiddlewareNext\u003cCtx\u003e) -\u003e MiddlewareResult\u003cCtx\u003e {\n    let val = \"Hello, World!\";\n    context.body(val);\n    Ok(context)\n}\n\n#[middleware_fn]\nasync fn error(mut context: Ctx, _next: MiddlewareNext\u003cCtx\u003e) -\u003e MiddlewareResult\u003cCtx\u003e {\n    let res = \"Hello, world\".parse::\u003cu32\u003e()\n        .map_err(|_| {\n            let mut context = Ctx::default();\n            \n            context.status(400);\n\n            ThrusterError {\n                context,\n                message: \"Custom error message\".to_string(),\n                cause: None,\n            }\n        }?;\n\n    context.body(\u0026format!(\"{}\", non_existent_param));\n\n    Ok(context)\n}\n\n#[tokio::main]\nfn main() {\n    println!(\"Starting server...\");\n\n    let app = App::\u003cRequest, Ctx, ()\u003e::new_basic()\n        .get(\"/plaintext\", m![plaintext])\n        .get(\"/error\", m![error]);\n\n    let server = Server::new(app);\n    server.build(\"0.0.0.0\", 4321).await;\n}\n```\n\n## Testing\nThruster provides an easy test suite to test your endpoints, simply include the `testing` module as below:\n\n```rust\nlet mut app = App::\u003cRequest, Ctx, ()\u003e::new_basic();\n\n...\n\napp.get(\"/plaintext\", m![plaintext]);\n\n...\n\nlet result = testing::get(app, \"/plaintext\");\n\nassert!(result.body == \"Hello, World!\");\n```\n\n## Make your own middleware modules\nMiddleware is super easy to make! Simply create a function and export it at a module level. Below, you'll see a piece of middleware that allows profiling of requests:\n\n```rust\n#[middleware_fn]\nasync fn profiling\u003cC: 'static + Context + Send\u003e(\n    mut context: C,\n    next: MiddlewareNext\u003cC\u003e,\n) -\u003e MiddlewareResult\u003cC\u003e {\n    let start_time = Instant::now();\n\n    context = next(context).await?;\n\n    let elapsed_time = start_time.elapsed();\n    info!(\"[{}μs] {}\", elapsed_time.as_micros(), context.route());\n\n    Ok(context)\n}\n```\n\nYou might find that you want to allow for more specific data stored on the context, for example, perhaps you want to be able to hydrate query parameters into a hashmap for later use by other middlewares. In order to do this, you can create an additional trait for the context that middlewares downstream must adhere to. Check out the provided [query_params middleware](https://github.com/thruster-rs/Thruster/blob/master/thruster/src/middleware/query_params.rs) for an example.\n\n## Other, or Custom Backends\n\nThruster is capable of just providing the routing layer on top of a server of some sort, for example, in the Hyper snippet above. This can be applied broadly to any backend, as long as the server implements `ThrusterServer`.\n\n```rust\nuse async_trait::async_trait;\n\n#[async_trait]\npub trait ThrusterServer {\n    type Context: Context + Send;\n    type Response: Send;\n    type Request: RequestWithParams + Send;\n\n    fn new(App\u003cSelf::Request, Self::Context\u003e) -\u003e Self;\n    async fn build(self, host: \u0026str, port: u16);\n}\n```\n\nThere needs to be:\n- An easy way to create a server.\n- A function to build the server into a future that could be loaded into an async runtime.\n\nWithin the `build` function, the server implementation should:\n- Start up some sort of listener for connections\n- Call `let matched = app.resolve_from_method_and_path(\u003csome method\u003e, \u003csome path\u003e);` (This is providing the actual routing.)\n- Call `app.resolve(\u003cincoming request\u003e, matched)` (This runs the chained middleware.)\n\n## Why you should use Thruster\n- Change your backends at will. Out of the box, Thruster now can be used over: [actix-web](https://github.com/thruster-rs/Thruster/blob/master/thruster/examples/actix_most_basic.rs), [hyper](https://github.com/thruster-rs/Thruster/blob/master/thruster/examples/hyper_most_basic.rs), or [a custom backend](https://github.com/thruster-rs/Thruster/blob/master/thruster/examples/hello_world.rs)\n- Thruster supports [testing](#testing) from the framework level\n- @trezm gets lonely when no one makes PRs or opens issues.\n- Thruster is more succinct for more middleware-centric concepts -- like a route guard. Take this example in actix to restrict IPs:\n```rust\nfn ip_guard(head: \u0026RequestHead) -\u003e bool {\n    // Check for the cloudflare IP header\n    let ip = if let Some(val) = head.headers().get(CF_IP_HEADER) {\n        val.to_str().unwrap_or(\"\").to_owned()\n    } else if let Some(val) = head.peer_addr {\n        val.to_string()\n    } else {\n        return false;\n    };\n\n    \"1.2.3.4\".contains(\u0026ip)\n}\n\n#[actix_web::post(\"/ping\")]\nasync fn ping() -\u003e Result\u003cHttpResponse, UserPersonalError\u003e {\n    Ok(HttpResponse::Ok().body(\"pong\"))\n}\n\n...\n        web::scope(\"/*\")\n            // This is confusing, but we catch all routes that _aren't_\n            // ip guarded and return an error.\n            .guard(guard::Not(ip_guard))\n            .route(\"/*\", web::to(HttpResponse::Forbidden)),\n    )\n    .service(ping);\n...\n```\n\nHere is Thruster:\n\n```rust\n\n#[middleware_fn]\nasync fn ip_guard(mut context: Ctx, next: MiddlewareNext\u003cCtx\u003e) -\u003e MiddlewareResult\u003cCtx\u003e {\n    if \"1.2.3.4\".contains(\u0026context.headers().get(\"Auth-Token\").unwrap_or(\"\")) {\n        context = next(context).await?;\n\n        Ok(context)\n    } else {\n        Err(Error::unauthorized_error(context))\n    }\n\n}\n\n#[middleware_fn]\nasync fn ping(mut context: Ctx, _next: MiddlewareNext\u003cCtx\u003e) -\u003e MiddlewareResult\u003cCtx\u003e {\n    context.body(\"pong\");\n    Ok(context)\n}\n\n...\n    app.get(\"/ping\", m![ip_guard, plaintext]);\n...\n```\nA bit more direct is nice!\n\n## Why you shouldn't use Thruster\n- It's got few maintainers (pretty much just one.)\n- There are other projects that have been _far_ more battle tested. Thruster is in use in production, but nowhere that you'd know or that matters.\n- It hasn't been optimized by wicked smarties. @trezm tries his best, but keeps getting distracted by his dog(s).\n- Serously, this framework ~could be~ _is_ great, but it definitely hasn't been poked and proded like others. Your help could go a long way to making it more secure and robust, but we might not be there just yet.\n\nIf you got this far, thanks for reading! Always feel free to reach out.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthruster-rs%2FThruster","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthruster-rs%2FThruster","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthruster-rs%2FThruster/lists"}