Nuxt.js

Nuxt.jsからGoogleスプレッドシートのデータを読み書きする

はじめに

Nuxt.jsからGoogleスプレッドシートのデータを読み込んだり書き込んだりしたのでその手順をまとめます。

できるようになること

以下のようなGoogleスプレッドシートのデータをNuxt.jsで読み込んだり、データの新規追加、更新、削除を行います。

前提と環境

以下の通りです。

  • Nuxt.js : 2.9.2
  • Googleアカウントは取得済とする

手順概要

Googleスプレッドシートのデータを自分が作成するNuxt.jsアプリから読み書きできるようにするためには、Googleが提供している「Google Sheets API」を使用します。そしてこれを使用するには、Google Cloud PlatformでGoogle Sheets APIの有効化と認証情報の作成が必要になります。その後、作成した認証情報を使ってNuxt.jsからGoogleスプレッドシートのデータを読み書きします。したがって以下のような手順になります。

Google Cloud Platform側での作業

  • Google Sheets APIを有効化する
  • Google Sheets APIを使用するための認証情報を作成する
  • Googleスプレッドシートをサービスアカウントと共有する

Nuxt.js側での作業

  • Nuxt.jsにGoogle Sheets API用のパッケージをインストールして設定する
  • Google Sheets APIを使ってGoogleスプレッドシートを読み書きするコード

各手順について順番に載せます。

Google Sheets APIを有効化する

まずGoogle Cloud Platformにログインします。ログイン後、以下のようにメニューから「APIとサービス」→「ライブラリ」をクリックします。なお、適当なプロジェクトが選択されていることを確認します。もしまだプロジェクトを作成してい無い場合は、新規作成しておきます。

以下のように検索フォームで「sheets」と検索して表示された「Google Sheets API」をクリックします。

以下のように表示されるので「有効化する」をクリックして完了です。

Google Sheets APIを使用するための認証情報を作成する

Google Cloud Platformでは、Googleが提供しているAPIを使用するためのアカウントを作成し、そのアカウントが使用できるAPIや権限を設定できます。よってここでもGoogle Sheets APIを使用するためのアカウント(サービスアカウント)を作成します。

Google Sheets APIを有効化すると、以下のようなダッシュボードが表示され、そこに「認証情報を作成」と表示されているのでこれをクリックします。

認証情報を以下のように設定して「必要な認証情報」というボタンをクリックします。

続いて以下のように設定します。「サービスアカウント名」は任意の名前でOKです。「サービスアカウントID」は、サービスアカウント名に応じて決定されます。また、「役割」については、このサービスアカウントによってどのような権限を付与するかによって変更します。ここでは、Googleスプレッドシートの編集のみなので「編集者」としました。もし他のAPIをこのサービスアカウントで使用したい場合は、それに応じて役割を変更します。

上記で「次へ」をクリックすると、以下のようにサービスアカウントとキーが作成されたとメッセージが表示されます。

同時に以下のようにサービスアカウントのキーファイルのダウンロードダイアログが開くので保存しておきます。

このダウンロードしたキーファイルを後ほどNuxt.jsで使用します。

Googleスプレッドシートをサービスアカウントと共有する

作成したサービスアカウントにGoogleスプレッドシートのデータへのアクセスを許可するために、Googleスプレッドシートの共有設定にサービスアカウントを追加する必要があります。
そのためにまず以下のようにGoogle Cloud Platformのメニューから「APIとサービス」→「サービスアカウント」をクリックします。

以下のように作成済のサービスアカウント一覧が表示されます。そこに先程作成したサービスアカウントもあり、「メール」という欄にメールアドレスも表示されていますのでこれをコピーします。

後はNuxt.jsからアクセスしたいスプレッドシートにて、以下のように共有設定にてコピーしたサービスアカウントのメールアドレスを追加します。

以下のようになると思います。

以上でGoogleスプレッドシートをサービスアカウントと共有することができました。

Nuxt.jsにGoogle Sheets API用のパッケージをインストールして設定する

事前準備として、Nuxt.jsにGoogle Sheets API用のパッケージをnpmでインストールします。こちらの公式ドキュメントに従います。

$ npm install googleapis@39 --save

また、以下のようにnuxt.config.jsに設定を追記する必要があります。

nuxt.config.js// (...上省略...)
/*
  ** Build configuration
  */
  build: {
    /*
    ** You can extend webpack config here
    */
    extend (config, ctx) {
      // 以下を追記
      config.node = {
        fs: 'empty',
        googleapis: 'empty',
        child_process: 'empty'
    }
    }
  }

