LoginSignup
35
30

More than 3 years have passed since last update.

CloudFunctionsからCloudSQLに接続

Last updated at Posted at 2019-12-07

福岡から世界中の"むずかしい"を簡単にする株式会社diffeasyCTOの西@_takeshi_24です。

この記事はアドベントカレンダー「diffeasyCTO西の24(にし)日連続投稿チャレンジ Advent Calendar 2019」の7日目の記事です。

この記事は「Nuxt.jsとFirebaseとCloudFunctionsでWebアプリ開発」シリーズとして、連載していきます。
Nuxt.jsとFirebaseなどを使ってWebアプリケーション開発にチャレンジしたい方、是非Qiitaアカウントかtwitterをフォローしていただき、ツッコミやいいね!お願いします!

ここまでの記事では、Firebaseの機能について書いてきましたが、Firebaseで用意されているデータベースはNoSQLのみです。
NoSQLは非常に便利ですが、やはり複雑なデータを取得する必要があるケースはRDBの方が操作しやすい場合が多いです。(私がRDB脳だからかもしれませんが・・・。)

そこで、CloudFunctionsからCloudSQLのRDBに接続する方法についてまとめます。

はじめに

こちらの記事は、「Nuxt.jsとFirebaseとCloudFunctionsでWebアプリ開発」シリーズとして連載していますので、「diffeasyCTO西の24(にし)日連続投稿チャレンジ Advent Calendar 2019」の過去の記事もご覧ください。

CloudSQLの準備

FireStoreのCloudFunctionsはGCPの管理コンソール画面から見ても、同じようにデプロイされています。
同じアカウントでGCPのコンソールにログインしてください。

1.メニューからCloudSQLを開き、「インスタンスを作成」をクリックします。
スクリーンショット 2019-11-30 22.50.54.png

2.DBを選択します。今回はMySQLを選択します。
スクリーンショット 2019-11-30 22.55.29.png

3.DB名、パスワードなど必要な事項を入力し、DBインスタンスを作成します。
スクリーンショット 2019-11-30 23.00.41.png

4.しばらく待つと、DBインスタンスが起動します。

ローカルからCloudSQLに接続する

テスト用のテーブルを作成するため、ローカルからCloudSQLに接続します。
IPとパスワードによる認証ではなく、よりセキュアなCloudProxy経由で接続します。

CloudProxyについては、こちらを参考にしてください。

ここでは、MacOSでの手順を書きます。
1.CloudProxyをインストールする。

$ curl -o cloud_sql_proxy https://dl.google.com/cloudsql/cloud_sql_proxy.darwin.amd64
$ chmod +x cloud_sql_proxy

2.GCP管理コンソールの「IAMと管理」→「サービスコンソール」を開きます。

3.サービスアカウト名を入力します。
スクリーンショット 2019-11-30 23.31.00.png

4.CloudSQL管理者の役割を選択します。
スクリーンショット 2019-11-30 23.27.33.png

5.「キーを作成」をクリックし、JSON形式でダウンロードします。

6.CloudSQL Proxyを使ってDBインスタンスと接続します。

./cloud_sql_proxy -instances=<INSTANCE_CONNECTION_NAME>=tcp:3306 \
                  -credential_file=<PATH_TO_KEY_FILE> &

Cloud SQL Admin API has not been used in projectのエラーが出た場合は、エラーメッセージに表示されるリンクを開いて、「CloudSQL Admin API」を有効にして再度接続してください。

2019/11/30 23:36:16 Listening on 127.0.0.1:3306 for sample-xxxxx:asia-northeast1:sample
2019/11/30 23:36:16 Ready for new connections

のようなメッセージが表示され、127.0.0.1:3306で接続されます。

7.mysql -uroot -p --host 127.0.0.1でMySQLに接続します。

8.sampleDBを作成します。

CREATE DATABASE sample DEFAULT CHARACTER SET utf8mb4;

9.sampleDBに接続します。

CONNECT sample;

10.usersテーブルを作成します。

CREATE TABLE users(
    id INT(11) AUTO_INCREMENT NOT NULL, 
    firstName VARCHAR(30) NOT NULL,
    familyName VARCHAR(30) NOT NULL,
    PRIMARY KEY (id)
);

CloudFunctionsからCloudSQLのRDBに接続

1.configにDB接続情報を設定します。

firebase functions:config:set db.name="sample" db.user="root" db.password="password" db.connection="sample-xxxxx:asia-northeast1:sample"

2.ローカル開発のため、/functions/.runtimeconfig.jsonにconfigをエクスポートします。

firebase functions:config:get > .runtimeconfig.json

3.CloudFunctionsでは、接続名で接続し、ローカル環境では、CloudSQL Proxy経由で、127.0.0.1のhostで接続しますので、.runtimeconfig.jsonに以下の設定を追加します。

.runtimeconfig.json
{
  "db": {

    "host": "127.0.0.1"
  }
}

