React NativeでGoogle Sheets APIを使用する

## Google Sheetsを読み取り専用データベースとして使用するReact Nativeアプリ

React NativeアプリのデータベースとしてGoogleスプレッドシートを使用することは推奨されません。なぜなら、クエリの実行を行えず、API呼び出しの割り当ても存在するからです。素直にFirestoreを使ったほうが良いです。

しかし非プログラマーがFirebaseコンソールでFirestoreにデータを追加/変更するのは技術的なハードルが高いです。Firestoreにデータを追加/変更するウェブUIを用意するのが正攻法ですが、その場合はアプリとウェブ管理画面の両方を作成する必要があります。そこで、Googleスプレッドシートを読み取り専用のデータベースとして使用するアイディアが役立つ場合があります。Googleスプレッドシートを使えばデータの管理画面を用意する必要がないからです。

[サンプルリポジトリ](https://github.com/kiyohken2000/google-sheets-test)

![img8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681892/5bd0d09c-4d93-3d27-77cc-5fcea53c9b39.png)

## データベース用のGoogleスプレッドシートを用意する

データベースとして使用するスプレッドシートを作成します。以下のようなスプレッドシートを作成しました。

![img1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681892/af6a207b-6fc9-de21-0131-5f89a0897642.png)

共有オプションを使用して閲覧権限を「リンクを知っている全員」にしておきます。このスプレッドシートにデータを追加/変更するとアプリに反映されます。

![img2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681892/75a2c943-b398-1678-10fb-31d931c9b4f1.png)

## Google Sheets APIを用意

アプリからGoogleスプレッドシートにアクセスするためのAPIキーをGCPコンソールで作成します。

まずGCPコンソールの**Google Sheets API**を開きます。

![img3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681892/1576fb3d-106d-2e89-c93f-a30f51de4853.png)

Google Sheets APIを有効にします。**有効にする**をクリックします。

![img4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681892/4890c413-8576-eb61-7d80-9a74a3d5482a.png)

Google Sheets APIが有効になりました。

![img5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681892/8ac6e121-8889-f632-9593-9323c7ff164d.png)

次にAPIキーを発行します。**認証情報** → **認証情報を作成** → **APIキー**を選択します。

![img6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681892/2d499fae-11fe-b6e7-cf7e-0a9de4f77932.png)

APIキーが発行されました。これはアプリからスプレッドシートにアクセスするときにURLパラメーターとして使用するのでメモしておきます。

![img7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681892/be9ee974-3758-d283-fec9-1f5ca6540b60.png)

## API URL

アプリからアクセスするときのURLは以下のようになります。

`https://sheets.googleapis.com/v4/spreadsheets/{Sheet-ID}/values/{Sheet-Name}?valueRenderOption=FORMATTED_VALUE&key={API-Key}`


変数は以下の3つです。

- Sheet-ID
- Sheet-Name
- API-Key

API-KeyはGCPコンソールで発行したものを使います。Sheet-IDはスプレッドシートURLに含まれています。Sheet-Nameはシート名です。

今回は**1zllNSyjNknAad5RJwYhCKQojqYi3D-gjQys9a1Ao3fI**がSheet-ID、**sheet1**がSheet-Nameです。

![img9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681892/96302bfb-46e5-8b6b-f06c-99a90f11b343.png)

これでスプレッドシートにアクセスする準備ができました。

## アプリからアクセスする

実際にアプリからスプレッドシートのデータを読み取ります。

まずはスプレッドシートのURLを記述します。

**src\config.js**

```javascript
const apiKey = 'my_api_key'
const sheetId = '1zllNSyjNknAad5RJwYhCKQojqYi3D-gjQys9a1Ao3fI'
const sheetName = 'sheet1'

const dataUrl = `https://sheets.googleapis.com/v4/spreadsheets/${sheetId}/values/${sheetName}?valueRenderOption=FORMATTED_VALUE&key=${apiKey}`

export { dataUrl }
```

アプリからURLにアクセスします。下の例ではaxiosを使用していますがHTTPクライアントならなんでもいいです。

**App.js**

```javascript
useEffect(() => {
  const fetchData = async() => {
    try {
      const res = await axios.get(dataUrl)
      const { data } = res
      console.log(data)
    } catch(e) {
      console.log('fetch data error', e)
    }
  }
  fetchData()
}, [])
```

json形式で以下のようなデータを取得することができます。

```
Object {
  "majorDimension": "ROWS",
  "range": "sheet1!A1:Z1000",
  "values": Array [
    Array [
      "id",
      "username",
      "age",
    ],
    Array [
      "1",
      "user1",
      "12",
    ],
    Array [
      "2",
      "user2",
      "13",
    ],
    Array [
      "3",
      "user3",
      "14",
    ],
    Array [
      "4",
      "user4",
      "15",
    ],
    Array [
      "5",
      "user5",
      "16",
    ],
    Array [
      "6",
      "user6",
      "17",
    ],
  ],
}
```

このままだと扱いにくいのでフォーマット用の関数を作成します。

**src\functions.js**

```javascript
const formatData = ({data}) => {
  const keys = data.values[0];
  const _data = data.values.slice(1);
  const obj = _data.map(arr => Object.assign({}, ...keys.map((k, i) => ({ [k]: arr[i] }))));
  return obj
}
```

データ取得後に作成した関数を通すようにします。

```javascript
useEffect(() => {
  const fetchData = async() => {
    try {
      const res = await axios.get(dataUrl)
      const { data } = res
      const _data = formatData({data})
      console.log(_data)
    } catch(e) {
      console.log('fetch data error', e)
    }
  }
  fetchData()
}, [])
```

フォーマット後は以下のようなデータが得られます。

```
Array [
  Object {
    "age": "12",
    "id": "1",
    "username": "user1",
  },
  Object {
    "age": "13",
    "id": "2",
    "username": "user2",
  },
  Object {
    "age": "14",
    "id": "3",
    "username": "user3",
  },
  Object {
    "age": "15",
    "id": "4",
    "username": "user4",
  },
  Object {
    "age": "16",
    "id": "5",
    "username": "user5",
  },
  Object {
    "age": "17",
    "id": "6",
    "username": "user6",
  },
]
```

あとは取得したデータをセットして描画するようにアプリに記述します。

**App.js**

```javascript
import React, { useEffect, useState } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import axios from 'axios';
import { dataUrl } from './src/config';
import { formatData } from './src/functions';

export default function App() {
  const [data, setData] = useState([])

  useEffect(() => {
    const fetchData = async() => {
      try {
        const res = await axios.get(dataUrl)
        const { data } = res
        const _data = formatData({data})
        setData(_data)
      } catch(e) {
        console.log('fetch data error', e)
      }
    }
    fetchData()
  }, [])

  return (
    <View style={styles.container}>
      {data.map((item) => {
        const { id, username, age } = item
        return (
          <View style={styles.row} key={id}>
            <Text>ID: {id}</Text>
            <View style={{paddingHorizontal: 5}}/>
            <Text>Name: {username}</Text>
            <View style={{paddingHorizontal: 5}}/>
            <Text>Age: {age}</Text>
          </View>
        )
      })}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  row: {
    flexDirection: 'row'
  }
});

```

以上、React NativeアプリからGoogleスプレッドシートにアクセスする方法でした。