上記を設定しないと、npm run devnpm run build時に以下のようなエラーが出てしまいます。

These dependencies were not found:                                                       friendly-errors 12:22:12
                                                                                         friendly-errors 12:22:12
* child_process in ./node_modules/google-auth-library/build/src/auth/googleauth.js       friendly-errors 12:22:12
* fs in ./node_modules/google-auth-library/build/src/auth/googleauth.js, ./node_modules/google-p12-pem/build/src/index.js and 2 others
                                                                                         friendly-errors 12:22:12
To install them, you can run: npm install --save child_process fs 

上記の原因と対策について完全に理解はできていませんが、以下に参考になりそうな情報があります。

Validate it works with react-native #150

Validate it works with react-native #150

後はNuxt.jsで各自が使用しているVuexストアの適当な箇所(store/index.jsなど)にGoogle Sheets APIを使ってGoogleスプレッドシートにアクセスするコードを記述します。

Google Sheets APIを使ってGoogleスプレッドシートを読み書きするコード

以下がコードになります。ただし、Googleスプレッドシートのデータを読み書きするのに必要な部分のみ記載します。また、色々な値を固定値として与えたり、冗長な部分を残したりしています。statemutationも利用せずに全てaction内に書いてしまっており、読み込んだデータもconsole.logに表示するなど実用性はないので、各自の用途に合わせて書き換えてください。
以下は、store/sheet.jsと適当な名前で作成したストア用のファイルとします。

store/sheet.js// googleapisはサーバーサイドでのみ動作する
if (process.server) {
  const { google } = require('googleapis')
}
const privatekey = require('../credential.json') // サービスアカウントのキーファイル(JSON形式)。Nuxt.jsアプリのルートディレクトリに置いている想定

export const state = () => ({
  // (.. .省略...)
})

export const mutations = {
 // (.. .省略...)
}

