Closed6

Cloud SQL & Cloud Functions (2nd gen.) でPrismaを使ってみる

hosaka313hosaka313

前提

  • 筆者はPrisma初挑戦
  • ORMの経験はRailsのActive Recordくらい。

→ 記事の水準を担保できないので、スクラップにしました。

目標

Prismaを使い、

  • CloudSQLへの接続
    • ローカル
    • Cloud Functions上
  • CloudSQLのmigration
  • CloudSQLへの書き込み
    • ローカル
    • Cloud Functions上

を行う。

環境

  • 2023年10月
  • MacBook Air (M1, 2020)
  • Node.js v. 18.18.1
  • Prisma 5.4.2

参考資料

https://www.prisma.io/docs/getting-started/quickstart

https://blog.n-t.jp/post/tech/firebase-cloudfunction-prisma-cloudsqlproxy/

hosaka313hosaka313

CloudSQLの作成

dev-prismaの名前で作成。スペックは最小構成。
試用なので、以後、rootユーザーを使う。

Cloud Functions作成

firebase init functions

でTypeScriptプロジェクトを作成

Prisma導入

npm install prisma 
npx prisma init

.envschema.prismaが作成される。

schema.prismaの作成

寄付フォームからのデータを想定して、下記のようにした。[1]

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}

model FormValue {
  id             Int     @id @default(autoincrement())
  first_name     String
  first_name_kana String
  last_name      String
  last_name_kana String
  email          String
  amount         Int
  anonymous      Boolean? @default(false)
  receipt        Boolean? @default(false)
  project        String
}

.envの設定

DATABASE_URLを設定する。

mysql://<username>:<password>@localhost/<db_name>?socket=/cloudsql/<接続名>

接続名はCloudSQLの管理画面にある。

脚注
  1. GPT-4は2022年までの学習データを持っており、Prismaをカバーしているので、TypeScriptの型情報を渡せば作ってもらえる。 ↩︎

hosaka313hosaka313

Cloud SQL Auth Proxyでローカル接続

Cloud SQL Auth Proxy導入

公式ドキュメントをもとに下記を実行(環境によって異なります。)

curl -o cloud-sql-proxy https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.6.1/cloud-sql-proxy.darwin.amd64

chmod +x cloud-sql-proxy

https://cloud.google.com/sql/docs/mysql/sql-proxy?hl=ja#macos-64-bit

Cloud SQL Admin APIも有効化

Cloud SQL Auth Proxy実行

portは1234に指定しています。

./cloud-sql-proxy --port 1234 <接続名>

エラー

下記の認証エラーに遭遇。

The proxy has encountered a terminal error: unable to start: error initializing dialer: failed to create token source: google: could not find default credentials. See https://cloud.google.com/docs/authentication/external/set-up-adc for more information

以下でCloudSQLのプロジェクトのアカウントで認証しなおして解消。

gcloud auth login
gcloud config set project <your project id>
gcloud auth application-default login
hosaka313hosaka313

Migration

portを書き換えたので、下記のように変更。

DATABASE_URL="mysql://<username>:<password>@localhost:1234/<db_name>"

ターミナルで下記を実行

npx prisma migrate dev --name init

すると、migrationsフォルダが作成される。

DBeaverで確認してみる。

接続方法は下記。localhostのDBとして接続できる。
https://dev.classmethod.jp/articles/dbeaver-cloud-sql-connect/

Prismaのコマンド

npx prisma migrate dev 

は開発環境用。本番環境では

npx prisma migrate deploy

を CI/CDで実行するべし、とのこと。
https://www.prisma.io/docs/concepts/components/prisma-migrate/migrate-development-production

GitHub Actionsの例が載っている。
https://www.prisma.io/docs/guides/deployment/deploy-database-changes-with-prisma-migrate

hosaka313hosaka313

CloudSQLへの書き込み 〜ローカル編〜

Prisma Clientのインストール

書き込みなどのクエリを実行する際にはPrisma Clientを用いる。

npm install @prisma/client

https://www.prisma.io/docs/concepts/components/prisma-client

ローカルでレコード追加

Cloud Functionsのプロジェクトで下記のテストコードを作成、実行。[1]

import { PrismaClient } from '@prisma/client'

async function test() {
  const prisma = new PrismaClient()
  const entry = await prisma.formValue.create({ // 型情報から補完が効く
    data: {
      "first_name": "太郎",
      "first_name_kana": "タロウ",
      "last_name": "山田",
      "last_name_kana": "ヤマダ",
      "email": "hoge@example.or.jp",
      "amount": 1000,
      "anonymous": false,
      "receipt": false,
      "project": "test_project",
    },
  })
  return entry
}

test().then((entry) => console.log(entry))

DBeaver等で追加を確認。

脚注
  1. ts-nodeを使用。 ↩︎

hosaka313hosaka313

CloudSQLへの書き込み 〜Cloud Functions編〜

Cloud Functions作成

簡単なCloud Functionsを作成する。

.env

mysql://<username>:<password>@localhost/<db_name>?socket=/cloudsql/<接続名>

データベース処理

database.ts
import { PrismaClient } from "@prisma/client"
import { FormData } from "./type"

export async function insertEntry(formData: FormData) {
  const prisma = new PrismaClient()

  try {
    await prisma.formValue.create({ 
      data: {
        first_name: formData.first_name, // オブジェクトは敢えて開いて書いている。
        first_name_kana: formData.first_name_kana,
        last_name: formData.last_name,
        last_name_kana: formData.last_name_kana,
        email: formData.email,
        amount: Number(formData.amount),
        anonymous: formData.anonymous,
        receipt: formData.receipt,
        project: formData.project,
      },
    }),
      console.log("successfully inserted")
  } catch (e) {
    console.error("DB Error: ", e)
  }
}

型定義

type.ts
export type FormData = {
  first_name: string;
  first_name_kana: string;
  last_name: string;
  last_name_kana: string;
  email: string;
  amount: number;
  anonymous?: boolean;
  receipt?: boolean;
  project: string;
};

エントリーポイント

index.ts
import * as functions from "firebase-functions/v2";
import { FormData } from "./type";
import { insertEntry } from "./database";

export const prismaTest = functions.https.onRequest(
  {
    region: "asia-northeast1",
  },
  async (req, res) => {
    const formData = req.body as FormData;
    await insertEntry(formData)

    res.send("ok");
  }
)

Cloud Functionsのデプロイ

firebase deploy --only functions

下記の通り、v2をimportしているのでCloud Functionsは第二世代としてdeployされる。

import * as functions from "firebase-functions/v2";

Cloud Functionsの管理画面から「未認証の呼び出しを許可する」を有効化
https://cloud.google.com/functions/docs/securing/managing-access-iam?hl=ja#console_4

CloudRunでCloudSQL接続設定

CloudRunを開き、CloudSQLへAuth Proxyで接続できるように設定。
https://cloud.google.com/sql/docs/mysql/connect-run?hl=ja#configure

*設定後、Cloud Functionsの再デプロイが必要。デプロイオプションなどありそうだが、わからなかった。

実行

postmanやThunder Clientからリクエストを送ってテスト。

このスクラップは2023/10/11にクローズされました