{{announcement.body}}
{{announcement.title}}

Angular, Rust, WebAssembly, Node.js, Serverless, and the New Azure Static Web Apps!

DZone 's Guide to

Angular, Rust, WebAssembly, Node.js, Serverless, and the New Azure Static Web Apps!

Learn how to take a front-end Angular app, a backend API written in Rust and compiled to Web Assembly, and deploy it to a Serverless environment with Azure Static Web Apps service.

· Web Dev Zone ·
Free Resource

In this guide, we are going to see how to take a front-end Angular app, a backend API written in Rust and compiled to Web Assembly, and deploy everything to a Serverless environment on the new Azure Static Web Apps service.

While the UI is written with Angular. The interesting part about this app is that the generator, the core part of the backend API, is entirely written in Rust, and then compiled to Web Assembly (or WASM for short). The public API is exposed behind a Node.js façade through a serverless Azure Function.

Let’s start.

What Are We Creating?

We are going to build a Cat names generator app. I love cats, I bet you do too. This app allows you to discover unique cat names for your loved pet.

Try the app live at https://catsify.app.

Our app structure is the following (showing only the important parts):

Plain Text
 




x
15


 
1
.
2
├── api
3
   ├── dist
4
      └── func
5
   ├── func
6
   ├── ...
7
   ├── Cargo.toml
8
   └── rust
9
├── app
10
   ├── dist
11
      └── ui
12
   ├── ...
13
   └── src
14
├── ...
15
└── scripts


Some highlights:

  • api is a standard Azure Functions App folder.
  • api/func contains the Node.js serverless function.
  • api/rust contains the Rust source code.
  • app contains the Angular source code.

And…

  • api/dist/func contains the API build artifacts.
  • app/dist/ui contains the APP build artifacts.

We will next describe the role of each stack: Rust/WASM, Node.js. Angular; and then explain how each part is built and deployed.

How Are We Creating It?

Azure Functions: The Node.js Code

Our public backend API is a Node.js Azure Function that acts as a Façade.

In the ./api/func/index.ts file, we simply import and invoke “a” generate() function (see next section), get and send back the result to the client.

Here is a simplified version of the code:

JavaScript
 




xxxxxxxxxx
1
14


 
1
const { generate } = require("./wasm_loader");
2
 
          
3
const func = async function (context, req) {
4
   const name = await generate();
5
   const [adjective, noun] = name.split(" ");
6
   context.res = {
7
     body: {
8
       adjective,
9
       noun,
10
     },
11
   };
12
};
13
 
          
14
export default func;


However, in the ./api/func/wasm_loader.ts file, this is where the magic happens, we actually load the WASM module compiled from Rust (see the Rust story), invokes the generate_name_str function, passing in a seed argument, and decode the resulted string output.

Here is a simplified version of the code:

JavaScript
 




xxxxxxxxxx
1
34


 
1
const fs = require('fs');
2
const path = require('path');
3
 
          
4
// the WASM file is copied to dis/func during the build
5
const wasmFile = path.join(__dirname, 'generator.wasm');
6
 
          
7
// a bunch of utilities to decode the WASM binary
8
function getInt32Memory(wasm) {...}
9
function getUint8Memory(wasm) {...}
10
function getStringFromWasm(wasm, ptr, len) {...}
11
 
          
12
// export a JavaScript function 
13
export const generate = async function() {
14
 
          
15
  // load the WASM module
16
  const bytes = new Uint8Array(fs.readFileSync(wasmFile));
17
  const result = await WebAssembly.instantiate(bytes);
18
  const wasm = await Promise.resolve(result.instance.exports);
19
 
          
20
  // setup args
21
  const retptr = 8;
22
  const seed = Date.now() % 1000 | 0;
23
 
          
24
  // invoke the WASM code
25
  const ret = wasm.generate_name_str(retptr, seed);
26
 
          
27
  // decode result
28
  const memi32 = getInt32Memory(wasm);
29
  const v0 = getStringFromWasm(...);
30
  wasm.__wbindgen_free(...);
31
 
          
32
  // this is final the decoded name
33
  return v0;
34
};


The Core API: The Rust Code

This is NOT a deep dive guide into Rust code or WASM. You can learn about Rust and WASM on the official Rust website.

As I mentioned earlier, the main part of the backend API is the names generator which is written in Rust.

Rust
 




