福岡から世界中の"むずかしい"を簡単にする株式会社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を開き、「インスタンスを作成」をクリックします。
3.DB名、パスワードなど必要な事項を入力し、DBインスタンスを作成します。
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と管理」→「サービスコンソール」を開きます。
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
に以下の設定を追加します。
{
"db": {
"host": "127.0.0.1"
}
}
4.Yarnでmysqlパッケージを追加します。
$ yarn add mysql
5./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
を以下の内容で新規作成。
<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/ に接続して、以下の画面が表示され、名前が更新されたら成功です。
##本番環境にデプロイして確認
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のハッシュタグでフィードバックいただけると嬉しいです!