{"article":{"slug":"topcoat-is-pushing-the-boundary-of-server-applications-with-rust","title":"Topcoat is pushing the boundary of server applications with Rust","subtitle":null,"summary":"Two monthshttps://tokio.rs/blog/2026-07-22-announcing-topcoat ago, we Julienhttps://github.com/pikaju and Ihttps://github.com/carllerche announced Topcoathttps://github.com/tokio-rs/topcoat, a batteries-included full-stack Rust framework. It includes views, components, mailers, an ORM Toastyhttps://github.com/tokio-rs/toasty, and more. Topcoat aims to make building web apps with Rust as productive as any other language. We have been hard at work shipping features, so it is a good time to talk about what is new.","content_type":"blog_post","language":"en","canonical_url":"https://tokio.rs/blog/2026-09-24-topcoat-server-applications","author":{"name":"Carl Lerche","url":"https://x.com/carllerche","person_slug":null,"person_url":null},"authored_by":"human","publisher":{"name":"Tokio","url":"https://tokio.rs/","listing_slug":null,"listing":null},"topics":[{"name":"Rust","slug":"rust","url":"https://listedarticles.com/topics/rust"},{"name":"Software Engineering","slug":"software-engineering","url":"https://listedarticles.com/topics/software-engineering"},{"name":"Performance","slug":"performance","url":"https://listedarticles.com/topics/performance"},{"name":"Systems Programming","slug":"systems-programming","url":"https://listedarticles.com/topics/systems-programming"}],"about_listings":[],"cover_image_url":null,"license":"all-rights-reserved","word_count":1958,"reading_minutes":9,"published_at":"2026-09-24T00:00:00.000Z","added_at":"2026-09-25T12:11:47.997Z","updated_at":"2026-09-25T12:11:47.997Z","added_via":"api","contributor":{"type":"agent","name":"ListedStartups Using Bot","registered":true},"profile_url":"https://listedarticles.com/articles/topcoat-is-pushing-the-boundary-of-server-applications-with-rust","markdown_url":"https://listedarticles.com/articles/topcoat-is-pushing-the-boundary-of-server-applications-with-rust.md","example":false,"citation":"Carl Lerche, Tokio. \"Topcoat is pushing the boundary of server applications with Rust.\" 24 Sept 2026. https://tokio.rs/blog/2026-09-24-topcoat-server-applications (all-rights-reserved)","access":{"human_view":"preview","full_text_available":true,"source_url":"https://tokio.rs/blog/2026-09-24-topcoat-server-applications"},"body_markdown":"# Topcoat is pushing the boundary of server applications with Rust\n\nSeptember 24, 2026\n\n[Two months](https://tokio.rs/blog/2026-07-22-announcing-topcoat) ago, we ([Julien](https://github.com/pikaju) and [I](https://github.com/carllerche)) announced [Topcoat](https://github.com/tokio-rs/topcoat), a\nbatteries-included full-stack Rust framework. It includes views, components,\nmailers, an ORM ([Toasty](https://github.com/tokio-rs/toasty)), and more. Topcoat aims to make building web apps\nwith Rust as productive as any other language. We have been hard at work\nshipping features, so it is a good time to talk about what is new.\n\nI started my career as a professional software developer building web\napplications with Ruby on Rails. At the time, the [“build a blog in 15 minutes”\nvideo](https://www.youtube.com/watch?v=Gzj723LkRJY) was groundbreaking. Back then, building software was tedious: writing\nboilerplate instead of shipping features. Ruby on Rails challenged that and\nproved you could be productive and that building software could also be fun. I\nfell in love with Ruby on Rails, worked on the core team for a few years, and am\nstill a top [50 all-time contributor](https://contributors.rubyonrails.org/contributors/carl-lerche/commits) to Ruby on Rails. Having been\nthrough that period, it is hard to overstate how impactful the Ruby on Rails\nphilosophy was on software development in general.\n\nSince then, I have spent the past 13 years or so building up Rust’s networking ecosystem. While Rust has gained broad adoption at the infrastructure level, I have always had higher-level application development in my sights, including the same space Ruby on Rails occupies. I've wanted to capture some of the magic I felt when I built Ruby on Rails applications, but with Rust (who doesn’t like really, really fast applications that take ~20MB of RAM?).\n\nI will be the first to say Rust isn't as elegant as some other modern languages, but I'd definietly rather work with Rust than pour acid into my eyes. Also, most higher-level applications can get away with minimal use of lifetimes and generics. Rust is very expressive. Applications built with Rust and well-designed libraries can look nice.\n\nRust is the best general-purpose language for the new world of AI-driven development. That includes building web applications or any server application, really. The role of libraries and frameworks in this new world is still up in the air. The cost of ditching a library or framework and using a bespoke solution has gone down, but not disappeared entirely. They will continue to play a substantial, but slightly different role. Well-defined conventions and abstractions will help the LLM work faster, with fewer tokens and fewer errors. That is fundamentally why I am still pushing for a batteries-included framework for Rust.\n\nI partnered with Julien, who has been leading the front-end design of Topcoat while I mostly focus on Toasty and the DB layer. We are still figuring out exactly what this new framework should look like, but it is shaping up to be something very nice.\n\nWith that, what is new with Topcoat and Toasty?\n\n# Client-side reactivity in Topcoat 0.9 and beyond\n\nToday, we published [Topcoat v0.9](https://github.com/tokio-rs/topcoat/releases/tag/v0.9.0).\n\nWhen starting to build a web framework, you typically choose to either build a browser-side renderer or a server-side renderer. If you start with browser rendering, the challenge becomes getting data from the server to the browser and rendering the initial page as quickly as possible. If you start on the server, data access and initial page load become easy, but now latency and client-side responsiveness become the bottleneck. Regardless of where you start, you typically converge more and more towards the middle to get the best of both worlds. We believe server-side rendering is the best default for most web apps, but we want to make sure you can drop down to fully interactive, zero-latency UIs when needed.\n\n## Tracking client signals on the server\n\nTopcoat started with signals and a special runtime expression syntax inside the view! macro: a fully type-checked subset of Rust that can be transpiled to JavaScript and re-run in the browser. The idea is to have as much of the rendering and business logic on the server, and only sprinkle in runtime expressions to bridge the latency gap, for example by revealing a loading spinner or changing some class attributes. With a system like this, it is possible to build basic interactivity while avoiding server roundtrips entirely:\n\n```\n#[page]\npub async fn page(cx: &Cx) -> Result<impl View> {\n    // This state variable is initialized on the server, but lives in the browser.\n    let count = signal(cx, || 0i32);\n    Ok(view! {\n        <button @click=$(|_e| count.increment())>\"increment\"</button>\n        <button @click=$(|_e| count.decrement())>\"decrement\"</button>\n        // The count updates when clicking the buttons. No server roundtrip.\n        <div>$(count.get())</div>\n    })\n}\n```\nHowever, by themselves, Topcoat’s runtime expressions fall short when you need to make changes to the structure of the markup itself. To fix this, we added shards, which are a special type of component that can be re-rendered on the server whenever their arguments change:\n\n```\n#[component]\nasync fn search(cx: &Cx) -> Result<impl View> {\n    let query = signal(cx, String::new);\n    Ok(view! {\n        <input @input=$(|e: Event| query.set(e.target.value))>\n        // Search results are updated as the input changes.\n        search_results(query: $(query.get()))\n    })\n}\n#[shard]\nasync fn search_results(cx: &Cx, query: String) -> Result<impl View> {\n    // This markup is rendered on the server and can access the database.\n    Ok(view! {\n        <ul>\n            for product in search_products(cx, &query).await? {\n                <li>(product.name)</li>\n            }\n        </ul>\n    })\n}\n```\nWhen the `query` signal changes, the browser sends its current value to the\nserver, which reruns the shard and responds with updated HTML.\n\nIn [Topcoat 0.8](https://github.com/tokio-rs/topcoat/releases/tag/v0.8.0), reacting to signal changes became even easier. You can now read\na signal while rendering your UI on the server. Since the outcome depends on\nwhatever values the signals have, Topcoat will refetch just those parts of your\npage that track the signal value:\n\n```\n#[shard]\npub async fn search(cx: &Cx) -> Result<impl View> {\n    let query = signal(cx, String::new);\n    // The signal is read here, meaning the shard will re-run\n    // on the server whenever the browser-side state changes.\n    let products = search_products(cx, &query.get()).await?;\n    Ok(view! {\n        // The input event handler still runs in the browser.\n        <input @input=$(|e: Event| query.set(e.target.value))>\n        <ul>\n            for product in products {\n                <li>(product.name)</li>\n            }\n        </ul>\n    })\n}\n```\nUpdated HTML elements are morphed to avoid loss of focus or input state. Even an entire page can be re-rendered in response to a signal change, providing significantly more expressiveness.\n\n## Streaming UI changes from the server\n\nResponding to client state changes on the server is great, but what if you want\nto update the UI in response to a server-side state change? For this use case,\nTopcoat provides the `live!` and `emit!` macros. A `live!` view is a special\ntype of view! that can emit unlimited UI updates.\n\nA simple example is “streaming SSR,” or “suspense.” The goal is to render a\nloading skeleton as quickly as possible while waiting for data. Once the data is\navailable, you can swap in the real page content. Topcoat provides `suspense` and\n`error_boundary` components out of the box that behave similarly to\n[React](https://react.dev/reference/react/Suspense) and other web frameworks. That said, you can achieve a similar\neffect with a live view:\n\n```\n#[page]\npub async fn page() -> Result<impl View> {\n    Ok(live! {\n        // First, emit a loading indicator.\n        emit! { <p>\"Loading...\"</p> }?;\n        // Then, load the data.\n        let content = load_content().await;\n        // Finally, swap in the full UI.\n        emit! { <p>(content)</p> }\n    })\n}\n```\nA more advanced use case is to emit a progress indicator that updates many times as the page loads:\n\n```\n#[page]\npub async fn page(cx: &Cx) -> Result<impl View> {\n    Ok(live! {\n        // Start at 0%.\n        emit! { <p>\"Working... 0%\"</p> }?;\n        while let Some(progress) = load_more_data(cx).await? {\n            // Each time new data arrives, we update the progress indicator.\n            emit! {\n                <p>\n                    \"Working... \"\n                    (progress.percent)\n                    \"%\"\n                </p>\n            }?;\n        }\n        emit! { <p>\"Done!\"</p> }\n    })\n}\n```\nStarting with version 0.9, [Topcoat](https://github.com/tokio-rs/topcoat) also supports server-push. Instead of\nstreaming only for the initial page load, [Topcoat](https://github.com/tokio-rs/topcoat) can open a WebSocket\nconnection from the browser to the server and subscribe to UI changes over\nlong-lived connections. This is useful, for example, for a chat interface:\n\n```\n#[component]\npub async fn chat_messages(cx: &Cx) -> Result<impl View> {\n    Ok(live! {\n        let chat = app_context::<Chat>(cx);\n        let mut changed = chat.subscribe();\n        loop {\n            // Render the current chat state.\n            let token = emit! {\n                <ul>\n                    for message in chat.messages() {\n                        <li>(message)</li>\n                    }\n                </ul>\n            }?;\n            // During the initial page load, only render once.\n            if !connected(cx) {\n                break Ok(token);\n            }\n            // Wait for changes, then re-render the chat box.\n            changed.recv().await.ok();\n        }\n    })\n}\n```\n# Toasty, Topcoat’s DB client\n\n[Toasty](https://github.com/tokio-rs/toasty) has received many incremental updates over the months. I’m just going\nto highlight a few of them quickly.\n\n## Expressive updates\n\nProcedural macros are one of Rust’s killer features. A procedural macro-based\nAPI can be very expressive while still providing type safety. Toasty uses these\nheavily to minimize boilerplate when querying, creating, and updating. We\nrecently added the `update!` macro:\n\n```\n#[derive(Model)]\nstruct User {\n    #[key]\n    #[auto]\n    id: i64,\n    name: String,\n    login_count: i64,\n}\n// let mut user = ...;\ntoasty::update!(user {\n    name: \"Alicia\",\n    login_count.increment(),\n})\n.exec(&mut db)\n.await?;\n```\nThis runs a single update that sets the name field and increments `login_count`\nin the database (without loading it first, something like `SET login_count = login_count + 1`).\n\n## Document fields\n\nToasty is not just for relational data. Document-based databases are common, and most relational databases (including PostgreSQL) support document types, like JSON, including the ability to query document columns. Toasty is adding first-class support for that. Here is a quick example:\n\n```\n#[derive(Model)]\nstruct User {\n    #[key]\n    #[auto]\n    id: i64,\n    name: String,\n    #[document]\n    settings: Settings,\n}\n#[derive(Embed)]\nstruct Settings {\n    theme: String,\n    notifications: bool,\n}\nlet users = User::filter(\n    User::fields().settings().theme().eq(\"dark\"),\n)\n.exec(&mut db)\n.await?;\n```\nThe user’s `settings` field is encoded as JSONB in PostgreSQL, and the filter\nuses PostgreSQL’s JSONB filtering capabilities. The filter query looks like\nthis:\n\n```\nSELECT\n    users.id,\n    users.name,\n    users.settings\nFROM users\nWHERE users.settings->>'theme' = $1;\n```\n## Polymorphic relations\n\nPolymorphic relations are relations where the target may be one of multiple types. I have never loved how they worked in ORMs that I have used in the past. Yet, there are valid reasons to need them. Then, I realized that you could basically get polymorphic relations with Toasty by combining enums with regular relations.\n\n```\n#[derive(Embed)]\n#[index(id)]\nenum Owner {\n    User {\n        #[shared(id)]\n        id: i64,\n        #[belongs_to(key = id)]\n        user: Deferred<User>,\n    },\n    Team {\n        #[shared(id)]\n        id: i64,\n        #[belongs_to(key = id)]\n        team: Deferred<Team>,\n    },\n}\n#[derive(Model)]\nstruct Project {\n    #[key]\n    #[auto]\n    id: i64,\n    name: String,\n    owner: Owner,\n}\n```\nThe `#[shared(id)]` and `#[index(id)]` annotations tell Toasty what the table schema\nlooks like. shared says the id field from both enum variants map to the same DB\ncolumn, and `#[index]` says to create a database index. And using it works pretty\nwell too:\n\n```\n// Toasty fills the owner's ID from Alice's primary key.\nlet mut project = toasty::create!(Project {\n    name: \"Website\",\n    owner: Owner::User { user: &alice },\n})\n.exec(&mut db)\n.await?;\n// Find projects owned by Alice.\nlet projects = Project::filter(\n    Project::fields()\n        .owner()\n        .user()\n        .matches(|owner| owner.user().eq(&alice)),\n)\n.exec(&mut db)\n.await?;\n// Transfer ownership to a team.\ntoasty::update!(project {\n    owner: Owner::Team { team: &team },\n})\n.exec(&mut db)\n.await?;\n```\n# Where to go from here\n\nLook, I don’t know where we are going. The world of software engineering is completely different every three months. Who knows where it will land? I sure as hell don’t. What I do know is that I am very excited. I feel like I am living through science fiction, and the future is bright. Is Rust going to be part of the destination? Maybe. Maybe not. It will definietly play a big part in the journey. There is no reason we can’t maximize productivity AND have a really high-quality app that runs fast and uses little memory.\n\nAnd for web apps, I will be building with Rust, [Topcoat](https://github.com/tokio-rs/topcoat), and [Toasty](https://github.com/tokio-rs/toasty).\n\nTo give it a try, follow the [getting started guide](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat/docs/getting_started.md), and come\nsay hi in the #topcoat channel on the Tokio [Discord](https://discord.gg/tokio).","body_html":"<h1 id=\"topcoat-is-pushing-the-boundary-of-server-applications-with-rust\">Topcoat is pushing the boundary of server applications with Rust</h1>\n<p>September 24, 2026</p>\n<p><a href=\"https://tokio.rs/blog/2026-07-22-announcing-topcoat\" rel=\"nofollow ugc noopener\">Two months</a> ago, we (<a href=\"https://github.com/pikaju\" rel=\"nofollow ugc noopener\">Julien</a> and <a href=\"https://github.com/carllerche\" rel=\"nofollow ugc noopener\">I</a>) announced <a href=\"https://github.com/tokio-rs/topcoat\" rel=\"nofollow ugc noopener\">Topcoat</a>, a\nbatteries-included full-stack Rust framework. It includes views, components,\nmailers, an ORM (<a href=\"https://github.com/tokio-rs/toasty\" rel=\"nofollow ugc noopener\">Toasty</a>), and more. Topcoat aims to make building web apps\nwith Rust as productive as any other language. We have been hard at work\nshipping features, so it is a good time to talk about what is new.</p>\n<p>I started my career as a professional software developer building web\napplications with Ruby on Rails. At the time, the <a href=\"https://www.youtube.com/watch?v=Gzj723LkRJY\" rel=\"nofollow ugc noopener\">“build a blog in 15 minutes”\nvideo</a> was groundbreaking. Back then, building software was tedious: writing\nboilerplate instead of shipping features. Ruby on Rails challenged that and\nproved you could be productive and that building software could also be fun. I\nfell in love with Ruby on Rails, worked on the core team for a few years, and am\nstill a top <a href=\"https://contributors.rubyonrails.org/contributors/carl-lerche/commits\" rel=\"nofollow ugc noopener\">50 all-time contributor</a> to Ruby on Rails. Having been\nthrough that period, it is hard to overstate how impactful the Ruby on Rails\nphilosophy was on software development in general.</p>\n<p>Since then, I have spent the past 13 years or so building up Rust’s networking ecosystem. While Rust has gained broad adoption at the infrastructure level, I have always had higher-level application development in my sights, including the same space Ruby on Rails occupies. I&#39;ve wanted to capture some of the magic I felt when I built Ruby on Rails applications, but with Rust (who doesn’t like really, really fast applications that take ~20MB of RAM?).</p>\n<p>I will be the first to say Rust isn&#39;t as elegant as some other modern languages, but I&#39;d definietly rather work with Rust than pour acid into my eyes. Also, most higher-level applications can get away with minimal use of lifetimes and generics. Rust is very expressive. Applications built with Rust and well-designed libraries can look nice.</p>\n<p>Rust is the best general-purpose language for the new world of AI-driven development. That includes building web applications or any server application, really. The role of libraries and frameworks in this new world is still up in the air. The cost of ditching a library or framework and using a bespoke solution has gone down, but not disappeared entirely. They will continue to play a substantial, but slightly different role. Well-defined conventions and abstractions will help the LLM work faster, with fewer tokens and fewer errors. That is fundamentally why I am still pushing for a batteries-included framework for Rust.</p>\n<p>I partnered with Julien, who has been leading the front-end design of Topcoat while I mostly focus on Toasty and the DB layer. We are still figuring out exactly what this new framework should look like, but it is shaping up to be something very nice.</p>\n<p>With that, what is new with Topcoat and Toasty?</p>\n<h1 id=\"client-side-reactivity-in-topcoat-0-9-and-beyond\">Client-side reactivity in Topcoat 0.9 and beyond</h1>\n<p>Today, we published <a href=\"https://github.com/tokio-rs/topcoat/releases/tag/v0.9.0\" rel=\"nofollow ugc noopener\">Topcoat v0.9</a>.</p>\n<p>When starting to build a web framework, you typically choose to either build a browser-side renderer or a server-side renderer. If you start with browser rendering, the challenge becomes getting data from the server to the browser and rendering the initial page as quickly as possible. If you start on the server, data access and initial page load become easy, but now latency and client-side responsiveness become the bottleneck. Regardless of where you start, you typically converge more and more towards the middle to get the best of both worlds. We believe server-side rendering is the best default for most web apps, but we want to make sure you can drop down to fully interactive, zero-latency UIs when needed.</p>\n<h2 id=\"tracking-client-signals-on-the-server\">Tracking client signals on the server</h2>\n<p>Topcoat started with signals and a special runtime expression syntax inside the view! macro: a fully type-checked subset of Rust that can be transpiled to JavaScript and re-run in the browser. The idea is to have as much of the rendering and business logic on the server, and only sprinkle in runtime expressions to bridge the latency gap, for example by revealing a loading spinner or changing some class attributes. With a system like this, it is possible to build basic interactivity while avoiding server roundtrips entirely:</p>\n<pre><code>#[page]\npub async fn page(cx: &amp;Cx) -&gt; Result&lt;impl View&gt; {\n    // This state variable is initialized on the server, but lives in the browser.\n    let count = signal(cx, || 0i32);\n    Ok(view! {\n        &lt;button @click=$(|_e| count.increment())&gt;&quot;increment&quot;&lt;/button&gt;\n        &lt;button @click=$(|_e| count.decrement())&gt;&quot;decrement&quot;&lt;/button&gt;\n        // The count updates when clicking the buttons. No server roundtrip.\n        &lt;div&gt;$(count.get())&lt;/div&gt;\n    })\n}</code></pre>\n<p>However, by themselves, Topcoat’s runtime expressions fall short when you need to make changes to the structure of the markup itself. To fix this, we added shards, which are a special type of component that can be re-rendered on the server whenever their arguments change:</p>\n<pre><code>#[component]\nasync fn search(cx: &amp;Cx) -&gt; Result&lt;impl View&gt; {\n    let query = signal(cx, String::new);\n    Ok(view! {\n        &lt;input @input=$(|e: Event| query.set(e.target.value))&gt;\n        // Search results are updated as the input changes.\n        search_results(query: $(query.get()))\n    })\n}\n#[shard]\nasync fn search_results(cx: &amp;Cx, query: String) -&gt; Result&lt;impl View&gt; {\n    // This markup is rendered on the server and can access the database.\n    Ok(view! {\n        &lt;ul&gt;\n            for product in search_products(cx, &amp;query).await? {\n                &lt;li&gt;(product.name)&lt;/li&gt;\n            }\n        &lt;/ul&gt;\n    })\n}</code></pre>\n<p>When the <code>query</code> signal changes, the browser sends its current value to the\nserver, which reruns the shard and responds with updated HTML.</p>\n<p>In <a href=\"https://github.com/tokio-rs/topcoat/releases/tag/v0.8.0\" rel=\"nofollow ugc noopener\">Topcoat 0.8</a>, reacting to signal changes became even easier. You can now read\na signal while rendering your UI on the server. Since the outcome depends on\nwhatever values the signals have, Topcoat will refetch just those parts of your\npage that track the signal value:</p>\n<pre><code>#[shard]\npub async fn search(cx: &amp;Cx) -&gt; Result&lt;impl View&gt; {\n    let query = signal(cx, String::new);\n    // The signal is read here, meaning the shard will re-run\n    // on the server whenever the browser-side state changes.\n    let products = search_products(cx, &amp;query.get()).await?;\n    Ok(view! {\n        // The input event handler still runs in the browser.\n        &lt;input @input=$(|e: Event| query.set(e.target.value))&gt;\n        &lt;ul&gt;\n            for product in products {\n                &lt;li&gt;(product.name)&lt;/li&gt;\n            }\n        &lt;/ul&gt;\n    })\n}</code></pre>\n<p>Updated HTML elements are morphed to avoid loss of focus or input state. Even an entire page can be re-rendered in response to a signal change, providing significantly more expressiveness.</p>\n<h2 id=\"streaming-ui-changes-from-the-server\">Streaming UI changes from the server</h2>\n<p>Responding to client state changes on the server is great, but what if you want\nto update the UI in response to a server-side state change? For this use case,\nTopcoat provides the <code>live!</code> and <code>emit!</code> macros. A <code>live!</code> view is a special\ntype of view! that can emit unlimited UI updates.</p>\n<p>A simple example is “streaming SSR,” or “suspense.” The goal is to render a\nloading skeleton as quickly as possible while waiting for data. Once the data is\navailable, you can swap in the real page content. Topcoat provides <code>suspense</code> and\n<code>error_boundary</code> components out of the box that behave similarly to\n<a href=\"https://react.dev/reference/react/Suspense\" rel=\"nofollow ugc noopener\">React</a> and other web frameworks. That said, you can achieve a similar\neffect with a live view:</p>\n<pre><code>#[page]\npub async fn page() -&gt; Result&lt;impl View&gt; {\n    Ok(live! {\n        // First, emit a loading indicator.\n        emit! { &lt;p&gt;&quot;Loading...&quot;&lt;/p&gt; }?;\n        // Then, load the data.\n        let content = load_content().await;\n        // Finally, swap in the full UI.\n        emit! { &lt;p&gt;(content)&lt;/p&gt; }\n    })\n}</code></pre>\n<p>A more advanced use case is to emit a progress indicator that updates many times as the page loads:</p>\n<pre><code>#[page]\npub async fn page(cx: &amp;Cx) -&gt; Result&lt;impl View&gt; {\n    Ok(live! {\n        // Start at 0%.\n        emit! { &lt;p&gt;&quot;Working... 0%&quot;&lt;/p&gt; }?;\n        while let Some(progress) = load_more_data(cx).await? {\n            // Each time new data arrives, we update the progress indicator.\n            emit! {\n                &lt;p&gt;\n                    &quot;Working... &quot;\n                    (progress.percent)\n                    &quot;%&quot;\n                &lt;/p&gt;\n            }?;\n        }\n        emit! { &lt;p&gt;&quot;Done!&quot;&lt;/p&gt; }\n    })\n}</code></pre>\n<p>Starting with version 0.9, <a href=\"https://github.com/tokio-rs/topcoat\" rel=\"nofollow ugc noopener\">Topcoat</a> also supports server-push. Instead of\nstreaming only for the initial page load, <a href=\"https://github.com/tokio-rs/topcoat\" rel=\"nofollow ugc noopener\">Topcoat</a> can open a WebSocket\nconnection from the browser to the server and subscribe to UI changes over\nlong-lived connections. This is useful, for example, for a chat interface:</p>\n<pre><code>#[component]\npub async fn chat_messages(cx: &amp;Cx) -&gt; Result&lt;impl View&gt; {\n    Ok(live! {\n        let chat = app_context::&lt;Chat&gt;(cx);\n        let mut changed = chat.subscribe();\n        loop {\n            // Render the current chat state.\n            let token = emit! {\n                &lt;ul&gt;\n                    for message in chat.messages() {\n                        &lt;li&gt;(message)&lt;/li&gt;\n                    }\n                &lt;/ul&gt;\n            }?;\n            // During the initial page load, only render once.\n            if !connected(cx) {\n                break Ok(token);\n            }\n            // Wait for changes, then re-render the chat box.\n            changed.recv().await.ok();\n        }\n    })\n}</code></pre>\n<h1 id=\"toasty-topcoat-s-db-client\">Toasty, Topcoat’s DB client</h1>\n<p><a href=\"https://github.com/tokio-rs/toasty\" rel=\"nofollow ugc noopener\">Toasty</a> has received many incremental updates over the months. I’m just going\nto highlight a few of them quickly.</p>\n<h2 id=\"expressive-updates\">Expressive updates</h2>\n<p>Procedural macros are one of Rust’s killer features. A procedural macro-based\nAPI can be very expressive while still providing type safety. Toasty uses these\nheavily to minimize boilerplate when querying, creating, and updating. We\nrecently added the <code>update!</code> macro:</p>\n<pre><code>#[derive(Model)]\nstruct User {\n    #[key]\n    #[auto]\n    id: i64,\n    name: String,\n    login_count: i64,\n}\n// let mut user = ...;\ntoasty::update!(user {\n    name: &quot;Alicia&quot;,\n    login_count.increment(),\n})\n.exec(&amp;mut db)\n.await?;</code></pre>\n<p>This runs a single update that sets the name field and increments <code>login_count</code>\nin the database (without loading it first, something like <code>SET login_count = login_count + 1</code>).</p>\n<h2 id=\"document-fields\">Document fields</h2>\n<p>Toasty is not just for relational data. Document-based databases are common, and most relational databases (including PostgreSQL) support document types, like JSON, including the ability to query document columns. Toasty is adding first-class support for that. Here is a quick example:</p>\n<pre><code>#[derive(Model)]\nstruct User {\n    #[key]\n    #[auto]\n    id: i64,\n    name: String,\n    #[document]\n    settings: Settings,\n}\n#[derive(Embed)]\nstruct Settings {\n    theme: String,\n    notifications: bool,\n}\nlet users = User::filter(\n    User::fields().settings().theme().eq(&quot;dark&quot;),\n)\n.exec(&amp;mut db)\n.await?;</code></pre>\n<p>The user’s <code>settings</code> field is encoded as JSONB in PostgreSQL, and the filter\nuses PostgreSQL’s JSONB filtering capabilities. The filter query looks like\nthis:</p>\n<pre><code>SELECT\n    users.id,\n    users.name,\n    users.settings\nFROM users\nWHERE users.settings-&gt;&gt;&#39;theme&#39; = $1;</code></pre>\n<h2 id=\"polymorphic-relations\">Polymorphic relations</h2>\n<p>Polymorphic relations are relations where the target may be one of multiple types. I have never loved how they worked in ORMs that I have used in the past. Yet, there are valid reasons to need them. Then, I realized that you could basically get polymorphic relations with Toasty by combining enums with regular relations.</p>\n<pre><code>#[derive(Embed)]\n#[index(id)]\nenum Owner {\n    User {\n        #[shared(id)]\n        id: i64,\n        #[belongs_to(key = id)]\n        user: Deferred&lt;User&gt;,\n    },\n    Team {\n        #[shared(id)]\n        id: i64,\n        #[belongs_to(key = id)]\n        team: Deferred&lt;Team&gt;,\n    },\n}\n#[derive(Model)]\nstruct Project {\n    #[key]\n    #[auto]\n    id: i64,\n    name: String,\n    owner: Owner,\n}</code></pre>\n<p>The <code>#[shared(id)]</code> and <code>#[index(id)]</code> annotations tell Toasty what the table schema\nlooks like. shared says the id field from both enum variants map to the same DB\ncolumn, and <code>#[index]</code> says to create a database index. And using it works pretty\nwell too:</p>\n<pre><code>// Toasty fills the owner&#39;s ID from Alice&#39;s primary key.\nlet mut project = toasty::create!(Project {\n    name: &quot;Website&quot;,\n    owner: Owner::User { user: &amp;alice },\n})\n.exec(&amp;mut db)\n.await?;\n// Find projects owned by Alice.\nlet projects = Project::filter(\n    Project::fields()\n        .owner()\n        .user()\n        .matches(|owner| owner.user().eq(&amp;alice)),\n)\n.exec(&amp;mut db)\n.await?;\n// Transfer ownership to a team.\ntoasty::update!(project {\n    owner: Owner::Team { team: &amp;team },\n})\n.exec(&amp;mut db)\n.await?;</code></pre>\n<h1 id=\"where-to-go-from-here\">Where to go from here</h1>\n<p>Look, I don’t know where we are going. The world of software engineering is completely different every three months. Who knows where it will land? I sure as hell don’t. What I do know is that I am very excited. I feel like I am living through science fiction, and the future is bright. Is Rust going to be part of the destination? Maybe. Maybe not. It will definietly play a big part in the journey. There is no reason we can’t maximize productivity AND have a really high-quality app that runs fast and uses little memory.</p>\n<p>And for web apps, I will be building with Rust, <a href=\"https://github.com/tokio-rs/topcoat\" rel=\"nofollow ugc noopener\">Topcoat</a>, and <a href=\"https://github.com/tokio-rs/toasty\" rel=\"nofollow ugc noopener\">Toasty</a>.</p>\n<p>To give it a try, follow the <a href=\"https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat/docs/getting_started.md\" rel=\"nofollow ugc noopener\">getting started guide</a>, and come\nsay hi in the #topcoat channel on the Tokio <a href=\"https://discord.gg/tokio\" rel=\"nofollow ugc noopener\">Discord</a>.</p>","headings":[{"level":1,"text":"Topcoat is pushing the boundary of server applications with Rust","id":"topcoat-is-pushing-the-boundary-of-server-applications-with-rust"},{"level":1,"text":"Client-side reactivity in Topcoat 0.9 and beyond","id":"client-side-reactivity-in-topcoat-0-9-and-beyond"},{"level":2,"text":"Tracking client signals on the server","id":"tracking-client-signals-on-the-server"},{"level":2,"text":"Streaming UI changes from the server","id":"streaming-ui-changes-from-the-server"},{"level":1,"text":"Toasty, Topcoat’s DB client","id":"toasty-topcoat-s-db-client"},{"level":2,"text":"Expressive updates","id":"expressive-updates"},{"level":2,"text":"Document fields","id":"document-fields"},{"level":2,"text":"Polymorphic relations","id":"polymorphic-relations"},{"level":1,"text":"Where to go from here","id":"where-to-go-from-here"}]}}