Node.js with TypeScriptでWeb Assembly使ってみる



## はじめに
バックエンドJS(Node.js)でWASM(Web Assembly)の使い方を説明する日本語記事が少なさそうだったので書いてみます。
Rustのインストール方法等は省略します。わからない場合は参考先をご確認ください。

バックエンドならそのままGoやRustで書けばいいのでは?と思うかもしれません。
それができる体制であれば、その方がいいと思います。
個人的には少人数でプロダクトを創っているチームは
とりあえず、フロントエンド/バックエンド両方をTypeScriptで書いて、プロダクトの仮説検証まわしつつ、
パフォーマンス向上させたい部分をWASMで置き換えていく戦略もありかなと思い、検討しています。


## この投稿で説明すること

この投稿では以下のようなプロジェクトを構成で

```shell
➜ tree -I 'target|node_modules' sample_project
sample_project
├── node_ts_express_sample
│   ├── package-lock.json
│   ├── package.json
│   ├── pkg -> ../wasm_sample/pkg
│   ├── src
│   │   └── index.ts
│   └── tsconfig.json
└── wasm_sample
    ├── Cargo.lock
    ├── Cargo.toml
    ├── pkg
    │   ├── package.json
    │   ├── wasm_sample.d.ts
    │   ├── wasm_sample.js
    │   ├── wasm_sample_bg.wasm
    │   └── wasm_sample_bg.wasm.d.ts
    └── src
        └── lib.rs
```

Node.jsのWeb Server内でWASMを実行する方法を説明します。

```shell
➜ npx ts-node src/index.ts
server started at http://localhost:8080
```
```shell
➜ curl http://localhost:8080/api/hello/John
Hello, John!
```


## Node.js with TypeScriptでWeb Assemblyを使う

### 1. ルートプロジェクト作成

```shell-session
➜ mkdir sample_project
➜ cd sample_project
```

### 2. Web Assembly作成
Rustを使って、Web Assemblyを作ります。

```shell
➜ cargo new --lib wasm_sample
cd wasm_sample
```

`src/lib.rs`を以下のコードに置き換えます。

```rust
#[wasm_bindgen]
pub fn hello(name: &str) -> String {
    return format!("Hello, {}!", name);
}
```

`Cargo.toml`を以下のコードに置き換えます。

```toml
[package]
name = "wasm_sample"
version = "0.1.0"
edition = "2018"

[lib]
crate-type = ["cdylib"]

[dependencies]
wasm-bindgen = "0.2"
```

以下のコマンドでWASMを作成します。

```shell
➜ wasm-pack build --target nodejs
```

成功すると以下のようなファイルが作成されます。


```shell
➜ ls pkg
package.json  wasm_sample.d.ts  wasm_sample.js  wasm_sample_bg.wasm  wasm_sample_bg.wasm.d.ts
```


ts-nodeでWASMが動作することを確認してください。

```typescript
➜ npx ts-node
> import {hello} from './pkg/wasm_sample'
{}
> hello("John")
'Hello, John!'
>
```

### 3. Node.jsでWeb Server作成

TypeScript用のプロジェクトを作成後、WASMにシンボリックリンクをはります。

```shell
➜ mkdir node_ts_express_sample
➜ cd node_ts_express_sample
➜ ln -s ../wasm_sample/pkg pkg
```
今回はWASMを動かすのが目的なので、詳細は省略しますが、TypeScriptが動くプロジェクトを作成してください。
ts-nodeを利用して、tsファイルを実行できる環境があれば十分です。

```shell
➜ npx ts-node src/index.ts
Hello Typescript world!
```
あとはWebサーバのモジュール(Express)をインストールし、

```shell
➜ npm i --save express
➜ npm -D @types/express
```
エントリーポイント(`src/index.ts`)を以下のコードに置き換えます。

```typescript
import express from 'express';
import { hello } from '../pkg/wasm_sample';

const app = express();
app.get('/api/hello/:name', (req, res) => {
    res.send(hello(req.params.name));
});

const PORT = 8080;
app.listen(PORT, () => {
    console.log(`server started at http://localhost:${PORT}`);
});

```

あとはServerを起動し、

```shell
➜ npx ts-node src/index.ts
server started at http://localhost:8080
```
以下のようにAPIをコールすると、結果が返ってくることが確認できます。

```shell
➜ curl http://localhost:8080/api/hello/John
Hello, John!
```

以上です。



## 参考
- [RustからWebAssembly (wasm)を生成してJavaScriptとブリッジ通信してみる](https://dev.classmethod.jp/articles/rust-webassembly-javascript/)
- [Typescript + Express Template (just add npm)](https://brettfisher.dev/typescript-template/)