← All transcripts

I was wrong about Rust on the web Transcript, AI Summary & Key Points

Dreams of Code · 4 days ago · Science & Technology · 12:40 · EN

Watch on YouTube

Answer

Rust on the web was starting to look good, but not because of the frameworks and reasons previously expected; Topcoat changed the approach to building Rust web applications.

AI Summary

Topcoat changes Rust web development through a server-side-first approach that avoids WebAssembly for client interactivity. It compiles supported Rust runtime expressions into JavaScript, provides raw JavaScript escape hatches, and connects client code to server-side procedures that automatically generate HTTP endpoints and client request code. Shards allow isolated server-rendered HTML updates, while server signals, request coalescing, aborted stale requests, and memoisation reduce unnecessary work. Topcoat also includes server-side rendering, API endpoints, a shadcn-inspired component library, Tailwind CSS support, module-based routing, asset bundling, a CLI, and live UI updates. The framework remains experimental and may undergo breaking changes, so production use is discouraged for now.

Key Points

  • Topcoat provides server-side rendering, API endpoints, a shadcn-inspired component library, built-in Tailwind CSS support, and client-side interactivity with little or no boilerplate.
  • Topcoat renders markup and evaluates runtime expressions on the server, then compiles those expressions into JavaScript for the client instead of using WebAssembly for interactivity.
  • Topcoat's JavaScript compilation simplifies interaction with browser APIs and JavaScript libraries through a raw macro that permits JavaScript inside an expression.
  • Runtime expressions support only a limited subset of Rust syntax, making them better suited to simpler client-side interactivity.
  • Procedures let client runtime expressions invoke asynchronous server-side Rust functions, and Topcoat automatically generates the corresponding HTTP endpoint and client request code.
  • Neon database branches can provide isolated data for demos, review apps, development environments, and production-data debugging without affecting production data.
  • Topcoat's app context stores values that outlive a single request, such as a database pool, and request context exposes HTTP request information, server-readable signals, and app-context values to server handlers.
  • Shards render HTML on the server and merge it into the DOM when their input expressions or server-read signals change.

Tools & resources

3 items

PNo. 2017
AIAINotes.us Tool

PostgreSQL

postgresql.org

PostgreSQL is an open-source relational database management system maintained by the PostgreSQL Global Development Group. It provides SQL-compliant, ACID transactions, MVCC concurrency, and extensibility through custom types, functions and extensions, and is used for general-purpose and enterprise database workloads.

Mentioned in
8 videos
Kind
Other
RNo. 0706
AIAINotes.us Tool

Rust

Open source · rust-lang/rust

Rust is a systems programming language and toolchain developed by the Rust project. Its compiler, standard library, documentation, package manager and build tool Cargo, formatter rustfmt, linter Clippy, and editor support are maintained in the main repository. The language uses a rich type system and ownership model to enforce memory and thread safety at compile time, while targeting fast, memory-efficient software for critical services, embedded devices, and integration with other languages. Rust is primarily distributed under the MIT and Apache 2.0 licenses, with portions under BSD-like licenses.

Mentioned in
11 videos
Kind
Other
TNo. 4518
AIAINotes.us Tool

Topcoat

Open source · tokio-rs/topcoat

Topcoat is a modular, batteries-included Rust framework for building full-stack web applications with server-side rendering, components, API routes, Tailwind CSS support, asset handling, and client-side interactivity. Its `view!` macro combines HTML-like templates with Rust control flow, while components can be asynchronous and access server-side resources such as databases directly. Topcoat evaluates `$()` expressions on the server for the initial render and translates them to JavaScript for browser-side reactivity without a WebAssembly bundle or separate client build step. Server-dependent updates can be implemented with `#[shard]` components, which re-render on the server when their arguments change and replace the resulting HTML in place. It also provides signals, event handlers, server procedures, request-context and per-request memoization, live updates, module-based route discovery, cookies and sessions, a CLI formatter, vendored editable UI components, and an asset bundler. The repository describes it as early-stage and experimental, with breaking changes expected.

Mentioned in
1 video
Kind
Other

Links mentioned

🔒 Full analysis locked

Unlock more videos and the full analysis

A credit unlocks one video's full analysis for good — the build steps, the tools and how each was used, the methods behind every use case. Pro opens the whole library instead, and raises how many videos you can analyse a day.

Unlock full analysis — free

Transcript

Searchable transcript of I was wrong about Rust on the web — Dreams of Code (12:40). Search for a phrase, then click its timestamp to jump straight to that moment in the video.

Captions sourced from the original video on YouTube, published by Dreams of Code. The video, its captions and all related intellectual property remain the property of their respective owners; AINotes claims no ownership. Provided for research, accessibility and search — see the Transcript Notice and Copyright Policy.

