Firestore良さそうですよね。ローカルで動作を試せるLocal Emulatorがあるので気軽に試せてさらによさそうです。JVM環境で接続する例があんまり見当たらなかったのでメモします。
firestore local emulatorの準備をする
とりあえずインストールして、
firebase setup:emulators:firestore
次のコマンドで起動できればOKです。
firebase serve --only firestore
firebaseのバージョンとかでいろいろエラー出たりするのでググったりしてなんとか入れてください。
firestore local emulatorをポートを指定して起動する
firebase serve --only firestore
ではポートが8080でしか起動できません、そこで次のコマンドでLocal Emulatorを起動します。
gcloud beta emulators firestore start --host-port=localhost:8812
次のような出力が得られれば成功です。
[firestore] API endpoint: http://localhost:8812 [firestore] If you are using a library that supports the FIRESTORE_EMULATOR_HOST environment variable, run: [firestore] [firestore] export FIRESTORE_EMULATOR_HOST=localhost:8812 [firestore] [firestore] Dev App Server is now running. [firestore]
application.confにlocal emulatorの設定を書く
Ktorからlocalhost:8812のLocal Emulatorに接続するために、application.confに設定を書きます。 特にfirebaseのための書き方などはないので、適当に書きます。
ktor { firebase { project_id = $PROJECT_ID firestore { emulator { host = localhost port = 8812 } } } }
Ktorから設定を読み込む
application.confの内容はApplication.environment
から読み込めます。
fun Application.module() { val firestoreEmulatorHost = environment.config.propertyOrNull("ktor.firebase.firestore.emulator.host")?.getString() val firestoreEmulatorPort = environment.config.propertyOrNull("ktor.firebase.firestore.emulator.port")?.getString()?.toInt() val projectId = environment.config.propertyOrNull("ktor.firebase.project_id")?.getString() ?: throw IllegalStateException( "firebase project id not found" ) }
Firestoreを初期化する
FirestoreでLocal Emulatorに接続する場合はFirestoreOptionsを使って直接Firestoreのインスタンスを作ります。
val firestore = FirestoreOptions .newBuilder() .setHost("$firestoreEmulatorHost:$firestoreEmulatorPort") .build() .service
本物につなぐ場合も考慮して次のようなobjectを作りました。
import com.google.auth.oauth2.GoogleCredentials import com.google.cloud.firestore.Firestore import com.google.cloud.firestore.FirestoreOptions import com.google.firebase.FirebaseApp import com.google.firebase.FirebaseOptions import com.google.firebase.cloud.FirestoreClient object FirebaseInitializer { data class FirestoreEmulator(val firestoreEmulatorHost: String, val firestoreEmulatorPort: Int) { fun toUrl() = "$firestoreEmulatorHost:$firestoreEmulatorPort" } fun firestore( projectId: String, emulator: FirestoreEmulator? ): Firestore { return if (emulator != null) { FirestoreOptions .newBuilder() .setHost(emulator.toUrl()) .build() .service } else { val credentials = GoogleCredentials.getApplicationDefault() val options = FirebaseOptions.Builder() .setCredentials(credentials) .setProjectId(projectId) .build() FirebaseApp.initializeApp(options) FirestoreClient.getFirestore() } } }
こんな感じでFirestoreを取り出せます。
val firestore = FirebaseInitializer.firestore( projectId, if (firestoreEmulatorHost != null && firestoreEmulatorPort != null) { FirebaseInitializer.FirestoreEmulator(firestoreEmulatorHost, firestoreEmulatorPort) } else { null } )
やったね。
おまけ: KoinでInjection
せっかくなのでKoinでFirestoreインスタンスをinjectできるようにします。 とりあえずKoinをdependenciesに追加し、
dependencies { implementation "org.koin:koin-core:2.0.1" implementation "org.koin:koin-ktor:2.0.1" }
モジュールを実装します。Applicationの拡張関数にしとくと楽です。
import io.ktor.application.Application import org.koin.dsl.module fun Application.applicationModule() = module { single { val firestoreEmulatorHost = environment.config.propertyOrNull("ktor.firebase.firestore.emulator.host")?.getString() val firestoreEmulatorPort = environment.config.propertyOrNull("ktor.firebase.firestore.emulator.port")?.getString()?.toInt() val projectId = environment.config.propertyOrNull("ktor.firebase.project_id")?.getString() ?: throw IllegalStateException( "firebase project id not found" ) FirebaseInitializer.firestore( projectId, if (firestoreEmulatorHost != null && firestoreEmulatorPort != null) { FirebaseInitializer.FirestoreEmulator(firestoreEmulatorHost, firestoreEmulatorPort) } else { null } ) } }
あとはアプリケーションの初期化時にKoinをinstallしてモジュールを設定すれば、適当に取り出せるようになります。
fun Application.module() { install(Koin) { modules( applicationModule() ) } // ... routing { get("/firestore") { val firestore: Firestore by this@routing.inject() // do something } //... }
まとめ
Firestore Local Emulator良さそう。admin的な画面って内蔵されてないんだろうか?ある気がする。わからない。