{"id":18765239,"url":"https://github.com/technetos/rustserve","last_synced_at":"2026-03-02T23:38:41.805Z","repository":{"id":74868745,"uuid":"485596554","full_name":"technetos/rustserve","owner":"technetos","description":"A runtime agnostic REST framework","archived":false,"fork":false,"pushed_at":"2023-03-19T23:44:09.000Z","size":52,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-13T05:12:18.702Z","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/technetos.png","metadata":{"files":{"readme":"README.rst","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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}},"created_at":"2022-04-26T01:48:42.000Z","updated_at":"2023-01-26T09:17:06.000Z","dependencies_parsed_at":"2024-11-07T18:38:36.989Z","dependency_job_id":"64f5c7a2-fb69-4c62-861c-2f3c561e71f7","html_url":"https://github.com/technetos/rustserve","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/technetos/rustserve","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/technetos%2Frustserve","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/technetos%2Frustserve/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/technetos%2Frustserve/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/technetos%2Frustserve/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/technetos","download_url":"https://codeload.github.com/technetos/rustserve/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/technetos%2Frustserve/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30025311,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-02T23:36:48.274Z","status":"ssl_error","status_checked_at":"2026-03-02T23:33:36.569Z","response_time":60,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":"2024-11-07T18:33:18.402Z","updated_at":"2026-03-02T23:38:41.787Z","avatar_url":"https://github.com/technetos.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"🍦Rustserve\n============\n\nRustserve is a runtime agnostic async HTTP web framework written in Rust.  \n\n\n📖How it works\n--------------\n\nThe main component in a Rustserve webserver is a `Controller`.  Controllers have\nmethods that map to the `HTTP` methods, `GET`, `POST`, `PUT`, etc.  All of the\n`Controller` methods have defaults that return 404, you don't have to implement\nany of them.  The `Controller` trait, along with a `Route` allow you to define\ncustom behavior for a route at a specific `HTTP` method.  \n\nThroughout this README we are going to build up an example server.  The full\nimplementation can be found `here`. Lets implement a `Controller`.  \n\n.. code-block:: rust\n\n   struct MyController {\n\n   }\n\n   impl Controller for MyController {\n\n   }\n\nThat's it!  Lets go over some details about whats going on here.  \n\n.. code-block:: rust\n\n   impl Controller for MyController {\n\n   }\n\nThis is the implementation of `Controller` for the `MyController` struct.  All\nof the methods in the `Controller` trait have defaults that return 404.  Lets\nimplement the `get` method for `MyController` so that it returns a 200 instead\n\n.. code-block:: rust\n\n   impl Controller for MyController {\n      fn get\u003c'a\u003e(\n          self: Arc\u003cSelf\u003e,\n          _req: Request\u003c\u0026'a [u8]\u003e,\n          _params: HashMap\u003cString, String\u003e,\n      ) -\u003e Pin\u003cBox\u003cdyn Future\u003cOutput = anyhow::Result\u003cResponse\u003cVec\u003cu8\u003e\u003e\u003e\u003e + Send + 'a\u003e\u003e {\n          Box::pin(async move {\n            Ok(Response::builder().status(200).body(vec![])?)\n          })\n      }\n   }\n\nOk! There is a lot going on here, lets break it down\n\n.. code-block:: rust\n\n   impl Controller for MyController {\n      // We are implementing the `get` method, meaning GET requests to this\n      // controller will use our `get` implementation\n      fn get\u003c'a\u003e(\n          // Self is an Arc\u003cSelf\u003e, meaning that you can only use controller\n          // methods when your controller is an Arc\u003cdyn Controller\u003e.  \n          self: Arc\u003cSelf\u003e,\n          // The request with its body as a series of bytes\n          _req: Request\u003c\u0026'a [u8]\u003e,\n          // The params parsed out of the uri such as /:version/\n          _params: HashMap\u003cString, String\u003e,\n      // The return type is a Future that outputs a Result\u003cResponse\u003cVec\u003cu8\u003e\u003e\u003e\n      ) -\u003e BoxFuture\u003c'a, anyhow::Result\u003cResponse\u003cVec\u003cu8\u003e\u003e\u003e\u003e {\n          // Create a Future\n          Box::pin(async move {\n            // Return a Result\u003cResponse\u003cVec\u003cu8\u003e\u003e\u003e\n            Ok(Response::builder().status(200).body(vec![])?)\n          })\n      }\n    }\n\n\nCool! So now GET requests to this controller return a 200 and an empty body. At\nthis point we have covered the basics of the `Controller` trait.  It provides\nthe standard HTTP methods to `MyController` and when we implement any of the\n`Controller` methods, those methods get used when a request comes in.\n\nError\n-----\n\nLets implement an `Error\u003c'a, T\u003e` method so we can understand more about how our\nerrors are being handled in our `get` implementation.\n\n.. code-block:: rust\n\n   #[derive(serde::Deserialize)]\n   struct MyError;\n\n   impl\u003c'a\u003e Error\u003c'a, MyError, 500\u003e for MyController {}\n\nWhew! Again there is a lot going on, lets break it down\n\n.. code-block:: rust\n\n   // Derive serialize for the error message\n   #[derive(serde::Serialize)]\n   // The actual error message\n   struct MyError;\n\n   // Implementation of the Error trait parameterized with our message and the\n   // HTTP status code for the error.\n   impl\u003c'a\u003e Error\u003c'a, MyError, 500\u003e for MyController {}\n\nSo whats going on here? We implemented the `get` method, that seems to make\nsense, but we are never using `MyError`, why is it necessary? \n\nThat's a great question.  Various kinds of errors can occur in your web server,\nsometimes something is broken or wrong resulting in a 500 or a request is\nmalformed and results in a 400, all of these errors can be represented by the\n`Error\u003c'a, T\u003e` trait.  The idea is that when something goes wrong in your\ncontroller, your controller defines what happens on a per error basis.  \n\nWith that in mind, lets re-implement the `get` method implementation to return\nan error.\n\n.. code-block:: rust\n\n\n   impl Controller for MyController {\n      fn get\u003c'a\u003e(\n          self: Arc\u003cSelf\u003e,\n          _req: Request\u003c\u0026'a [u8]\u003e,\n          _params: HashMap\u003cString, String\u003e,\n      ) -\u003e Pin\u003cBox\u003cdyn Future\u003cOutput = anyhow::Result\u003cResponse\u003cVec\u003cu8\u003e\u003e\u003e\u003e + Send + 'a\u003e\u003e {\n          Box::pin(async move {\n              self.error(MyError {}).await\n          })\n      }\n    }\n\nWow! Thats actually really simple.  When we implement the `Error` trait for\n`MyController` the controller gains a method called `self.error`. Lets dive into\nthe `Error` trait a bit more.\n\n\n.. code-block:: rust\n\n   impl\u003c'a\u003e Error\u003c'a, MyError, 500\u003e for MyController {}\n\n   //                    ^      ^            ^\n   //                    |      |            |\n   //                    |      |            |\n\nLets break this down.  First we can see that the `Error` trait takes in a few\ngeneric arguments, `MyError` and `500`.  The first generic parameter is the\nerror message, it can be anything that implements `serde::Serialize`, the second\nargument is the HTTP status code that accompanies the returned error.  This\nmeans that we can use the same error message with different error codes.  Next\nwe can see that we are implementing the `Error` trait for `MyController`, this\nprovides `MyController` a `self.error` method that expects an instance of\n`MyError`.  As we can see in the example above, `self.error` is an async\nfunction and must be `.await'ed`.  The `Error` trait can be implemented multiple\ntimes for the same controller.  This allows controllers to use `self.error` with\nany error messages supported by the controller. The returned type from calling\n`self.error` is a future that yields `anyhow::Result\u003cResponse\u003cVec\u003cu8\u003e\u003e` or in\nother terms, a future that yields an HTTP response with the serialized message\nas the response body.\n\nReply\n-----\n\nCool! Now we know how to return errors, but what if we want to return a 200 ok\nwith a response body?\n\nThats another great question! Lets take another look at our example where we\nreturned a 200 ok with an empty body.\n\n.. code-block:: rust\n\n   impl Controller for MyController {\n      fn get\u003c'a\u003e(\n          self: Arc\u003cSelf\u003e,\n          _req: Request\u003c\u0026'a [u8]\u003e,\n          _params: HashMap\u003cString, String\u003e,\n      ) -\u003e Pin\u003cBox\u003cdyn Future\u003cOutput = anyhow::Result\u003cResponse\u003cVec\u003cu8\u003e\u003e\u003e\u003e + Send + 'a\u003e\u003e {\n          Box::pin(async move {\n            Ok(Response::builder().status(200).body(vec![])?)\n          })\n      }\n   }\n\nHere we are manually crafting a response with a 200 status code and empty body,\nThats fine but theres a better way.\n\n.. code-block:: rust\n\n   #[derive(serde::Serialize)]\n   struct MyMessage {}\n\n   impl Reply\u003c'a, MyMessage\u003e for MyController {}\n\n   impl Controller for MyController {\n      fn get\u003c'a\u003e(\n          self: Arc\u003cSelf\u003e,\n          _req: Request\u003c\u0026'a [u8]\u003e,\n          _params: HashMap\u003cString, String\u003e,\n      ) -\u003e Pin\u003cBox\u003cdyn Future\u003cOutput = anyhow::Result\u003cResponse\u003cVec\u003cu8\u003e\u003e\u003e\u003e + Send + 'a\u003e\u003e {\n          Box::pin(async move {\n              self.reply(MyMessage).await\n          })\n      }\n    }\n\nWoah woah slow down, whats this `Reply` trait?\n\nThats another great question! Lets dive into it.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftechnetos%2Frustserve","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftechnetos%2Frustserve","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftechnetos%2Frustserve/lists"}