Angular, Rust, WebAssembly, Node.js, Serverless, and... the NEW Azure Static Web Apps! 🎉
Tags: javascript, beginners, webdev, staticwebapps
Published: 2020-05-19 17:54:12

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](https://docs.microsoft.com/azure/static-web-apps/?WT.mc_id=javascript-19992-wachegha) service.

While the UI is written with [Angular](https://angular.io). 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):

```
.
├── api
│   ├── dist
│   │   └── func
│   ├── func
│   ├── ...
│   ├── Cargo.toml
│   └── rust
├── app
│   ├── dist
│   │   └── ui
│   ├── ...
│   └── src
├── ...
└── 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](https://en.wikipedia.org/wiki/Facade_pattern). 

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
const { generate } = require("./wasm_loader");

const func = async function (context, req) {
   const name = await generate();
   const [adjective, noun] = name.split(" ");
   context.res = {
     body: {
       adjective,
       noun,
     },
   };
};

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
const fs = require('fs');
const path = require('path');

// the WASM file is copied to dis/func during the build
const wasmFile = path.join(__dirname, 'generator.wasm');

// a bunch of utilities to decode the WASM binary
function getInt32Memory(wasm) {...}
function getUint8Memory(wasm) {...}
function getStringFromWasm(wasm, ptr, len) {...}

// export a JavaScript function 
export const generate = async function() {

  // load the WASM module
  const bytes = new Uint8Array(fs.readFileSync(wasmFile));
  const result = await WebAssembly.instantiate(bytes);
  const wasm = await Promise.resolve(result.instance.exports);

  // setup args
  const retptr = 8;
  const seed = Date.now() % 1000 | 0;

  // invoke the WASM code
  const ret = wasm.generate_name_str(retptr, seed);

  // decode result
  const memi32 = getInt32Memory(wasm);
  const v0 = getStringFromWasm(...);
  wasm.__wbindgen_free(...);

  // this is final the decoded name
  return v0;
};
```

### 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](https://www.rust-lang.org/learn).

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

```rust
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn generate_name_str(seed: i32) -> String {
    // the seed is coming from the JS side
    let a = seed % (ADJECTIVES.len() as i32);
    let b = seed % (NOUNS.len() as i32);
    [ADJECTIVES[a as usize].to_string(), " ".to_string(), NOUNS[b as usize].to_string()].join("")
}

const ADJECTIVES: [&str; 1116] = [
"aback",
"abaft",
//...
];
const NOUNS: [&str; 1201] = [
"abbey",
"abbie",
//...
];

// used for debugging only
pub fn main() {
    println!("{:?}", generate_name_str(1));
}
```

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]`](https://github.com/rustwasm/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](https://github.com/angular/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.

```bash
cd api
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)`:

```bash
cd api
wasm-pack build # will create ./pkg/
```
And the following configuration in `api/Cargo.toml`:

```toml
[dependencies]
wasm-bindgen = "0.2.58"

[lib]
crate-type = ["cdylib", "rlib"]
path = "rust/lib.rs"

[[bin]]
name = "generator"
path = "rust/lib.rs"

[profile.release]
lto = true
panic = "abort"
# Tell `rustc` to optimize for small code size.
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`:
```bash
cp pkg/catsify_bg.wasm dist/func/generator.wasm
```

### The Angular build

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

```bash
ng build --prod
```

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

### Project Build Recap

```bash
cd api
tsc                # builds the Azure function.
wasm-pack build    # builds the WASM module.
cp pkg/catsify_bg.wasm \
   dist/func/generator.wasm
cd ../app
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](https://docs.microsoft.com/azure/static-web-apps/?WT.mc_id=javascript-19992-wachegha)! 🎉

[Static Web Apps](https://docs.microsoft.com/azure/static-web-apps/?WT.mc_id=javascript-19992-wachegha) is a new offering from Azure App Service. It's a new, simplified hosting option for modern web apps powered by serverless APIs.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/u8f592oztv5hxga57qwd.png)

[Static Web Apps](https://docs.microsoft.com/azure/static-web-apps/?WT.mc_id=javascript-19992-wachegha) offers:
- Free web hosting for static content like HTML, CSS, JavaScript, and images.
- Integrated [API](https://docs.microsoft.com/azure/static-web-apps/add-api?WT.mc_id=javascript-19992-wachegha) 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](https://docs.microsoft.com/azure/static-web-apps/custom-domain?WT.mc_id=javascript-19992-wachegha) to provide branded customizations to your app.
- Seamless security model with a reverse-proxy when calling APIs, which requires no CORS configuration.
- [Authentication](https://docs.microsoft.com/azure/static-web-apps/authentication-authorization?WT.mc_id=javascript-19992-wachegha) provider integrations with Azure Active Directory, Facebook, Google, GitHub, and Twitter.
- Customizable [authorization](https://docs.microsoft.com/azure/static-web-apps/user-information?WT.mc_id=javascript-19992-wachegha) role definition and assignments.
- Back-end [routing rules](https://docs.microsoft.com/azure/static-web-apps/routes?WT.mc_id=javascript-19992-wachegha) enabling full control over the content and routes you serve.
- Generated staging versions powered by [pull requests enabling preview versions](https://docs.microsoft.com/en-us/azure/static-web-apps/review-publish-pull-requests?WT.mc_id=javascript-19992-wachegha) of your site before publishing.

## Let's deploy our app, in 3 steps!

### Connect the GitHub account

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/6ux5nmo8ejkxtyvxxruq.png)

### Provide the build information

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/md2ak7xryrjyacr5phy0.png)


### Watch GitHub build and Deploy the app on Azure Static Web Apps 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/54pcsmxuf3kimvgbyggk.png)

> Note: The [YML workflow](https://github.com/manekinekko/catsify/blob/master/.github/workflows/azure-static-web-apps-lively-ground-0f97a8a1e.yml) file created by Azure Static Web Apps has been updated with custom build steps.

# Try it

- Visit the [Static Web Apps quickstart](https://docs.microsoft.com/en-us/azure/static-web-apps/getting-started?WT.mc_id=javascript-19992-wachegha) to build and deploy your first static web app in minutes.
- See the [Static Web Apps documentation](http://aka.ms/swadocs?WT.mc_id=javascript-19992-wachegha) for more info about Static Web Apps.
Follow the guided learning paths in Microsoft Learn for [creating and publishing an Angular, React, Svelte, or Vue JavaScript app](https://aka.ms/swaframeworks?WT.mc_id=javascript-19992-wachegha) and API or for [creating and publishing an app with the Gatsby static site generator](https://aka.ms/staticsitegen?WT.mc_id=javascript-19992-wachegha).
 
# Resources

- [Azure Static Web Apps documentation](https://docs.microsoft.com/en-us/azure/static-web-apps/?WT.mc_id=javascript-19992-wachegha)
- [Azure Functions documentation](https://docs.microsoft.com/azure/azure-functions/?WT.mc_id=javascript-19992-wachegha)
- [Create your first function using Visual Studio Code](https://docs.microsoft.com/azure/azure-functions/functions-create-first-function-vs-code?WT.mc_id=javascript-19992-wachegha)
- [Try Azure For Free](https://azure.microsoft.com/free?wt.mc_id=javascript-19992-wachegha)

{% github manekinekko/catsify %}