4.Yarnでmysqlパッケージを追加します。

$ yarn add mysql

5./functions/index.jsに以下の内容を追加します。

/functions/index.js
const functions = require("firebase-functions");
const https = functions.region("asia-northeast1").https;
const admin = require("firebase-admin");
admin.initializeApp();
const mysql = require("mysql");
const util = require("util");

const mysqlConfig = {
  connectionLimit: 1,
  user: functions.config().db.user,
  password: functions.config().db.password,
  database: functions.config().db.name
};
let mysqlPool;

exports.updateUser = https.onCall(async (data, context) => {
  let user = await _selectUser(context.auth.uid);
  if (user) {
    user.firstName = data.firstName;
    user.familyName = data.familyName;
    return await _updateUser(user);
  } else {
    user = {
      uid: context.auth.uid,
      firstName: data.firstName,
      familyName: data.familyName
    };
    return await _insertUser(user);
  }
});

exports.getUser = https.onCall(async (data, context) => {
  return await _selectUser(context.auth.uid);
});

// usersをSELECT
_selectUser = async function(uid) {
  await _initMysqlPool();
  const query = `select * from users where uid = ?`;

  let record = await mysqlPool.query(query, uid);

  console.log(record);
  let user = null;
  if (record.length > 0) {
    user = record[0];
  }

  return user;
};

// usersにINSERT
_insertUser = async function(user) {
  await _initMysqlPool();
  const query = `insert into users (uid, firstName, familyName) values (?, ?, ?)`;

  let record = await mysqlPool.query(query, [
    user.uid,
    user.firstName,
    user.familyName
  ]);

  return record;
};

// usersを更新
_updateUser = async function(user) {
  await _initMysqlPool();
  const query = `update users set firstName = ? , familyName = ? where uid = ? `;

  let record = await mysqlPool.query(query, [
    user.firstName,
    user.familyName,
    user.uid
  ]);

  return record;
};

// ConnectionPoolを初期化し、Promise化
_initMysqlPool = async function() {
  if (functions.config().db.host) {
    // ローカルは、host名でlocalのCloudSQL Proxyに接続
    mysqlConfig.host = functions.config().db.host;
  } else {
    // CloudFunctionsは、接続名で接続
    mysqlConfig.socketPath = `/cloudsql/${functions.config().db.connection}`;
  }

  if (!mysqlPool) {
    mysqlPool = await mysql.createPool(mysqlConfig);
  }

  // promisifyで、Promise化
  mysqlPool.query = util.promisify(mysqlPool.query);
};

Nuxt.jsからCloudSQLを扱うCloudFunctionsに接続。

1./pages/mysql_user/index.vueを以下の内容で新規作成。

/pages/mysql_user/index.vue
<template>
  <section class="container">
    <v-row>
      <v-col>
        <v-text-field v-model="familyName" placeholder="姓"></v-text-field>
      </v-col>
      <v-col>
        <v-text-field v-model="firstName" placeholder="名"></v-text-field>
      </v-col>
      <v-col>
        <v-btn @click="insertUser">更新</v-btn>
      </v-col>
    </v-row>
  </section>
</template>
<script>
import { functions } from "~/plugins/firebase";

export default {
  data() {
    return {
      firstName: "",
      familyName: ""
    };
  },
  mounted() {
    this.getUser();
  },
  methods: {
    async getUser() {
      const getUser = functions.httpsCallable("getUser");
      const result = await getUser();
      if (result.data) {
        this.firstName = result.data.firstName;
        this.familyName = result.data.familyName;
      }
    },
    async insertUser() {
      const updateUser = functions.httpsCallable("updateUser");
      await updateUser({
        firstName: this.firstName,
        familyName: this.familyName
      });
    }
  }
};
</script>

2.ローカルで、yarn run devで起動して、http://localhost:3000/mysql_user/ に接続して、以下の画面が表示され、名前が更新されたら成功です。
スクリーンショット 2019-12-01 12.08.54.png

本番環境にデプロイして確認

1.nuxt-sampleをgenerateします。

$ cd nuxt-sample
$ yarn generate:prod

2.firebase.jsonファイルと同じディレクトリで、デプロイコマンドを実行します。

$ firebase deploy

3.ローカル環境と同じように動作確認できれば、成功です!

最後に

この記事は「Nuxt.jsとFirebaseとCloudFunctionsでWebアプリ開発」シリーズとして、連載していきます。
続きはアドベントカレンダー「diffeasyCTO西の24(にし)日連続投稿チャレンジ Advent Calendar 2019」に掲載していきます。

Nuxt.jsとFirebaseなどを使ってWebアプリケーション開発にチャレンジしたい方、是非Qiitaアカウントかtwitterをフォローしていただき、ツッコミやいいね!お願いします!

#advent_24のハッシュタグでフィードバックいただけると嬉しいです!

35
30
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
35
30