Slide 1

Slide 1 text

TypeScript + Firebaseで作る サーバーレス アプリケーション 2018年10月24日(水) ゆるはち.it Vol.3 Kota Nonaka (@uutarou10)

Slide 2

Slide 2 text

今日やること • Firebaseの機能紹介 • TypeScript + Firebase Webアプリケーションコード解説 !2

Slide 3

Slide 3 text

Firebaseについて !3

Slide 4

Slide 4 text

Firebase • モバイルアプリ用のバックエンドを
 提供するプラットフォーム(mBaaS) • 現在はGoogleが提供している • iOS/Android/UnityなどのSDKが提供
 実は、Web用のJavaScript SDKも! !4

Slide 5

Slide 5 text

Firebaseが提供するサービス 現在はBeta版含め18個のサービスが提供されている
 (Web SDKで利用できるサービスは9個) !5 • Cloud Firestore • Cloud Functions • Authentication • Hosting • Cloud Storage • Realtime Database • Predictions • Cloud Messaging • Dynamic Links

Slide 6

Slide 6 text

Firestore / Realtime Database • どちらもNoSQLデータベース • リアルタイム更新やオフライン状態での更新などにも対応 • Realtime Databaseの進化版がFirestore • 今回のサンプルアプリではFirestoreを使用 !6

Slide 7

Slide 7 text

Authentication • ユーザー認証のあれこれをやってくれるサービス • 様々な認証方法 • メールアドレス/パスワード認証 • Google/GitHub/Twitter/Facebookなどを用いたSNS認証 • SMS認証機能 • Firebaseの各サービスへの権限管理にも使われる !7

Slide 8

Slide 8 text

Hosting • 静的なWebサイトをホスティングできる • GoogleのCDNが使われる • Let’s Encryptを用いた無料SSL • もちろん独自ドメインも使用可能 • デプロイはCLIツールからワンコマンドで可能 !8

Slide 9

Slide 9 text

料金 • https://firebase.google.com/pricing/ • 趣味で使う程度の規模ならば無料で使用可能 !9

Slide 10

Slide 10 text

実際のコードを見てみる !10

Slide 11

Slide 11 text

サンプルアプリを作った • 2チャンネルクローン? 「8ちゃんねる」 • 八王子の8、ゆるはちの8です… • Reactを用いたSPA && 言語はTypeScript • わかりづらかったらごめんなさい • Firebase Authentication と Firestore を使用 • https://eight-channel.firebaseapp.com • https://github.com/uutarou10/eight-channel !11

Slide 12

Slide 12 text

ざっくりこんな要件 • スレッド作成/新規投稿はGoogle
 ログインが必要 • 閲覧は全ユーザーが可能 • (表示されていないけど)
 投稿ユーザーを記録する !12

Slide 13

Slide 13 text

ディレクトリ構成 • firebase.json / firestore.indexes.json / firestore.rules
 Firebase関係の設定ファイルたち
 FirebaseのCLIツールで生成される • src
 アプリケーション本体のソースコード !13

Slide 14

Slide 14 text

初期化処理 !14

Slide 15

Slide 15 text

初期化 • コンソール画面から取得したAPI Keyなどを専用のメソッドに渡す。 !15

Slide 16

Slide 16 text

初期化 export const app = firebase.initializeApp({ apiKey: process.env.REACT_APP_API_KEY, authDomain: process.env.REACT_APP_AUTH_DOMAIN, databaseURL: process.env.REACT_APP_DB_URL, messagingSenderId: process.env.REACT_APP_MESSAGING_SENDER_ID, projectId: process.env.REACT_APP_PROJECT_ID, storageBucket: process.env.REACT_APP_STORAGE_BUCKET, }); !16 src/util/firebase.ts 環境変数に先ほどの値が入っている

Slide 17

Slide 17 text

Firestore !17

Slide 18

Slide 18 text

データ構造 !18 threadsというcollection

Slide 19

Slide 19 text

データ構造 !19 それぞれのスレッドがdocumentとして 格納されている

Slide 20

Slide 20 text

データ構造 !20 それぞれのthreadsのドキュメント下に postsという別のcollection

Slide 21

Slide 21 text

データ構造 !21 投稿の内容がkey-valueの形で
 入っている

Slide 22

Slide 22 text

