21 Oct 24

Rust, WebAssembly, and Yew

I’ve been working on a project that compiles a Rust app, that utilizes the Yew framework, into WebAssembly. According to cloc, It has a little over 37,000 lines of Rust code.

I don’t know where that places it in size relative to other Rust apps that target WebAssembly, but I think it is large enough to credit the team with some experience. It’s been developed over more than a year too. It’s not a simple “hello world” app.

It utilizes an API for the backend (also written in Rust). It has an admin section for common CRUD and management.

After working on it, there have been some “ah ha” moments. I’ll detail a few below that others working on Yew apps might benefit from.

A little background: Yew borrows ideas from React, so the patterns are familiar for teams moving from JavaScript-based apps to those targetting WebAssembly—mainly Components. I’ve documented the structure in the README to help others onboard. It’s similar to the following:

Conventions (over configuration)

Below are the common objects and files that are used in CRUD functionality:

domain objects:

  • found in /domains

  • contains the definition of CRUD objects

  • file names are plural

  • for example: UserNew is the object that contains the attributes of a new user collected via a form

service objects:

  • found in /services

  • contains the definition of CRUD methods that interact with the other services (aka API calls for objects)

  • file names are plural

  • for example: making the API call to create a new assessment

views:

  • found in /views

  • contains the component/markup responses that correspond with a route

  • CRUD views for objects are stored in subdirectories representing the objects

  • file names reflect the CRUD action (e.g. read.rs and update.rs)

  • subcomponents that may or may not be shared amongst views are stored in a subdirectory called partials

  • for example: view/users/read is the page for viewing the details of a user

components:

  • found in /components

  • contains the definition of common and reusable markup responses

  • file names reflect the concept of the component (e.g. table.rs or error_message.rs)

  • for example: all of the list views utilize the table component to render the table of objects

hooks:

  • found in /hooks

  • contains the definition of common and reusable browser actions

  • file names reflect the name of the action/callback (e.g. use_logout)

I’m sure it will evolve and we’ll probably change our minds or at the very least make some exceptions. Still, the point was to establish a pattern that works well enough and covers the majority of use cases so that engineers onboarding onto the project would know where to look and how to contribute.

Like Rust and WebAssembly, Yew has a learning curve. Learning details such as preferring a context over passing props comes from some experience. Deciding not to shove everything in a component file requires the same. Over the next few months, I’ll be posting about specifics in regards to Yew.

Music companion of this post: Alone - The Cure

What I am working on currently: Besides the mentioned Yew app, I’m introducing Webhooks to Schemabook.