export const actions = {
  // スプレッドシートの値を読み込む
  getSheetsData ({ state, commit, dispatch }) {
    // authorizeで認証した上でreadDataFromSheetを実行する
    dispatch('authorize')
      .then((token) => {
        dispatch('readDataFromSheet', token)
      }).catch((error) => {
        console.log(error)
      })
  },
  // スプレッドシートにデータを新しい行として追加する
  addData ({ state, commit, dispatch }) {
    // authorizeで認証した上でaddDataToSheetを実行する
    dispatch('authorize')
      .then((token) => {
        dispatch('addDataToSheet', token)
      }).catch((error) => {
        console.log(error)
      })
  },
  // スプレッドシートのデータを更新する
  updateData ({ state, commit, dispatch }) {
    // authorizeで認証した上でupdateDataToSheetを実行する
    dispatch('authorize')
      .then((token) => {
        dispatch('updateDataToSheet', token)
      }).catch((error) => {
        console.log(error)
      })
  },
  // スプレッドシートのデータを削除する
  deleteData ({ state, commit, dispatch }) {
    // authorizeで認証した上でdeleteDataToSheetを実行する
    dispatch('authorize')
      .then((token) => {
        dispatch('deleteDataToSheet', token)
      }).catch((error) => {
        console.log(error)
      })
  },

  // サービスアカウントのキーファイルを使って認証する
  authorize ({ state, commit }) {
    return new Promise((resolve, reject) => {
      const jwtClient = new google.auth.JWT(
        privatekey.client_email,
        null,
        privatekey.private_key,
        [
          'https://www.googleapis.com/auth/spreadsheets',
          'https://www.googleapis.com/auth/drive'
        ]
      )
     
      jwtClient.authorize(
        (response) => {
          console.log('Successfully connected!')
          resolve(jwtClient)
        },
        (error) => {
          reject(error)
        })
    })
  },
  // データ読み込み用
  readDataFromSheet ({ state }, jwtClient) {
    const spreadsheetId = 'sljltweitoSteulSLUtekwuAkuheeShuek' // 各自のスプレッドシートID
    const sheetName = 'apisheet!A:B' // シート名と読み込み範囲
    const sheets = google.sheets('v4')

    // データの取得にgetを使用する
    sheets.spreadsheets.values.get({
      auth: jwtClient,
      spreadsheetId,
      range: sheetName
    }, (err, response) => {
      if (err) {
        console.log('API error: ' + err)
      } else {
        console.log('Name and Company list from Google Sheets:')
        for (const row of response.data.values) {
          console.log('Name [%s]\t\tCompany [%s]', row[0], row[1])
        }
      }
    })
  },
  // データ追加用
  addDataToSheet ({ state }, jwtClient) {
    const spreadsheetId = 'sljltweitoSteulSLUtekwuAkuheeShuek' // 各自のスプレッドシートID
    const sheetName = 'apisheet!A:B' // シート名と読み込み範囲
    const sheets = google.sheets('v4')

    // 新しく追加するデータ
    const values = [
      [
        'Your Name', 'Your Company'
      ],
      [
        'D Name', 'D Company'
      ]
    ]
    const resource = {
      values
    }
    // データの追加にappendを使用する
    // 以下では「apisheet」というシートの「A1」からデータが無い部分に新しく行が追加されて上記のvalueが書き込まれる
    sheets.spreadsheets.values.append({
      auth: jwtClient,
      spreadsheetId,
      range: 'apisheet!A1',
      resource,
      valueInputOption: 'USER_ENTERED', // データをパースして数値、日付などに変換するためのオプション
      insertDataOption: 'INSERT_ROWS' // データを上書きせずに新しく行を挿入するためのオプション
    }, function (err, response) {
      if (err) {
        console.log('API error: ' + err)
      } else {
        console.log('Sheets updated:')
      }
    })
  },
  // データ更新用
  updateDataToSheet ({ state }, jwtClient) {
    const spreadsheetId = 'sljltweitoSteulSLUtekwuAkuheeShuek' // 各自のスプレッドシートID
    const sheets = google.sheets('v4')

    // 更新用データ
    const values = [
      [
        'Test Name', 'Test Company'
      ],
      [
        'A Name', 'A Company'
      ]
    ]
    const resource = {
      values
    }

    // データの更新にupdateを使用する
    // 以下では「apisheet」というシートの「A20」に上記のvaluesが上書きされる
    sheets.spreadsheets.values.update({
      auth: jwtClient,
      spreadsheetId,
      range: 'apisheet!A20',
      resource,
      valueInputOption: 'USER_ENTERED'
    }, (err, response) => {
      if (err) {
        console.log('API error: ' + err)
      } else {
        console.log('Sheets updated:')
      }
    })
  },
    // データ削除用
  deleteDataToSheet ({ state }, jwtClient) {
    const spreadsheetId = 'sljltweitoSteulSLUtekwuAkuheeShuek' // 各自のスプレッドシートID
    const sheets = google.sheets('v4')
   
    // データの削除にclearを使用する
    sheets.spreadsheets.values.clear({
      auth: jwtClient,
      spreadsheetId,
      range: 'apisheet!A1' // 削除したいセル
    }, function (err, response) {
      if (err) {
        console.log('API error: ' + err)
      } else {
        console.log('Data deleted:')
      }
    })
  }

}

export const getters = {
  // (.. .省略...)
}

各処理は基本的に同じ内容ですが、指定できるオプションや使用するSheets APIの関数が異なります。データの読み込みにはget、新規作成にappend、更新にupdate、削除にclearと別れています。なお、これら以外にももっと大きなデータをまとめて処理したい場合やフィルタをかけて条件に合致したデータのみを更新するようなメソッドも用意されています。これらについては、全て以下の公式ドキュメントに記載されています。

Reading & Writing Cell Values  |  Sheets API  |  Google Developers

Spreadsheets can have multiple sheets, with each sheet having any number of rows or columns.

まだ日本語対応していないため、Google翻訳などを合わせて使うといいかもしれません。

後は、上記で定義したアクションを以下のように適当なコンポーネントでfetchなどで使用できます。

pages/sheet.vue<template>
  <section class="section">
  <!--  (...省略...) -->
  </section>
</template>

<script>
export default {
  name: 'sheettest',
  data () {
    return {
      // (...省略...)
    }
  },
  // 以下のようにfetchで使用可
  // ただし、googleapiはサーバーサイドでのみ動作するため、
  // コンポーネント内で定義したmethodからは呼べない 
  // 以下はstore/sheet.js に各メソッドを書いた想定
  async fetch ({ store }) {
    await store.dispatch('sheet/getSheetsData')
    await store.dispatch('sheet/addData')
    await store.dispatch('sheet/updateData')
    await store.dispatch('sheet/deleteData')
  },
  methods: {
    // (...省略...)
  }
}
</script>

まとめ

Nuxt.jsからGoogleスプレッドシートにアクセスできると、簡単なアプリならば簡易データベースとしてGoogleスプレッドシートを使用でき用途が広がりそうです。

SPONSORED LINK

コメントを残す

メールアドレスが公開されることはありません。