Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up for free
TypeScriptから使いやすいFirestore-simpleを紹介します(2020年版)
Kenta Kase
June 26, 2020
Programming
3
1.4k
TypeScriptから使いやすいFirestore-simpleを紹介します(2020年版)
2020/06/26 Firebase Realtime Meetup
https://firebase-community.connpass.com/event/175985/
Kenta Kase
June 26, 2020
Tweet
Share
More Decks by Kenta Kase
See All by Kenta Kase
CircleCIでLayer Cachingを使わずにdocker buildを高速化する
kesin11
0
220
CI/CDのボトルネックを把握できていますか?BigQueryでビルド情報ダッシュボードを構築した話
kesin11
7
4k
GitHub Actionsが他のCIサービスと比較してできることできないこと
kesin11
1
690
iOSアプリのリジェクトリスクを早期に発見するための取り組み
kesin11
4
14k
BitriseでFastlane/Gradleプラグインの統合テスト
kesin11
0
2.3k
Firebase Test Labの紹介
kesin11
5
1.1k
Cloud Functions × BigQuery
kesin11
5
1.7k
Firestoreをもっと手軽に使えるfirestore-simpleを作った
kesin11
2
3.5k
UIテストに最適なテストリポーターを求めて
kesin11
1
1.8k
Other Decks in Programming
See All in Programming
Git Rebase
bkuhlmann
7
1k
A technique to implement DSL in Ruby
okuramasafumi
0
550
Micro Frontends with Module Federation: Beyond the Basics @jax2022
manfredsteyer
PRO
0
280
WindowsコンテナDojo:第2回 Windowsコンテナアプリのビルド、公開、デプロイ
oniak3ibm
PRO
0
140
dbtとBigQueryで始めるData Vault入門
kazk1018
0
170
Milestoner
bkuhlmann
1
200
TDX22: User-Mode DB Ops
ca_peterson
3
1.1k
Named Document って何?
harunakano
0
280
Explore Java 17 and beyond
josepaumard
3
630
実録mruby組み込み体験
coe401_
0
100
脱オブジェクト指向講座(5分LT資料)
kishida
8
11k
Groovy Roadmap
paulk
7
13k
Featured
See All Featured
jQuery: Nuts, Bolts and Bling
dougneiner
56
6.4k
How to train your dragon (web standard)
notwaldorf
57
3.8k
Principles of Awesome APIs and How to Build Them.
keavy
113
15k
Web Components: a chance to create the future
zenorocha
303
40k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
29
4.3k
Mobile First: as difficult as doing things right
swwweet
212
7.5k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
314
19k
Designing on Purpose - Digital PM Summit 2013
jponch
106
5.6k
Writing Fast Ruby
sferik
612
57k
Navigating Team Friction
lara
175
11k
Put a Button on it: Removing Barriers to Going Fast.
kastner
56
2.3k
Testing 201, or: Great Expectations
jmmastey
21
5.4k
Transcript
TypeScriptから使いやすいFirestore-simpleを紹介します(2020年版) Firebase Realtime Meetup (2020/06/26) @Kesin11
⾃⼰紹介 @Kesin11 (GitHub/Twitter) 本業 組織横断でテストやCI/CDの調査・サポートなど (おそらく普通よりは)CIマニアの気がある Firebaseはプライベート開発がメイン 2019年のアドベントカレンダーでエミュレータを使ったテストの書き⽅を紹介 Firebase Emulator
Suiteをフル活⽤してTDDで開発しよう 2
はじめに この発表はFirestoreの機能は⼀通り知っている前提で話します 質問はYouTubeのコメントにお願いします! 発表中に答えきれなかったらTwitterの#FJUGあたりで回答するかも? 放送後はFJUGのSlackで質問して頂ければ回答できます 3
TypeScriptでFirestoreのコード書くときどんな感じのコードになりますか?
多分こんな感じ? type User = { name: string, age: number, }
const alice: User = { name: 'alice', age: 20 } const docRef = await firestore.collection('users').add(alice) const snapshot = await firestore.collection('users').doc(docRef.id).get() const user = snapshot.data() as User // ここでキャストして型を付ける 毎回collectionを書くのが⾯倒くさい 毎回data()を書くのが⾯倒くさい 毎回キャストで型を付けるのが⾯倒くさい 気軽にキャストしていくのは型ミスに繋がる 5
⾃分でUserCollectionクラスを作ろう class UserCollection { collection: CollectionReference constructor (firestore: Firestore) {
this.collection = firestore.collection('users') } async fetch (userId: string) { const snapshot = await this.collection.doc(userId).get() return snapshot.data() as User } } const userCollection = new UserCollection(firestore) // id='alice' を渡すだけ const user = await userCollection.fetch('alice') 6
⾃分でUserCollectionクラスを作ろう 初期の頃に⾃分が取っていたアプローチ collectionやdata()を毎回書かなくて済む キャストする場所が集約される コレクション毎に似たようなクラスを作る必要がある? 汎⽤化したクラスを作れば全コレクションで使いまわせそう Firebaseプロジェクト毎に汎⽤化したクラスをコピペで持ってくる? -> ライブラリ化しよう 7
Firestore-simple https://github.com/Kesin11/Firestore-simple
Firestore-simpleの思想 . 冗⻑なコードを省く . 強⼒な型推論と補完 . 素のFirestoreのAPIから乖離しすぎない . 間違いを起こしやすい機能へのサポート 9
1. 冗⻑なコードを省く
サンプルケース: FirestoreとTypeScript側でキー名や型が微妙に違う場合 Firestore側 /users : { name: string, age: number,
created_at: Timestamp } TypeScript側 User: { name: string, age: number, createdAt: Date } こういうケース、たまにありませんか? (むしろ皆さんが普段どうやっているのか知りたい) Firestore-simpleはこういう処理を上⼿く実現できます 11
素のFirestoreで書く場合 type User = { name: string, age: number, createdAt:
Date, // js のDate 型で扱いたい } const snapshot = await firestore.collection('users').doc(docRef.id).get() const data = snapshot.data() if (!data) return const user = { name: data.name, age: data.age, createdAt: data.created_at.toDate() // キー名を変換しつつ、Date 型に変換 } as User // 型を揃えたのでここでキャスト 12
Firestore-simpleで書いた場合 ライブコーディングします
Firestore-simpleで書いた場合 type User = { // id は必須プロパティ id: string,
name: string, age: number, createdAt: Date, } type UserFirestore = { name: string, age: number, created_at: Date, } const firestoreSimple = new FirestoreSimple(firestore) const userCollection = firestoreSimple.collection<User, UserFirestore>({ path: 'users', decode: (doc) => { return { id: doc.id, name: doc.name, age: doc.age, createdAt: doc.created_at.toDate() } } }) const user = await userCollection.fetch('alice') // => { id: 'alice', name: 'alice', // => age: 20, createdAt: 2020-06-20T13:12:54.816Z } 14
Firestore-simpleで書いた場合 collectionのpathを指定するのは⼀度だけ fetchした結果にUser型が⾃動で付く Firestore -> js での変換処理(decode)を書くのも⼀度だけ decode: (doc) =>
{} の中で doc も型推論されているので補完が効く(今回は 詳しい説明は省略) 逆⽅向のjs -> Firestoreの変換は encode: (obj) => {} (今回は省略) 型に id を必須にしているのは data() をなくしたゆえの制約 id と他のプロパティを同じようにアクセス可能にするため 先の例: user.id (FirestoreのdocumentId), user.name , user.age 15
その他よく使うと思われる機能のショートハンド fetchAll bulkAdd , bulkSet , bulkDelete batchによる⼀括更新処理のラッパー // batch
でset await userCollection.bulkSet([ { id: 'alice', name: 'alice', age: 20 }, { id: 'bob', name: 'bob', age: 22 }, ]) // colletion の全ドキュメントを取得 const users: User[] = await userCollection.fetchAll() // batch でdelete await userCollection.bulkDelete(users.map((user) => user.id)) 16
2. 強⼒な型推論と補完
現代ではwithConverterを使うという⼿がある data() の結果に型が付く。Firestore <-> jsの変換も可能 Firestore-simpleの開発を始めた2018年頃にはなかった const userConverter = {
toFirestore (user: User): FirebaseFirestore.DocumentData { return { name: user.name, age: user.age, created_at: user.createdAt } }, fromFirestore ( data: FirebaseFirestore.DocumentData): User { return { name: data.name, age: data.age, createdAt: data.created_at.toDate() } } } const userCollection = firestore.collection('users').withConverter(userConverter) await userCollection.doc('alice').set({ name: 'alice', age: 20, createdAt: new Date() }) const snapshot = await userCollection.doc('alice').get() const user: User | undefined = snapshot.data() 18
Firestoreを扱う上でバグが起きやすいのは? (個⼈的には)キー名のtypoという凡ミスが多い docRef.update({ nama: 'bob' }) collection('user').where('aga', '>=', '20') これらはwithConverterでは防ぐことができない
withConverterを通したとしても update や where はキー名までチェックしてくれない 19
Firestore-simpleでは型から抽出されるキー名で補完が可能 ライブコーディングでお⾒せします SpeakerDeckで⾒てる⽅は過去記事のGIFアニメをどうぞ TypeScriptからFirestoreを使いやすくするfirestore-simple v4をリリースしました
3. 素のFirestoreのAPIから乖離しすぎない
ActiveRecord⾵にはしない // 擬似コードです const user = new User user.name =
'alice' user.age = 20 await user.save() // ここでDB に書き込まれる const alice = await User.findBy({ name: 'alice') // DB からデータ取得 素のFirestoreと使い⽅がかなり異なる 何かエラーが起きた場合にユーザーがデバッグするのが⼤変になりそう Firestoreのアップデートに追従して機能追加していくのも⼤変そう 22
外しやすいライブラリを⽬指す あくまでFirestoreの補助 使い⽅を⼤きく変えるライブラリは外しにくくなる 外しにくい=採⽤しにくい 個⼈的なポリシー jsでまだドラフト中の新しい構⽂は使わない(デコレータとか) そもそもFirestoreがもっと便利になれば⾃作して⾃分が使う理由もなくなる withConverterが来たときは開発をやめるか⼀瞬悩みましたが、⾃分にとってはまだ 代替に⾄るものではなかった 23
4. 間違いを起こしやすい機能へのサポート batch transaction
batch const userCollectionRef = firestore.collection('users') const batch = firestore.batch() batch.set(userCollectionRef.doc('alice'),
{ name: 'alice', age: 20 }) batch.set(userCollectionRef.doc('bob'), { name: 'bob', age: 22 }) await batch.commit() 個⼈的に気に⼊らない点 transactionと挙動が似ているのに、runTransactionとAPIが全く異なる set, update, deleteの使い⽅が普通と異なる addが存在しない doc() とidを空にすると裏でランダム値を⼊れてくれるのでaddは実現可能 なぜ⽤意してくれない・・・ 25
Firestore-simpleによるbatchのサポート 単に配列を⼀括処理したい場合は bulkAdd , bulkSet , bulkdDelete runTransactionと同じように書ける runBatch も⽤意
await firestoreSimple.runBatch(async (_batch) => { await userCollection.set({ id: 'alice', name: 'alice', age: 20 }) await userCollection.set({ id: 'bob', name: 'bob', age: 20 }) await userCollection.add({ name: 'random', age: 20 }) }) // <- runBatch を抜けるタイミングで⾃動的にbatch.commit() される runBatch の中ではsetなどはすべて⾃動的にbatch内処理として扱われる うっかりbatch外処理として実⾏してしまうミスは起きない 26
Firestore-simpleによるtransactionのサポート runTransactionもrunBatchと同じ改良がされている add, set, update, deleteは⾃動的にtransaction処理として扱われる transactionはgetも同様 ⼀部の処理だけtransaction外で⾏ってしまうという危険なミスは起きない await firestoreSimple.runTransaction(async
(_tx) => { await userCollection.fetch('alice') await userCollection.fetch('bob') await userCollection.set({ id: 'alice', name: 'alice', age: 20 }) await userCollection.set({ id: 'bob', name: 'bob', age: 22 }) }) // <- runTransaction を抜けるタイミングで⾃動的にtransaction.commit() される 27
Firestoreの新しめの機能もカバーしてます FieldValue.serverTimestamp FieldValue.increment CollectionGroup where INなど Firestore Emulator Firestore-simple⾃体のテストがemulatorで実⾏されているのでバッチリ
最近のv7のアップデート
最近のv7のアップデート Web SDKにも対応しました! v6以前とはインストール⽅法から変わりました npm i firestore-simple はDEPRECATED Admin SDK,
Cloud Function⽤ npm i @firestore-simple/admin Web SDK⽤ npm i @firestore-simple/web 30
Firestore-simpleのターゲットユーザー TypeScriptで型をちゃんと付けたい 使い⽅が素のFirestoreから⼤きく変わらない⽅が好み UIフレームワークとは疎結合にしたい Firestore層のテストを書きたい Firestore-simple⾃体のテストコードをぜひ参考に 31
今後の予定 Web SDKの⼀部機能のサポートができていないので対応したい getやonSnapshotなどWeb SDKのみ存在するオプションにまだ未対応 APIドキュメント⽣成 TypeScriptで流⾏りのドキュメント⽣成ツールを知りたい Firestoreに新機能が来たら対応したい 今年はIOがなかったのでいつ来るのだろう 32
付録1 ドキュメント https://github.com/Kesin11/Firestore-simple/blob/master/README.md サンプルコード(AdminとWebで別々にあります) https://github.com/Kesin11/Firestore- simple/tree/master/packages/admin/example https://github.com/Kesin11/Firestore- simple/tree/master/packages/web/example
付録2 過去記事 v7をリリースしたので改めてfirestore-simpleを紹介します firestore-simple v5をリリースしました TypeScriptからFirestoreを使いやすくするfirestore-simple v4をリリースしました Firestoreをもっと⼿軽に使えるfirestore-simpleがバージョン2になりました Firestoreをもっと⼿軽に使えるfirestore-simpleを作った