コードから扱う - Read export default { getList: async () => { const querySnapshot = await db.collection('threads').orderBy('createdAt', 'desc').get(); const result: Thread[] = []; querySnapshot.forEach(thread => { result.push(createThreadByDocumentSnapshot(thread)); }); return result; }, !22 src/repository/thread.ts

Slide 23

Slide 23 text

コードから扱う - Read const createThreadByDocumentSnapshot = (snapShot:firebase.firestore.DocumentSnapshot): Thread => { const data = snapShot.data() as firebase.firestore.DocumentData; return { id: snapShot.id, title: data.title, uid: data.uid, createdAt: data.createdAt.toDate() }; }; !23 src/repository/thread.ts dataメソッドを呼び出すと 値を取得できる

Slide 24

Slide 24 text

コードから扱う - Write create: async (title: string, uid: string) => { const docRef = await db.collection('threads').add({ title, uid, createdAt: firebase.firestore.FieldValue.serverTimestamp() }); return createThreadByDocumentSnapshot(await docRef.get()); } !24 src/repository/thread.ts

Slide 25

Slide 25 text

ところで • 「クライアントから直接DBが触れるのはわかった。」 • 「何でも誰でも書き放題、読み放題じゃない?」 • 「想定していないデータを突っ込む事ができてしまうのでは?」 !25

Slide 26

Slide 26 text

セキュリティールール • JavaScript風のDSLで操作を実行できる条件を指定する • これを活用して… • 必須カラムを指定 • 権限を設定 • 発行できるクエリに制限をかける !26

Slide 27

Slide 27 text

セキュリティールール service cloud.firestore { match /databases/{database}/documents { match /threads/{thread}{ allow read; allow create: if request.auth.uid == request.resource.data.uid && request.resource.data.keys().hasAll(['title', 'createdAt', 'uid']); match /posts/{post} { allow read; allow create: if request.auth.uid == request.resource.data.uid && request.resource.data.keys().hasAll(['name', 'body', 'uid', 'createdAt']); } } } } !27 firestore.rules

Slide 28

Slide 28 text

セキュリティールール service cloud.firestore { match /databases/{database}/documents { match /threads/{thread}{ allow read; allow create: if request.auth.uid == request.resource.data.uid && request.resource.data.keys().hasAll(['title', 'createdAt', 'uid']); match /posts/{post} { allow read; allow create: if request.auth.uid == request.resource.data.uid && request.resource.data.keys().hasAll(['name', 'body', 'uid', 'createdAt']); } } } } !28 firestore.rules title, createdAt, uidを持っている事 requestしてきたユーザーのuidがリクエストの uidと一致する事

Slide 29

Slide 29 text

Authentication !29

Slide 30

Slide 30 text

Authentication • 事前にConsoleから使いたい認証方法をオンに設定しておく • Google認証やパスワード認証の場合はほとんどオンにするだけ • Google以外のSNS認証の場合はアクセストークンなどを
 別途取得する必要あり !30

Slide 31

Slide 31 text

!31

Slide 32

Slide 32 text

認証を行う export const signInWithGoogle = () => { const provider = new firebase.auth.GoogleAuthProvider(); auth.signInWithRedirect(provider); }; !32 src/util/firebase.ts

Slide 33

Slide 33 text

認証を行う • プロバイダーとしてGoogleを指定して、
 signInWithRedirectを呼ぶだけ。 • これだけでRedirectした上で認証して元のページに戻ってくる !33

Slide 34

Slide 34 text

ハンドラー auth.onAuthStateChanged(user => { store.dispatch(authStateChanged(user)); }); !34 src/App.tsx

Slide 35

Slide 35 text

ハンドラー • 認証状態が変化(ログイン/ログアウト)するとonAuthStateChangedで 設定したハンドラーにユーザーが渡されて呼ばれる • 今回はRedux(状態管理のライブラリ)にユーザーを投げて保持しておく • Google認証の場合はGoogleのAPIを使うためのtokenを取得できる • Googleのサービスと連携したアプリケーションの開発も可 !35

Slide 36

Slide 36 text

まとめ • FirebaseはWebアプリでも超使える! • 素早く何かを作りたい/プロトタイプ作りという用途には最適 • Authのハンドラーなどやや癖がある部分もあるので、Firebaseに依存しないよ うにするには”うまいことする力”が必要だと思った • もしくはFirebaseべったりなコードなるのを覚悟する • セキュリティールールには気をつけないといけない • フロントエンドの勉強をするのに最適だと個人的には感じました。 !36

Slide 37

Slide 37 text

JavaScript要素が少なくてゴメンナサイ おしまい !37