00:00 Earlier this year, I made a video about Rust on the web, specifically how it was starting to look quite good. In that video, I covered a number of reasons why I felt this was the case, as well as giving an overview on some of the popular web frameworks I was most excited about. Little did I know at the time, however, I was wrong. Whilst it was true that Rust on the Web was starting to look quite good, it wasn't because of any of the reasons that I thought in that video.

00:26 Instead, it was because just a couple of months later, something was going to change the way that I built web applications using Rust, a new web framework. So, the first big question is what makes Topcoat different? On the surface, it looks like a typical modern web framework. It has serverside rendering, the ability to create API endpoints, a shad CN inspired component library, built-in support for Tailwind CSS, and clientside interactivity.

00:58 Even the code itself is very similar to other Rust web frameworks. It has the exact same concept of component functions, a view macro for defining HTML, and the same signal-based abstraction for managing and reacting to client states. So, what exactly makes Top Coat different? Well, it's because of how these features are implemented in a way that requires little or no boilerplate.

01:21 For example, client side interactivity. When it comes to most other Rust web frameworks, this is achieved through the use of web assembly. And whilst it is incredibly capable, it does come with a number of caveats. Top coat is different as it doesn't use web assembly for its interactivity. Instead, it takes two separate approaches. One for the server and one for the client.

01:38 On the server, it renders all markup and evaluates any runtime expressions for the initial render. On the client, in order to run those same expressions, it takes an approach that's a little more unusual. It compiles the Rust expressions into JavaScript. This not only gives TopCat a much simpler clientside runtime model, but it also allows it to bypass some of the friction that comes with using web assembly.

02:00 For example, working with browser APIs and JavaScript libraries. This is a requirement for any non-trivial web application and when using WAM it can be a pain in the ass. Once again, top coat is different as it provides an escape hatch to write raw JavaScript inside of an expression using the raw macro which makes it incredibly simple to interact with both browser APIs and your favorite JavaScript libraries.

02:22 Whilst this approach of compiling RS into JavaScript makes client side interactivity much simpler than with web assembly, it doesn't come without compromise. In order to effectively compile into JavaScript, runtime expressions only support a limited subset of Rust syntax. On first glance, this may give the impression that the framework is incapable of handling more complex interactivity, but that isn't actually the case.

02:47 Whilst it's true that runtime expressions are better suited towards more simple client side interactivity, this is in fact an intentional design decision. Instead, when it comes to implementing anything more complex, that is where the core paradigm of Top Coat comes into play. When it comes to web development, I've typically gravitated towards more serverside solutions.

03:11 For example, the initial version of my course websites was built using the Goth stack. Therefore, I find Top Code to be rather appealing, especially as it provides some rather elegant solutions for integrating the server with the client. One such solution is procedures which allow you to invoke serverside code from the client in a way that feels incredibly seamless.

03:31 To create a procedure, you begin by defining an async function with the code you want to run on the server. Then by adding the procedure attribute, it can be called from a runtime expression as if it were an ordinary async function in Rust. Now I'm able to invoke my server code from the client in a way that feels rather refined. By creating a procedure, TopCode automatically generates both the HTTP endpoint and the client request code for you, making it simple to invoke tasks that are better suited to run on the

04:07 server, like writing data to a database. Exactly. Although in order to do that, it requires a couple of things. The first of which being a database instance. Fortunately, I'm able to obtain one pretty much instantly using my Postgre provider and the sponsor of today's video, Neon. To demonstrate integrating with the database, I've already set up a Neon project with the relevant schema.

04:27 So, I'm going to go ahead and use the Neon CLI to check out a new branch of this data, which will automatically add the connection URL to my EMV file for me. This ability to branch data in Neon is not only incredibly useful when it comes to demos, but it also works well for things like review apps, development environments, and debugging production data without affecting prod.

04:46 In addition to database branching, other useful features that Neon provides include automatic backups, point in time recovery, and data anonymization. All of which I found to be an essential part of running a production application, and a few of the many reasons that I've been using Neon now for the past 2 years. All of these features are available on the free plan of Neon, which you can sign up for today by using my link in the description down below.

05:10 A big thank you to Neon for sponsoring this video. With the connection to my Neon Postgres instance created, the next step is to make it available to my procedure in top coat. This is achieved by registering the value of the resource we want to share to the router's app context. If you've ever used Axim before, this is very similar to registering values using the width state method.

05:32 And that's not a coincidence as both axom and top coat belong to the same GitHub or that probably explains why I like both projects. As for the app context, this is used for storing any values that need to outlive a single request, such as a database pool, so that they can then be read by any serverside handler, such as a procedure. In order to access a registered value, however, it requires first defining the following parameter inside of your functions arguments.

05:57 This parameter represents the request context, which Top Coat will automatically pass in for any server handler that defines it. The request context is used for three different purposes. The first of which is for obtaining information about the underlying HTTP request, such as its headers, the path, or the HTTP method. The second is for creating a signal which takes a request context as its first argument and a closure as its second.