xxxxxxxxxx
1
24


 
1
use wasm_bindgen::prelude::*;
2
#[wasm_bindgen]
3
pub fn generate_name_str(seed: i32) -> String {
4
    // the seed is coming from the JS side
5
    let a = seed % (ADJECTIVES.len() as i32);
6
    let b = seed % (NOUNS.len() as i32);
7
    [ADJECTIVES[a as usize].to_string(), " ".to_string(), NOUNS[b as usize].to_string()].join("")
8
}
9
 
          
10
const ADJECTIVES: [&str; 1116] = [
11
"aback",
12
"abaft",
13
//...
14
];
15
const NOUNS: [&str; 1201] = [
16
"abbey",
17
"abbie",
18
//...
19
];
20
 
          
21
// used for debugging only
22
pub fn main() {
23
    println!("{:?}", generate_name_str(1));
24
}


Without going into many details, we basically use two Rust vectors to store the nouns and adjectives of the cats, then we construct a string by choosing a random value from each vector. The generate_name_str function is exported from Rust to JavaScript using the #[wasm_bindgen] outer attribute. This will enable us to call this function from the JavaScript code, and pass in the seed argument.

The UI: The Angular Code

The Angular app was generated with Angular CLI version 10.0.0-next.4. A classic setup!

How Are We Building It?

Azure Function

Our Node.js Azure Function Node.js code is written in TypeScript, hence we are using tsc to transpile into JavaScript and output the result in the ./dist/func folder.

Shell
 




xxxxxxxxxx
1


 
1
cd api
2
tsc # will create ./dist/func/


Rust to WASM

For the Rust code, in order to compile it and generate the WASM module, we use the [wasm-pack](https://github.com/rustwasm/wasm-pack):

Shell
 




xxxxxxxxxx
1


 
1
cd api
2
wasm-pack build # will create ./pkg/


And the following configuration in api/Cargo.toml:

TOML
 




xxxxxxxxxx
1
16


 
1
[dependencies]
2
wasm-bindgen = "0.2.58"
3
 
          
4
[lib]
5
crate-type = ["cdylib", "rlib"]
6
path = "rust/lib.rs"
7
 
          
8
[[bin]]
9
name = "generator"
10
path = "rust/lib.rs"
11
 
          
12
[profile.release]
13
lto = true
14
panic = "abort"
15
# Tell `rustc` to optimize for small code size.
16
opt-level = "s"


Note: wasm-pack generates a Node.js wrapper that loads the WASM module. Since we wrote our own loader, we are only interested in the *.wasm file. We will then need to copy the WASM file to the ./api/dist/func:

Shell
 




xxxxxxxxxx
1


 
1
cp pkg/catsify_bg.wasm dist/func/generator.wasm
2
 
          


The Angular build

Building the Angular app is straightforward. The Angular CLI takes care of everything:

Shell
 




xxxxxxxxxx
1


1
ng build --prod


This command will generate the app bundle under ./app/dist/ui.

Project Build Recap

Shell
 




xxxxxxxxxx
1


 
1
cd api
2
tsc                # builds the Azure function.
3
wasm-pack build    # builds the WASM module.
4
cp pkg/catsify_bg.wasm \
5
   dist/func/generator.wasm
6
cd ../app
7
ng build --prod    # builds the front-end app.


Now that we have created a front-end Angular app, and a backend serverless API, what’s the easiest to static serverless app on Azure?

Introducing: Azure Static Web Apps!

Static Web Apps is a new offering from Azure App Service. It’s a new, simplified hosting option for modern web apps powered by serverless APIs.

Static Web Apps

Static Web Apps offers:

  • Free web hosting for static content like HTML, CSS, JavaScript, and images.
  • Integrated API support provided by Azure Functions.
  • First-party GitHub integration where repository changes trigger builds and deployments.
  • Globally distributed static content, putting content closer to your users.
  • Free SSL certificates, which are automatically renewed.
  • Custom domains to provide branded customizations to your app.
  • Seamless security model with a reverse-proxy when calling APIs, which requires no CORS configuration.
  • Authentication provider integrations with Azure Active Directory, Facebook, Google, GitHub, and Twitter.
  • Customizable authorization role definition and assignments.
  • Back-end routing rules enabling full control over the content and routes you serve.
  • Generated staging versions powered by pull requests enabling preview versions of your site before publishing.

Let’s Deploy Our App, in 3 Steps!

Connect the GitHub account

Connect GitHub account

Provide the Build Information

Alt Text

Watch GitHub Build and Deploy the App on Azure Static Web Apps

GitHub deployment

Note: The YML workflow file created by Azure Static Web Apps has been updated with custom build steps.

Try It

Resources

Topics:
angular, azure, nodejs, serverless, web assembly

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}