Nuxt.jsでNCMB（ニフクラ mobile backend）のデータストアを利用する

今、前の記事（[NuxtJSによる基本CRUDサンプル](https://qiita.com/JQinglong/items/14340ff6a1eeae9463b8)）の続きで、アプリを作っていますが、それは、Nuxt.jsでFirebaseを使用しています。
が、突然、「そうだ NCMB行こう」と思ってしまい、簡単にToDoリストアプリの取っ掛かり部分を作ってみました。

## プロジェクト作成

以下のような感じで、Nuxt.jsプロジェクトを作ります。

```sh
yarn create nuxt-app todo-ncmb

? Project name: todo-ncmb
? Programming language: JavaScript
? Package manager: Yarn
? UI framework: Vuetify.js
? Nuxt.js modules: Axios
? Linting tools: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Testing framework: None
? Rendering mode: Single Page App
? Deployment target: Static (Static/JAMStack hosting)
? Development tools: jsconfig.json (Recommended for VS Code if you're not using typescript)
```

```sh
cd todo-ncmb
yarn dev
```

色々エラーになったけど、起動はしました。とりあえず大丈夫そうです。

![スクリーンショット 2020-08-26 19.34.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77307/383ddb1d-10aa-835c-2584-7b9fcd2055ae.png)

（やはり Vuetify 良いですね・・・）

## NCMB登録
NCMBサービスのログインから始めます。

https://console.mbaas.nifcloud.com/login
以前使ったことがある気がしましたが、ログインしようとしたら新規登録を要求されました。
登録後ログインし、アプリの新規登録を行います。
公式サイトのクイックスタートの手順に従えば問題ないと思います。

[アプリの新規作成](https://mbaas.nifcloud.com/doc/current/introduction/div_quickstart_javascript.html#%E3%82%A2%E3%83%97%E3%83%AA%E3%81%AE%E6%96%B0%E8%A6%8F%E4%BD%9C%E6%88%90)
アプリケーションキー、クライアントキーもメモしておきます。

続いて、SDKのインストールを行います。
上記ページの続きで手順は大丈夫でしょう。

ここで、「No Xcode or CLT version detected!」というエラーが発生しました。
以前もあったなと言うことで、[こちら](https://qiita.com/hppRC/items/a01e33a5c890f7b2c125)を参照して、下記を実行します。

```sh:
yarn cache clean
sudo rm -rf $(xcode-select -print-path)
xcode-select --install
```

さらに、

```
This dependency was not found:   
* fs in ./node_modules/node-localstorage/LocalStorage.js   
```

というエラーも出ました。
これは、[こちら](https://github.com/nuxt-community/dotenv-module/issues/11
)を参考に、nuxt.config.js に記載を追加します。

```js:nuxt.config.js
  build: {
    extend (config, { isDev, isClient }) {
        config.node = {
            fs: 'empty'
        }
    }
  }
```

これで、SDKが組み込まれた形になりました。

## NCMB データストア登録

Nuxt.js で NCMB を使おうと思った際に、最初に参照することになると思われるのが、こちらかと思います。
[Nuxt.jsを使ってVue × NCMBでサーバサイドレンダリングを実現する](https://blog.mbaas.nifcloud.com/entry/2018/04/18/133235)

このサンプルでは、Helloオブジェクトからメッセージを表示する形になっていますので、Helloクラスを作ることにします。

データストアにクラスを作る方法はこちら。
[データストア (JavaScript) : 基本的な使い方 | ニフクラ mobile backend](https://mbaas.nifcloud.com/doc/current/datastore/basic_usage_javascript.html#%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%92%E6%96%B0%E3%81%9F%E3%81%AB%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B&gsc.tab=0)

クラス名:　Hello
[フィールドを新たに作成する](https://mbaas.nifcloud.com/doc/current/datastore/basic_usage_javascript.html#%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E3%82%92%E6%96%B0%E3%81%9F%E3%81%AB%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B)
フィールド名:　message
と言う感じで作成しました。
さらに、[レコードを追加する](https://mbaas.nifcloud.com/doc/current/datastore/basic_usage_javascript.html#%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B)にしたがって、メッセージを登録します。
![スクリーンショット 2020-08-27 6.42.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77307/8fe7a9c5-4147-300a-6e23-ce7d38875bc9.png)

## 起動時エラーの対応

ここで、画面を開こうとするとエラーになります。

```
vue.runtime.esm.js?2b0e:8440 You are running Vue in development mode.
Make sure to turn on production mode when deploying for production.
See more tips at https://vuejs.org/guide/deployment.html
client.js?06a0:96 Error: apikey and clientkey required
    at Object.NCMB (ncmb.js?8d49:34)
    at eval (ncmb.js?8d49:123)
    at Object../node_modules/ncmb/lib/ncmb.js (index.js:1429)
    at __webpack_require__ (runtime.js:854)
    at fn (runtime.js:151)
    at eval (index.vue?c0d6:91)
    at Module../node_modules/babel-loader/lib/index.js?!./node_modules/@nuxt/components/dist/loader.js?!./node_modules/vue-loader/lib/index.js?!./pages/index.vue?vue&type=script&lang=js& (index.js:83)
    at __webpack_require__ (runtime.js:854)
    at fn (runtime.js:151)
    at eval (index.vue?b1ff:1)
```

追いかけてみると、

```js
const NCMB = require('ncmb');
```

の時点でエラーとなっていることが分かります。
対応は、[こちら](https://github.com/NIFCLOUD-mbaas/UserCommunity/issues/456)を参考にし、node_modules の中のファイルを直接修正することになります。

```js:node_modules/ncmb/lib/ncmb.js
  // if (typeof define === 'function' && define.amd) {
  //  define([], NCMB);
  // }
```

これはなんとかして欲しいところですね・・・

もう少しちょこちょこいじって、こんなページファイルにします。

```js:todo-ncmb/pages/index.vue
<template>
  <v-layout
    column
    justify-center
    align-center
  >
    <v-flex
      xs12
      sm8
      md6
    >
          {{ messages }}

    </v-flex>
  </v-layout>
</template>

<script>
import Logo from '~/components/Logo.vue'
import VuetifyLogo from '~/components/VuetifyLogo.vue'

const NCMB = require('ncmb');
const application_key = 'xxx';
const client_key = 'xxx';
const ncmb = new NCMB(application_key, client_key);

export default {
  components: {
  },
  async asyncData ({ params }) {
    const Hello = ncmb.DataStore('Hello');
    const messages = await Hello.fetchAll();
    // return {messages};
    return {messages};
  },
  data() {
    return {
    }
  },
}
</script>
```

起動するとこんな感じで、メッセージが取得できることが分かります。

![スクリーンショット 2020-08-27 6.44.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77307/a267eed8-42bc-7224-88ef-de76d7b4546a.png)

## ToDo登録
では、CRUDの基本として、ToDoリストを作るとして、ToDoの登録を作ってみます。

layouts/default.vue のメニューにToDoページへのリンクを追加し、ページファイル pages/todo.vue を追加します。

### store作成

ここで、NCMBへの接続処理は、ストアを作って、ミューテーション、アクションに移すことにします。
こちらを参考にさせていただきました。
[Nuxt.jsでのvuex（store）の簡単なやり方](https://qrunch.net/@itahana/entries/RV6zu252J67FJpkl?ref=qrunch)

NCMBオブジェクトへの保存については、公式のこちらを参照。
[データストア (JavaScript) : 基本的な使い方 | ニフクラ mobile backend](https://mbaas.nifcloud.com/doc/current/datastore/basic_usage_javascript.html#%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E4%BF%9D%E5%AD%98&gsc.tab=0)

いきなり登録できます。事前にクラスを作っておく必要はありません。

![スクリーンショット 2020-08-27 9.57.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77307/55d64b31-3746-87aa-119b-2c73acc10073.png)

最初、登録後に再読み込みされませんでした（登録した行が追加されない）。
登録後に、

```js
this.$store.dispatch('todo/setTodos')
```

を書いてあげることで表示されるようになりました。

![スクリーンショット 2020-08-27 23.02.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77307/64851778-df49-2616-168d-8287ac1a9c1f.png)

### dotenv

ここまで、サンプルにあるように、application_key、client_key を直書きしていたのですが、やはりそれはまずい。
ということで、dotenv を導入します。
こちらを参照。
[Nuxt.js - Firebaseを使ったToDoアプリの作成](https://qiita.com/so-heee/items/200beb014c8a8ad100d1)

## まとめ
と言うわけで、下記のようなソースになっています。

やはり、Firebase に比べて利用者が少ないと思うので、調べるのに時間がかかることはありそう。
ただ、こんな感じで基礎的な部分ができてしまえば、意図したものはできなくはなさそう。

乗り換えてみるか・・・検討は続きます。

```js:todo-ncmb/pages/todo.vue
<template>
  <div class="">
    <v-form
      ref="form"
      v-model="valid"
      lazy-validation
    >
      <v-text-field
        v-model="form.todo"
        label="ToDo"
        required
        :rules="todoRules"
      ></v-text-field>
      <v-btn
        :disabled="!valid"
        color="success"
        class="mr-4"
        @click="submit"
      >
        登録
      </v-btn>
    </v-form>
    <v-simple-table
      fixed-header
    >
      <template v-slot:default>
        <thead>
          <tr>
            <th class="text-left">Name</th>
            <th class="text-left">Done</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="todo in todos" :key="todo.objectId">
            <td>{{ todo.name }}</td>
            <td>{{ todo.done }}</td>
          </tr>
        </tbody>
      </template>
    </v-simple-table>
  </div>
</template>

<script>
export default {
  data: () => ({
    valid: true,
    form: {
      todo: null
    },
    todoRules: [
      v => !!v || 'ToDo is required',
      v => (v && v.length <= 10) || 'ToDo must be less than 10 characters',
    ],
  }),
  mounted () {
    this.$store.dispatch('todo/setTodos')
  },
  computed: {
    todos () {
      // return this.$store.state.videos.videos
      return this.$store.getters['todo/todos']
    },
  },
  methods: {
    submit() {
      let action = 'todo/addTodos';
      this.$store.dispatch(action, this.form.todo)
        .then(() => {
          console.log('created')
          this.$store.dispatch('todo/setTodos')
        })
        .catch((resp) => {
          console.log(resp)
        })

    },
  },
}
</script>

<style>

</style>
```

```js:todo-ncmb/store/todo.js
const NCMB = require('ncmb');
const application_key = process.env.APPLICATION_KEY;
const client_key = process.env.CLIENT_KEY;

const ncmb = new NCMB(application_key, client_key);
const Todos = ncmb.DataStore('Todo');

export const state = () => ({
  todos: {}
})

export const actions = {

  async setTodos({commit}) {  
    const res = await Todos.fetchAll()
    commit('setTodos', res)  
  },  

  // add: 
  async addTodos({commit}, todoName) {  
    var todo = new Todos
    todo.set("name", todoName)
         .set("done", false)
         .save()
         .then(function(todo){
          // 保存後の処理
          console.log('addTodos success')
         })
         .catch(function(err){
           // エラー処理
           console.log('addTodos err:' + err)
          });
  },  

  // update: 
  // remove: 
  // get: 

}

export const mutations = {  
  setTodos(state, todos) {  
    state.todos = todos  
  },  
}  

export const getters = {
  todos: (state) => {
    return state.todos
  },
}

```