06:22 This gives a signal the ability to be read on the server which is something we'll cover more shortly. The final purpose is for reading values from the app context which is achieved by passing it to the following function. One thing worth mentioning is that the app context uses Rust's type system when it comes to keying values. Which means in order to pull out our database connection, we just need to explicitly set the type of the variable that we're binding it to.

06:44 Now with the database connection available, we can write the following query to persist the input value of the procedure to the database. And with that, we should be good to go. In addition to procedures, the request context is available to other server handlers that top code provides such as pages, layouts, routes, and also components, which when coming from a clientside framework feels a little unusual.

07:16 In addition to components, top coat provides another unusual serverside resource, or at least for those who haven't used HTMX. Rather than explaining what this resource is, I thought it would be easier to demonstrate it with the following example. If I go ahead and type in a query into this form, you can see it automatically updates the HTML with results from that query.

07:36 In a typical front-end framework, this is achieved through clientside rendering. However, when it comes to top coat, this is done entirely on the server through the use of a shard. As for how to define a shard, well, it's very similar to a procedure where you have an async function with any arguments you want to be passed in, which also returns a result.

07:54 There are a few key differences, however. The first of which is that it's annotated using the shard attribute and the second is that it returns an HTML view. The biggest difference however is when it comes to actually calling this function rather than calling it inside of an expression. It's instead embedded into a view with each of the arguments wrapped in their own expression.

08:12 This allows Top Coat to react whenever the value of a signal associated with one of these expressions changes. When it does so, Top Code will automatically send a request to the shard function, which will render the HTML on the server and then merges it into the DOM. In addition to input arguments, a shard can also rerender on the server whenever the value of one of its internally defined signals changes.

08:34 For this to happen, the signal needs to be read outside of a runtime expression, which means it's read by the server. This is yet another feature that Topco provides. One that isn't limited only to shards as signals can be read by other server handler types such as a component, a layout, or even a page. For example, here I have a page handler with a signal called query which is being read by the server on the line underneath.

08:59 Whenever the value of this signal changes, the page will rerender on the server and display the updated results just like we saw in our previous shard example. So what's the point of using a shard then? Well, whilst server red signals do provide similar reactivity with other handler types, doing so causes the entire page to rerender on the server before it's merged into the client.

09:20 Shards, on the other hand, will rerender in isolation due to the fact they have their own HTTP endpoint. This means that by using a shard, you're able to narrow down the part of the page that is rerendered in response to a signal change, effectively making a shard an optimization. In both cases, however, it's worth remembering that rerendering on the server does involve a server round trip, which means latency.

09:44 When it comes to server reactive web frameworks, this can result in multiple inflight requests at the same time, especially when client state is updated faster than the server can respond. Fortunately, Top Code handles this out of the box. First by coalesing multiple signal changes during a single tick and then by aborting any previous in-flight requests.

10:03 By doing so, it ensures that only the latest arguments win. Because of this, it means that by using both server red signals and shards as well as procedures, Topcoat allows you to create interactive web applications without the need for a heavy front-end framework, which really speaks to Top Coat's serverside first philosophy. A philosophy that's carried over to this next feature, one that also shows how elegant Top Coat can be.

10:31 This is a feature that's found in a couple of other modern frameworks. One that is used to improve the performance of any request that may hit the same resource multiple times. To show an example of this, here I have a simple function called get user by ID which performs a database query. This function is called twice by the following procedure. Once directly on line 20 and once indirectly through the check or function.

10:53 If I go ahead and invoke this procedure, you can see in the terminal that the database is queried twice. Whilst this behavior is expected, it's certainly not desired. Instead, it would be better to query the database once and reuse that result for the duration of the request. This is a very common problem when it comes to back-end web development. And whilst there are quite a few solutions to this, there's none quite as elegant as how TopCode does it.

11:16 If I go ahead and add in the following attribute of memoise to my get user by ID function and then execute the procedure again, you can see this time the database is only hit once, improving our procedures performance with just a single line of code. The memoise attribute can be added to any function that has the request context as one of its parameters and by doing so top coach will cache the result of that function for the duration of a single request keyed by its arguments.

11:40 Additionally, it can be added to both synchronous and asynchronous functions and provides support for singleflight request handling out of the box. For myself, the memorization feature is a clear example of how elegant Top Coat intends to be. And it's not the only feature that highlights this, with other examples being module-based routing, asset bundling, the Top Coat CLI, and the recently released live feature, which allows the server to push UI updates directly to the client.

12:06 As for the future of the project, well, it's worth remembering that Top Coat is still very much in an experimental state, which means there's going to be breaking changes as it continues to evolve. Therefore, it's probably not a good idea to use Top Coat for any production web application just yet. Unless, of course, you happen to be a degenerate Rust enjoyer and you're happy to fix any issues you'll encounter.