KuzuDB をVue3で扱う

こんにちは｜こんばんは。カエルのアイコンで活動しております @kyamaz :frog: です。

## はじめに
本稿は、グラフデータベースである kuzudb を Vue3 で扱うための手順を取り上げます。
前半の Vue3 環境の構築については拙稿『[p5.jsをVue3のコンポーネントにする](https://qiita.com/kyamaz/items/2a77685b091065050eea)』に習っており、後半もほかの複数の手順を参考にしながら本稿にまとめております。

## KuzuDB とは
高性能なクエリ処理と拡張性を重視したオープンソースのグラフデータベースです。MITライセンスで提供されており、商用利用も可能です。グラフデータベースは他に Neo4j が有名ですが、KuzuDB は Cypherライクなクエリ言語をサポートしており、Neo4jと同様の使い方ができるのも特徴の一つです。高速なストレージ管理や並列クエリ実行による高スループットの実現などもされており、Neo4jの代替として大規模なグラフデータを効率的に扱いたいユースケースに向いているようです。
このあとは、Vue3 の環境を整えたのちに、KuzuDB をブラウザ上で扱うWebassembly 実装である `kuzu-wasm` を使えるように整えていきます。

## Vue3 + TypeScript + Vite の環境を整える
まず、プロジェクト名を`kuzuvue`（お好きな名前でよいです）として[Vue 3 + TypeScript + Vite + Jest](https://github.com/imomaliev/vue-ts)の環境をまず整えます。

```shell
% npm init vite@latest kuzuvue -- --template vue-ts

Scaffolding project in /hoge/fuga/kuzuvue...

Done. Now run:

  cd kuzuvue
  npm install
  npm run dev
```

出力された通りに`npm`を実行しておきましょう。`npm run dev`の後に次のように出力されますので、手元のブラウザで`http://localhost:5173/`にアクセスしてみます。
```
  VITE v5.0.XX  ready in　XXX ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help
  ```
動作が確認できれば`Ctrl-C`でプロセスを終わらせて、次の手順に進みます。ついでに、プロジェクトルートで次のモジュールもインストールしておきましょう。

* `npm install vue-router`
* `npm install -D vitest`

次に、vue-router で動作するように整えていきます。`src`フォルダに`router.ts`を新規で作成します。

```typescript: router.ts
// router.ts
import { createRouter,createWebHistory } from 'vue-router';
import Home from "./pages/Home.vue";

const routes = [
    { path: '/', name: 'home', component: Home },
]

const router = createRouter({
    history: createWebHistory(), // HTML5 History モード
    routes,
})

export default router;
```

また、`main.ts`を次のように書き換えます。

```typescript: main.ts
// main.ts
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'; // router.tsをインポート

const app = createApp(App)
app.use(router) // Vue Routerを使用する
app.mount('#app')
```

そして、`src`フォルダの構成を変更して、必要なファイルを編集していきます。

* `src`フォルダの下に`pages`を作成し、そこに`App.vue`を`Home.vue`いうファイル名でコピーしておきます。
* `components`,`assets`フォルダを`pages`の下に移動します。（`mv src/components src/assets src/pages/`）
* `App.vue`を次のように変更します。

```vuejs:App.vue
<template>
<router-link to="/"></router-link>
<router-view/>
</template>

<script lang="ts">
export default {
}
</script>

<style scoped>
</style>
```
ここまでは、一般的な Vite＋Vue3＋VueRouter の開発テンプレートの構築手順です。
次から、このテンプレートの状態から kuzudb を使えるようにしていきましょう。

## kuzudb をインストールする

kuzudb 関連のモジュールを使用できるようにインストールします。
※本来はTypeScriptで型チェックができるようにする必要がありますが、型定義が簡単にインストールできなく、:frog: がうまく環境を整えられなかったため、ここでは割愛させて頂き、Javascriptとしてのみ動作させる方向で進めます。型チェックができるようにするには、Reactの例になりますが、@hibohiboo さんの以下の記事をご参考ください。

https://qiita.com/hibohiboo/items/30aadc02d932e579e8c2


```
% npm install kuzu
% npm install @kuzu/kuzu-wasm
```
ここからは、`HelloWorld.vue`に kuzu を実装していきます。
その前に、トップページをシンプルになるように編集しておきましょう。`Home.vue`を次のように、コンポーネント`HelloWorld`を使うように変更します。

```vuejs: Home.vue
<script lang="ts">
import HelloWorld from './components/HelloWorld.vue'
export default {
  components: {
    HelloWorld
  }
}
</script>

<template>
  <div>
    <HelloWorld />
  </div>
</template>

<style scoped>
</style>
```
HelloWorld.vue に kuzu-wasm を利用する部分を次のように書きます。


```vuejs:HelloWorld.vue
<script setup>
import { ref, onMounted } from 'vue';
import kuzu_wasm from "@kuzu/kuzu-wasm";

const users = ref([]);

const do_mounted = async () => {
  const kuzu = await kuzu_wasm();
  const db = await kuzu.Database();
  const conn = await kuzu.Connection(db);

  // テーブルの作成
   conn.execute(`CREATE NODE TABLE User(name STRING, age INT64, PRIMARY KEY(name))`);
   conn.execute(`CREATE REL TABLE Follows(FROM User TO User, since INT64)`);

  // データの挿入
   conn.execute(`CREATE (u:User {name: 'Alice', age: 31});`);
   conn.execute(`CREATE (u:User {name: 'Bob', age: 40});`);

  // データの取得
  const res =  conn.execute(`MATCH (a:User) RETURN a.name AS name, a.age AS age;`);
  console.log(res);
};
onMounted(do_mounted);

</script>

<template>
  <div>
    <h1>Kuzu-WASM with Vue</h1>
    <ul>
      <li v-for="user in users" :key="user.name">
        {{ user.name }} - {{ user.age }}歳
      </li>
    </ul>
  </div>
</template>
```

**（動作不備）users に KuzuDB の値を入れてサイト上に表示したいのですが、うまく動作できておりません。対応できましたら改めて修正します。ご容赦ください。**


### WASM を試す際に、SharedArrayBuffer の対応が必要

このまま`npm run dev`で試してみると、ブラウザのコンソールに `SharedArrayBuffer is not defined` のエラーが出ます。調べてみると以下の記事が参考になりました。

https://qiita.com/6in/items/5987fcd60fc6d18633fb

そこで `vite.config.ts` を次のように書き換えてから、`npm run dev`を行い、ブラウザで試してみたところ、エラーが解消しました。

```typescript: vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vite.dev/config/
export default defineConfig({
  plugins: [vue()],
  build: { target: 'esnext' },
  optimizeDeps: {
    exclude: ['@kuzu/kuzu-wasm'],
    esbuildOptions: {
      target: 'es2022',
    },
  },
  server: {
    headers: {
      'Cross-Origin-Embedder-Policy': 'require-corp',
      'Cross-Origin-Opener-Policy': 'same-origin',
    },
  },
})
```

## 最後に
初稿の公開では、完全に動作ができていないままですが、ご見識の高い方からのご助言もあると助かります。
ご一読いただきまして有り難うございます。

最後に、本稿を記載するために検証したNodeJSとブラウザ環境を記しておきます。お手元の環境で検証する際に動作が異なる場合などは、参考になるかもしれません。

<details><summary>本稿の環境</summary>

本稿のために使用した環境は以下となります。

macOS: Sequoia 15.3.1 (chip: Apple M1)
node: v18.19.0
npm: 10.9.2
Google Chrome	133.0.6943.127 (Official Build) （arm64）

</details>


（●）（●） Happy Hacking!
/""　＿＿""＼