【Flutter】Firebase Data Connect を試す
1. はじめに
時間が経つのは早いもので...もう半年ほど前になってしまいますが、Google I/O 2024にてFirebase Data Connectが発表されました。
Firebase Data Connectを使用すると、Cloud SQLでホストされているPostgresSQLへアプリから直接接続できるようになるということですね。
これは、RDBを使いたいという要望がかなり高かったのでしょうね。加えて、Supabaseへの対抗ということもあるのでしょうか。
触ってみたいと思いつつなかなか時間が取れずにいましたが、1ヶ月ほど前にfirebase_data_connectが公開されたこともあって今回お試ししてみることにしました。
Firebase Data ConnectはまだGAされておらず(2024年10月現在)、限定公開プレビュー版というステータスです。
そのためEarly Accessの申し込みが必要になります。ここではEarly Accessの申請が通っていること、Firebaseのプロジェクト作成やFlutterのプロジェクトの接続が終わっていることを前提に試していこうと思います。
2. ゴール
Data Connectがサンプルとして用意しているスキーマ(Movie, MovieMetadata)を使って、以下のように映画の一覧をqueryします。また、画面右上の + アイコンにて表示されるダイアログからmutationを実行して映画の情報を追加できるようにします。
mutation | query |
---|---|
コードはこちらです。お試しですので、エラー処理などは入っていません。
パッケージは、firebase_data_connectを利用しています。
3. Data Connectを構成する
FirebaseコンソールからData Connectを構成していきます。
Crate new Cloud SQL instance
を選びました。使用しているFirebaseのプロジェクトはSpark PlanだったのですがBlaze Planが必須のようでしたのでアップグレードしました。
Cloud SQLのインスタンスIDやデータベース名を入力していきます。
サービスIDを入力してSubmitすると、
以下のようにData Connectが使用できるようになりました。
Google CloudコンソールのCloud SQLからもデータベースが作成されたことが確認できました。
4. スキーマを構成する
Set up an environment with our extention for Visual Studio Code
とあります。
拡張機能のこのあたりを使用するとセットアップできそうです。
ただ、私はVSCodeをあまり使っていない(JetBrains派なので & JetBrainsの拡張機能はまだ無さそう)ということもあって、コマンドで進めてみることにしました。
4.1 firebase init dataconnect
% firebase init dataconnect
######## #### ######## ######## ######## ### ###### ########
## ## ## ## ## ## ## ## ## ## ##
###### ## ######## ###### ######## ######### ###### ######
## ## ## ## ## ## ## ## ## ## ##
## #### ## ## ######## ######## ## ## ###### ########
You're about to initialize a Firebase project in this directory:
/Users/osaki/github/motucraft/firebase_playground
Before we get started, keep in mind:
* You are initializing within an existing Firebase project directory
=== Project Setup
First, let's associate this project directory with a Firebase project.
You can create multiple project aliases by running firebase use --add,
but for now we'll just set up a default project.
i Using project fir-playground-5015e (firebase-playground)
=== Dataconnect Setup
i dataconnect: ensuring required API firebasedataconnect.googleapis.com is enabled...
✔ dataconnect: required API firebasedataconnect.googleapis.com is enabled
i dataconnect: ensuring required API sqladmin.googleapis.com is enabled...
✔ dataconnect: required API sqladmin.googleapis.com is enabled
i dataconnect: ensuring required API compute.googleapis.com is enabled...
✔ dataconnect: required API compute.googleapis.com is enabled
? Your project already has existing services. Which would you like to set up local files for? asia-northeast1/playground-data-connect
✔ Wrote dataconnect/dataconnect.yaml
✔ Wrote dataconnect/schema/schema.gql
✔ Wrote dataconnect/connector/connector.yaml
✔ Wrote dataconnect/connector/mutations.gql
✔ Wrote dataconnect/connector/queries.gql
✔ Detected FLUTTER app in directory /Users/osaki/github/motucraft/firebase_playground
? Which connector do you want set up a generated SDK for? playground-data-connect/default
i Wrote new config to /Users/osaki/github/motucraft/firebase_playground/dataconnect/connector/connector.yaml
I1013 21:10:30.517916 50323 codegen.go:82] [connector "default" dartSdk] Generating sources into "/Users/osaki/github/motucraft/firebase_playground/dataconnect-generated/dart/default_connector"
I1013 21:10:30.519871 50323 dartgen.go:575] Started Dart code generation for connector default
I1013 21:10:30.529082 50323 generate.go:40] Generated all sources. Writing them to disk...
I1013 21:10:30.530671 50323 collector.go:107] connector "default" dartSdk wrote into "/Users/osaki/github/motucraft/firebase_playground/dataconnect-generated/dart/default_connector"
Generated sources: create_movie.dart [2920B] create_movie_metadata.dart [4453B] list_movies.dart [2386B] get_movie_by_id.dart [4742B] default.dart [1376B]
i Generated SDK code for default
✔ If you'd like to add more generated SDKs to your app your later, run firebase init dataconnect:sdk again
i Writing configuration info to firebase.json...
i Writing project information to .firebaserc...
✔ Firebase initialization complete!
firebase init dataconnect
を実行すると、プロジェクト直下にdataconnect
ディレクトリ、dataconnect-generated
ディレクトリが生成されました。
dataconnect-generated
ディレクトリについて、以下はテーブルを作成してからの結果なので複数のdartファイルが生成されているように見えますが、初回はdefault.dart
だけが生成されました。
% tree dataconnect dataconnect-generated
dataconnect
├── connector
│ ├── mutations.gql
│ └── queries.gql
├── dataconnect.yaml
└── schema
└── schema.gql
dataconnect-generated
└── dart
└── default_connector
├── create_movie.dart
├── create_movie_metadata.dart
├── default.dart
├── get_movie_by_id.dart
└── list_movies.dart
6 directories, 10 files
dataconnect-generated
はプロジェクト直下ではなくlibディレクトリ配下に配置したいため、connector.yaml
のoutputDir
を編集しておくのが良さそうです。
connectorId: default
generate:
dartSdk:
outputDir: ../../lib/dataconnect/dataconnect-generated/dart/default_connector
package: default_connector
この辺りのドキュメント に説明があります。
修正したら、firebase dataconnect:sdk:generate
を実行しておきます。firebase dataconnect:sdk:generate --watch
で変更を監視するという方法もあるようです。
4.2 schema.gql を編集してテーブルを定義する
以下のようにMovie
テーブル、MovieMetadata
テーブルを定義しました。こちらの内容 です。
type Movie @table {
# The below parameter values are generated by default with @table, and can be edited manually.
# implies directive `@col(name: "movie_id")`, generating a column name
id: UUID! @default(expr: "uuidV4()")
title: String!
imageUrl: String!
genre: String
}
type MovieMetadata @table {
# @unique indicates a 1-1 relationship
movie: Movie! @unique
# movieId: UUID <- this is created by the above reference
rating: Float
releaseYear: Int
description: String
}
4.3 query/mutation を定義する
- queries.gql
query ListMovies @auth(level: PUBLIC) {
movies {
id
title
imageUrl
genre
}
}
query GetMovieById($id: UUID!) @auth(level: PUBLIC) {
movie(id: $id) {
id
title
imageUrl
genre
metadata: movieMetadata_on_movie {
rating
releaseYear
description
}
}
}
- mutations.gql
mutation CreateMovie(
$title: String!
$genre: String!
$imageUrl: String!
) @auth(level: PUBLIC) {
movie_insert(
data: {
title: $title
genre: $genre
imageUrl: $imageUrl
}
)
}
mutation CreateMovieMetadata(
$movieId: UUID!
$releaseYear: Int
$description: String
$rating: Float
) @auth(level: PUBLIC) {
movieMetadata_insert(
data: {
movieId: $movieId
releaseYear: $releaseYear
description: $description
rating: $rating
}
)
}
今回はお試しのため認証も行いませんので、mutationには@auth(level: PUBLIC)
を付けています。
ドキュメントはこの辺り が該当します。感覚としては、Firestoreのセキュリティールールと通ずるものがありそうだなと捉えました。まぁ、同じFirebaseのサービスですからね。
4.4 スキーマをデプロイする
以下のようにデプロイしました。
firebase deploy --only dataconnect --project fir-playground-5015e
(node:56412) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
=== Deploying to 'fir-playground-5015e'...
i deploying dataconnect
i dataconnect: ensuring required API firebasedataconnect.googleapis.com is enabled...
✔ dataconnect: required API firebasedataconnect.googleapis.com is enabled
i dataconnect: ensuring required API sqladmin.googleapis.com is enabled...
✔ dataconnect: required API sqladmin.googleapis.com is enabled
i dataconnect: ensuring required API compute.googleapis.com is enabled...
✔ dataconnect: required API compute.googleapis.com is enabled
i dataconnect: Preparing to deploy
i dataconnect: Successfully prepared schema and connectors
i dataconnect: Checking for CloudSQL resources...
i dataconnect: Found existing instance playground-cloud-sql.
i dataconnect: Found existing database playground-database.
i dataconnect: Deploying Data Connect schemas...
i dataconnect: Schemas deployed.
i dataconnect: Deploying connectors...
✔ dataconnect: Deployed connector projects/fir-playground-5015e/locations/asia-northeast1/services/playground-data-connect/connectors/default
i dataconnect: Connectors deployed.
✔ dataconnect: Deployment complete! View your deployed schema and connectors at https://console.firebase.google.com/project/fir-playground-5015e/dataconnect
✔ Deploy complete!
Project Console: https://console.firebase.google.com/project/fir-playground-5015e/overview
これにより、以下のようにテーブルが作成されたことを確認できました。
このように、コンソール上からqueryを書いてDBアクセスすることもできます。
firebase_data_connect を利用してコードを書く。
5.まだEarly Accessということもあってあまり情報が無いのですが、Use generated Flutter SDKsを参照しながら実装していきました。
Subscribe to changesという説明がありましたので、以下のようにQueryRef
を提供するProviderを用意しておき、mutation実行後にawait ref.read(listMovieRefProvider).execute()
を実行することでサブスクライブさせています。
QueryRef<ListMoviesData, void> listMovieRef(ListMovieRefRef ref) {
return DefaultConnector.instance.listMovies().ref();
}
Stream<QueryResult<ListMoviesData, void>> movies(MoviesRef ref) {
final listRef = ref.watch(listMovieRefProvider);
return listRef.subscribe();
}
ただし、このSubscribeはリアルタイム更新ではありません。どうやら、リアルタイム更新はまだ存在しない ようですね。
投票しておきましょう。
6. おわりに
Firebase Data Connectをお試しして雰囲気を掴むことができました。
RDBで作られた既存システムをドキュメントベースのDB(Firestore)へ移行するのはとても大変だと思いますが、Cloud SQL(PostgreSQL)であれば選択肢が広がりそうです。
今回のお試しの中で、リアルタイム更新と同じく「あれ?これどうするの??」と思ったのがトランザクションです。ドキュメントや、firebase_data_connectのAPI referenceも探してみたのですが記載は無さそうでした。
この辺りはまだEAだからということでしょうか。GAされたらまた確認してみようと思います。
Discussion