{"id":446562,"date":"2024-02-28T13:13:56","date_gmt":"2024-02-28T12:13:56","guid":{"rendered":"https:\/\/blog.jetbrains.com\/?post_type=rust&#038;p=446562"},"modified":"2024-02-28T13:14:01","modified_gmt":"2024-02-28T12:14:01","slug":"how-to-write-your-first-rust-web-app-with-rocket-and-rustrover","status":"publish","type":"rust","link":"https:\/\/blog.jetbrains.com\/en\/rust\/2024\/02\/28\/how-to-write-your-first-rust-web-app-with-rocket-and-rustrover","title":{"rendered":"How to Write Your First Rust Web App with Rocket and RustRover"},"content":{"rendered":"\n<p>If you\u2019re like me and you\u2019re just starting your Rust journey, you likely have some or most of your experience from another ecosystem. That\u2019s great, but you\u2019re hearing a lot about this new technology stack called Rust and are wondering where to start. Have no fear. We\u2019ve got the tutorial just for you. In this post, we\u2019ll build a simple HTML web application running on a Rust backend using the Rocket library.<\/p>\n\n\n\n<p>If you haven\u2019t already, now is a great time to <a href=\"https:\/\/www.jetbrains.com\/rust\/\" target=\"_blank\" rel=\"noopener\">download RustRover<\/a>, our latest IDE explicitly designed for the Rust community. You\u2019ll also want to<a href=\"https:\/\/www.jetbrains.com\/help\/rust\/rust-toolchain.html\" target=\"_blank\" rel=\"noopener\"> install Rust using RustRover<\/a> or by <a href=\"https:\/\/www.rust-lang.org\/learn\/get-started\" target=\"_blank\" rel=\"noopener\">using the community getting started guide<\/a>.<\/p>\n\n\n\n<p>By the end of this tutorial, we\u2019ll have a web application hosting static files, an application with multiple endpoints, server-side template rendering, flash messaging, and request\/response flows. First, let\u2019s look at the library we\u2019ll be using, then dive into the specifics of building our sample application.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What is Rocket?<\/h2>\n\n\n\n<p><a href=\"https:\/\/rocket.rs\/\" target=\"_blank\" rel=\"noopener\">Rocket is a web framework built for Rust<\/a> that makes writing fast, type-safe, secure web applications simple. Like many modern web frameworks, Rocket expresses its application-building philosophy by adding endpoints accessible at unique paths. Rocket has out-of-the-box support for routing, data handling, validation, responders, cookies, web sockets, and database access. It is a fully-featured framework built on top of what makes Rust unique: type safety and macros.<\/p>\n\n\n\n<p>Now that you have a high-level understanding of Rocket, let\u2019s start building our application.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">A New Rocket Project<\/h2>\n\n\n\n<p>After starting RustRover, you\u2019ll want to create a new project from the <strong>New Project <\/strong>dialog.<\/p>\n\n\n\n<p>You can call your project anything you\u2019d like, but I\u2019ll call mine <code>\u201crocketapp\u201d<\/code> for this tutorial. Be sure to select the \u201c<strong>Binary (application)\u201d<\/strong> option and then click the <strong>Create<\/strong> button.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" width=\"1600\" height=\"1300\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2024\/02\/image-75.png\" alt=\"New Project dialog in RustRover showing a new Binary application.\" class=\"wp-image-446575\"\/><\/figure>\n\n\n\n<p>Let\u2019s set up the project\u2019s dependencies. In your <code>Cargo.toml<\/code> file, add the <code>rocket<\/code> and <code>rocket_dyn_templates<\/code> dependencies. <strong>RustRover will automatically download and update your crates whenever you add new dependencies, which is a fantastic feature.<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">[package]\nname = \"rocketapp\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nrocket = \"0.5.0\"\nrocket_dyn_templates = { version = \"0.1.0\", features = [\"handlebars\", \"tera\"] }<\/pre>\n\n\n\n<p>Next, in your <code>main.rs<\/code> file, paste the following code. Here, we\u2019ll add a new endpoint that responds with a <code>String<\/code> value. Take note of the <code>#[get(\u201c\/\u201d)]<\/code> attribute, which tells the Rust compiler about additional metadata for our newly created endpoint. In this case, a new endpoint responds to a user HTTP <code>GET<\/code> request at the <code>\/<\/code> path.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">use rocket::{get, launch, routes};\n\n#[launch]\nfn rocket() -> _ {\n    rocket::build()\n        .mount(\"\/\", routes![root])\n}\n\n#[get(\"\/\")]\nasync fn root() -> String {\n    \"Hello, World\".to_string()\n}\n<\/pre>\n\n\n\n<p>You should be able to run your application now using the <strong>Run Toolbar<\/strong> to initiate a new instance of your web application.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" width=\"1600\" height=\"1143\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2024\/02\/image-74.png\" alt=\"Running the Rocket application in RustRover\" class=\"wp-image-446564\"\/><\/figure>\n\n\n\n<p>Congratulations! You have successfully built your first Rust-powered application. Next, let\u2019s modify our application to include static file hosting, template rendering, and practice some response flow.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Level-up The Rocket Application<\/h2>\n\n\n\n<p>As a first step, let\u2019s add two new directories to the project&#8217;s root: <code>templates<\/code> and <code>public<\/code>. The <code>templates<\/code> directory will hold our view templates, while the <code>public<\/code> directory will hold static artifacts such as CSS, images, or JavaScript files.<\/p>\n\n\n\n<p>I\u2019ve copied the <a href=\"https:\/\/picocss.com\/\" target=\"_blank\" rel=\"noopener\">Pico CSS library<\/a> to a <code>css<\/code> folder, giving me some basic styling, which I\u2019ll use later in my HTML templates.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" width=\"852\" height=\"798\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2024\/02\/image-76.png\" alt=\"The folder view for my rocketapp in RustRover\" class=\"wp-image-446586\"\/><\/figure>\n\n\n\n<p>Now, let\u2019s update our application to render a new <strong>Handlebars <\/strong>template. Add a new <code>root.html.hbs<\/code> file to the <code>templates<\/code> directory with the following HTML.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"html\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;html lang=\"\">\n&lt;head>\n    &lt;meta charset=\"UTF-8\">\n    &lt;meta name=\"viewport\"\n          content=\"width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0\">\n    &lt;meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\">\n    &lt;link href=\"\/public\/css\/pico.min.css\" rel=\"stylesheet\">\n    &lt;title>Hello Rust&lt;\/title>\n&lt;\/head>\n&lt;body>\n&lt;main class=\"container\">\n    &lt;h1>{{message}}&lt;\/h1>\n&lt;\/main>\n&lt;\/body>\n&lt;\/html>\n<\/pre>\n\n\n\n<p>You can install the <a href=\"https:\/\/plugins.jetbrains.com\/plugin\/6884-handlebars-mustache\" target=\"_blank\" rel=\"noopener\">Handlebars plug-in from the JetBrains Marketplace<\/a> to make working with this syntax much more enjoyable. We need to modify our <code>main.rs <\/code>file again, but this time, we\u2019ll register the template system and map our static file directory to a path.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">use rocket::{get, launch, routes};\nuse rocket::fs::{FileServer, Options, relative};\nuse rocket_dyn_templates::{context, Template};\n\n#[launch]\nfn rocket() -> _ {\n    rocket::build()\n        \/\/ add templating system\n        .attach(Template::fairing())\n        \/\/ serve content from disk\n        .mount(\"\/public\", FileServer::new(relative!(\"\/public\"), Options::Missing | Options::NormalizeDirs))\n        \/\/ register routes\n        .mount(\"\/\", routes![root])\n}\n\n#[get(\"\/\")]\nasync fn root() -> Template {\n    Template::render(\"root\", context! { message: \"Hello, Rust\"})\n}\n<\/pre>\n\n\n\n<p>Now we\u2019re getting somewhere. You\u2019ll notice the use of the symbols amongst the code, mostly <code>#<\/code> and <code>!<\/code>. The usage is known as <a href=\"https:\/\/doc.rust-lang.org\/book\/ch19-06-macros.html\" target=\"_blank\" rel=\"noopener\">\u201cmacros\u201d<\/a> and is a fundamental part of the Rust language. Macros allow you to write declarative code while giving the Rust compiler hints as to what code to generate at compile time to make your application work. While other languages may lean on runtime discovery, Rust can determine how parts interact with each other at compile time, thus reducing wasted CPU cycles and unnecessary memory allocations.<\/p>\n\n\n\n<p>Let\u2019s start handling user data from HTML forms.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Data Handling in Rocket<\/h2>\n\n\n\n<p>We\u2019ve already established that Rust is about type and memory safety, so it should be no surprise that memory structures are an essential part of the language. You can think of structures as places in memory where our data lives, preferably in the fastest parts. Rocket lets us use a <code>struct<\/code> to represent form requests coming from HTML. Add the following data type in a new <code>models.rs<\/code> file in your <code>src<\/code> directory.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">use rocket::{FromForm};\n\n#[derive(FromForm, Debug)]\npub struct Person {\n    #[field(validate=len(1..))]\n    pub(crate) first_name: String,\n    #[field(validate=len(1..))]\n    pub(crate) last_name: String,\n}\n<\/pre>\n\n\n\n<p>A few things are happening in this small snippet of code.<\/p>\n\n\n\n<ol>\n<li>We are using the <code>field<\/code> macro and <code>derive<\/code> implementation for two different traits, <code>FromForm<\/code> and <code>Debug<\/code><\/li>\n\n\n\n<li>The keyword <code>pub<\/code> denotes that the <code>struct<\/code> and its fields are publicly accessible.<\/li>\n\n\n\n<li>Each field uses built-in Rocket validators for length; many more are available.<\/li>\n<\/ol>\n\n\n\n<p>Let\u2019s update our <code>root<\/code> template to handle submitting a form using this <code>Person<\/code> structure.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"html\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;!doctype html>\n&lt;html lang=\"en\">\n&lt;head>\n    &lt;meta charset=\"utf-8\">\n    &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n    &lt;meta name=\"color-scheme\" content=\"light dark\"\/>\n    &lt;link rel=\"stylesheet\" href=\"\/public\/css\/pico.min.css\">\n    &lt;title>Hello world!&lt;\/title>\n&lt;\/head>\n&lt;body>\n&lt;main class=\"container\">\n    &lt;h1>Hello Rust!&lt;\/h1>\n\n    {{#if errors }}\n        &lt;article>\n            &lt;header>&#x1f97a;Oh No!&lt;\/header>\n            &lt;p>There are some invalid fields in the form&lt;\/p>\n            &lt;ul>\n                {{#each errors}}\n                    &lt;li>{{this}}&lt;\/li>\n                {{\/each}}\n            &lt;\/ul>\n        &lt;\/article>\n    {{\/if}}\n\n    &lt;form method=\"POST\" enctype=\"multipart\/form-data\" action=\"\/\">\n        &lt;input type=\"text\"\n               name=\"first_name\"\n               placeholder=\"First Name\"\n               aria-label=\"First Name\"\n               value=\"{{ first_name }}\"\n               aria-invalid=\"{{ first_name_error}}\"\n        \/>\n\n        &lt;input type=\"text\" name=\"last_name\"\n               placeholder=\"Last Name\"\n               aria-label=\"Last Name\"\n               value=\"{{ last_name }}\"\n               aria-invalid=\"{{ last_name_error}}\"\n        \/>\n        \n        &lt;button type=\"submit\">Say Hello&lt;\/button>\n    &lt;\/form>\n&lt;\/main>\n&lt;\/body>\n&lt;\/html>\n<\/pre>\n\n\n\n<p>You\u2019ll likely notice a few Handlebars placeholders, which we will use in subsequent endpoints, so hold tight.<\/p>\n\n\n\n<p>Before returning to Rust, add one more template for our success page. I\u2019ve called it <code>hello.html.hbs<\/code> in the <code>templates<\/code> directory.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"html\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;!doctype html>\n&lt;html lang=\"en\">\n&lt;head>\n    &lt;meta charset=\"utf-8\">\n    &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n    &lt;meta name=\"color-scheme\" content=\"light dark\" \/>\n    &lt;link rel=\"stylesheet\" href=\"\/public\/css\/pico.min.css\">\n    &lt;title>Hello {{ name }}!&lt;\/title>\n&lt;\/head>\n&lt;body>\n&lt;main class=\"container\">\n    &lt;dialog open>\n        &lt;article>\n            &lt;header>\n                &lt;a href=\"\/\" aria-label=\"Close\" rel=\"prev\">&lt;\/a>\n                &lt;p>\n                    &lt;strong>&#x1f5d3;&#xfe0f; {{ message }}!&lt;\/strong>\n                &lt;\/p>\n            &lt;\/header>\n            &lt;p>\n                Hello {{ name }}\n            &lt;\/p>\n        &lt;\/article>\n    &lt;\/dialog>\n&lt;\/main>\n&lt;\/body>\n&lt;\/html>\n<\/pre>\n\n\n\n<p>We need to write two endpoints now, one to handle the user request and the other to redirect to when the form is valid. Let\u2019s do the \u201ceasy\u201d one first, the success page.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#[get(\"\/hi?&lt;name>\")]\nasync fn hello(name: String, flash: Option&lt;FlashMessage&lt;'_>>) -> Template {\n    let message = flash.map_or_else(|| String::default(), |msg| msg.message().to_string());\n    Template::render(\"hello\", context! { name , message })\n}\n<\/pre>\n\n\n\n<p>A few important things are happening in this function.<\/p>\n\n\n\n<ol>\n<li>We are receiving a <code>FlashMessage<\/code>, information stored in a cookie from the previous endpoint. We\u2019ll see how you can set this value soon.<\/li>\n\n\n\n<li>We are pulling the <code>name<\/code> value from the query string.<\/li>\n<\/ol>\n\n\n\n<p>Note that this is a demonstration of what Rocket provides as essential features of a web framework. You can experiment with this and pass all the values through the <code>FlashMessage<\/code> or the query string.<\/p>\n\n\n\n<p>Next, we\u2019ll delve into the most complex part of this sample application, the <code>POST<\/code> endpoint. Before looking at the code, let\u2019s discuss the behavior we want.<\/p>\n\n\n\n<ul>\n<li>When the response is <em>valid<\/em>, we want to redirect to the path <code>\/hi?name=<\/code> and set a <code>FlashMessage<\/code>.<\/li>\n\n\n\n<li>If the response is <em>invalid, <\/em>we want to render the <code>root<\/code> template and set values in the template context to display messages to the user.<\/li>\n<\/ul>\n\n\n\n<p>Let\u2019s see the code and talk about response flow.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#[post(\"\/\", data = \"&lt;form>\")]\nasync fn create(form: Form&lt;Contextual&lt;'_, Person>>) -> Result&lt;Flash&lt;Redirect>, Template> {\n    if let Some(ref person) = form.value {\n        let name = format!(\"{} {}\", person.first_name, person.last_name);\n        let message = Flash::success(Redirect::to(uri!(hello(name))), \"It Worked\");\n        return Ok(message);\n    }\n\n    let error_messages: Vec&lt;String> = form.context.errors().map(|error| {\n        let name = error.name.as_ref().unwrap().to_string();\n        let description = error.to_string();\n        format!(\"'{}' {}\", name, description)\n    }).collect();\n\n    Err(Template::render(\"root\", context! {\n        first_name : form.context.field_value(\"first_name\"),\n        last_name : form.context.field_value(\"last_name\"),\n        first_name_error : form.context.field_errors(\"first_name\").count() > 0,\n        last_name_error : form.context.field_errors(\"last_name\").count() > 0,\n        errors: error_messages\n    }))\n}\n<\/pre>\n\n\n\n<p>Rocket recognizes Rust\u2019s <code>Result<\/code> type. The <code>Result<\/code> type enables us to produce a tuple, a structure that contains multiple options. In our case, we have a successful and failed state. In the branching logic here, we handle the states of Rocket\u2019s <code>Form&lt;Contextual&lt;&gt;&gt;<\/code> type, which provides us with the state of the user\u2019s form submission. Here, we can use Rust\u2019s pattern matching to handle the two most important states of <code>Some<\/code> and <code>Err<\/code>. Response flow is essential to building web applications, as you may need to redirect or render elements based on user input, and with Rust, it\u2019s never been easier.<\/p>\n\n\n\n<p>For the last time, let\u2019s update our <code>main.rs<\/code> to wire up the new endpoints and import our <code>Person<\/code> structure from our <code>model.rs<\/code> module.&nbsp;<\/p>\n\n\n\n<p><strong>As a side note, RustRover is excellent at finding types in modules and updating your <code>use<\/code> statements. <\/strong>RustRover features allow you to focus on writing code rather than hunting down modules.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">mod models;\n\nuse rocket::{get, launch, post, routes, uri};\nuse rocket::form::{Contextual, Form};\nuse rocket::fs::{FileServer, Options, relative};\nuse rocket::request::FlashMessage;\nuse rocket::response::{Flash, Redirect};\nuse rocket_dyn_templates::{context, Template};\nuse crate::models::Person;\n\n#[launch]\nfn rocket() -> _ {\n    rocket::build()\n        \/\/ add templating system\n        .attach(Template::fairing())\n        \/\/ serve content from disk\n        .mount(\"\/public\", FileServer::new(relative!(\"\/public\"), Options::Missing | Options::NormalizeDirs))\n        \/\/ register routes\n        .mount(\"\/\", routes![root, create, hello])\n}\n\n#[get(\"\/\")]\nasync fn root() -> Template {\n    Template::render(\"root\", context! { message: \"Hello, Rust\"})\n}\n\n#[post(\"\/\", data = \"&lt;form>\")]\nasync fn create(form: Form&lt;Contextual&lt;'_, Person>>) -> Result&lt;Flash&lt;Redirect>, Template> {\n    if let Some(ref person) = form.value {\n        let name = format!(\"{} {}\", person.first_name, person.last_name);\n        let message = Flash::success(Redirect::to(uri!(hello(name))), \"It Worked\");\n        return Ok(message);\n    }\n\n    let error_messages: Vec&lt;String> = form.context.errors().map(|error| {\n        let name = error.name.as_ref().unwrap().to_string();\n        let description = error.to_string();\n        format!(\"'{}' {}\", name, description)\n    }).collect();\n\n    Err(Template::render(\"root\", context! {\n        first_name : form.context.field_value(\"first_name\"),\n        last_name : form.context.field_value(\"last_name\"),\n        first_name_error : form.context.field_errors(\"first_name\").count() > 0,\n        last_name_error : form.context.field_errors(\"last_name\").count() > 0,\n        errors: error_messages\n    }))\n}\n\n#[get(\"\/hi?&lt;name>\")]\nasync fn hello(name: String, flash: Option&lt;FlashMessage&lt;'_>>) -> Template {\n    let message = flash.map_or_else(|| String::default(), |msg| msg.message().to_string());\n    Template::render(\"hello\", context! { name , message })\n}\n<\/pre>\n\n\n\n<p>Running the application, we can see our form, and that validation works.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" width=\"1174\" height=\"1054\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2024\/02\/image-77.png\" alt=\"The Rocket application running in the browser showing a first name and last name form with validation errrors.\" class=\"wp-image-446597\"\/><\/figure>\n\n\n\n<p>We can also submit our first and last names, letting our app redirect us to the success page.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" width=\"1120\" height=\"596\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2024\/02\/image-78.png\" alt=\"An HTML dialog showing &quot;Hello Khalid Abuhakmeh&quot; and &quot;It Worked!&quot; as the title.\" class=\"wp-image-446608\"\/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>This tutorial came about because I was curious about what a type-safe language like Rust has to offer folks interested in web development, and to my surprise, the answer is a lot.<\/p>\n\n\n\n<p> The Rust development experience is fast and straightforward once you understand some of the basics of the language. The syntax may seem intimidating at first, but <a href=\"https:\/\/www.jetbrains.com\/rust\/\" target=\"_blank\" rel=\"noopener\">RustRover<\/a> does a great job surfacing the Rocket documentation, and combined with <a href=\"https:\/\/www.jetbrains.com\/ai\/\" target=\"_blank\" rel=\"noopener\">JetBrains AI Assistant<\/a>, I found the hints I needed along the way to make this demo. I also want to thank the Rust community for being helpful over on Mastodon.<\/p>\n\n\n\n<p><a href=\"https:\/\/github.com\/khalidabuhakmeh\/rocketapp\" target=\"_blank\" rel=\"noopener\">If you\u2019d like to try this sample, I\u2019ve made it available as a GitHub repository.<\/a> I\u2019d also love to hear about your experience with Rust, Rocket, RustRover, and other Rust-based web frameworks in the ecosystem.<\/p>\n","protected":false},"author":1079,"featured_media":449673,"comment_status":"closed","ping_status":"closed","template":"","categories":[],"tags":[530,8430,416],"cross-post-tag":[],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/rust\/446562"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/rust"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/types\/rust"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/users\/1079"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/comments?post=446562"}],"version-history":[{"count":11,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/rust\/446562\/revisions"}],"predecessor-version":[{"id":458165,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/rust\/446562\/revisions\/458165"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/media\/449673"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/media?parent=446562"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/categories?post=446562"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/tags?post=446562"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/cross-post-tag?post=446562"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}