フロントエンドエンジニアのためのFirebaseサーバーレス開発徹底解説

 フロントエンドエンジニアのためのFirebaseサーバーレス開発徹底解説

「チャリティカンファレンス沖縄2020 Vol.1 フロントエンド」での登壇資料です。

これまで様々な開発案件でFirebaseを用いたサーバーレス開発を実践してきた経験をもとに、以下のポイントを徹底解説します。
・Firebaseで何ができるのか?
・Firebaseで簡単ユーザー認証。
・CloudFunctionsでもっと柔軟にサーバーレス開発。
・NoSQLデータベースの設計パターン解説。
・Firebase開発におけるアクセス権限などの注意点。

508d2cf1990876daf4797795a2d81bd2?s=128

TakeshiNishi

June 21, 2020
Tweet

Transcript

  1. DIBSJUZDPO@PLJ !@UBLFTIJ@ ϑϩϯτΤϯυΤϯδχΞͷͨΊͷ FirebaseαʔόʔϨε։ൃపఈղઆ

  2. /VYUKT'JSFCBTF7VFKT3VCZ($1ϒϩοΫνΣʔϯ ελʔτΞοϓىۀ ԭೄେ޷͖ԹઘΩϟϯϓཱྀߦ ੢෢࢙ ʹ͚ͨ͠͠ !@UBLFTIJ@ w ෱Ԭͷ4*FSͰγεςϜΤϯδχΞ w ϑϦʔϥϯεϓϩάϥϚʔ

    w ౦ژͷελʔτΞοϓ$P'PVOEFSऔక໾$50 w ϑϦʔϥϯεϓϩάϥϚʔ w גࣜձࣾEJ⒎FBTZऔక໾$50 w גࣜձࣾ0OF4NBMM4UFQ୅දऔక໾$50ˡࠓ͜͜ DIBSJUZDPO@PLJ !@UBLFTIJ@ ࣗݾ঺հ גࣜձࣾ0OF4NBMM4UFQ ୅දऔక໾$50
  3. DIBSJUZDPO@PLJ !@UBLFTIJ@ ΋͘͡ w 'JSFCBTFͰԿ͕Ͱ͖Δͷ͔ʁ w 'JSFCBTFͰ؆୯Ϣʔβʔೝূ w 'JSFTUPSFͱ3FBMUJNF%BUBCBTF w

    $MPVE'VODUJPOTͰαʔόʔϨε։ൃ w /P42-σʔλϕʔεͷઃܭύλʔϯղઆ w 'JSFCBTF։ൃʹ͓͚ΔΞΫηεݖݶͳͲͷ஫ҙ఺ w ·ͱΊ
  4. 'JSFCBTFͱ͸ʁ උ੉ͷϑΫΪฒ໦

  5. 'JSFCBTFͱ͸ʁ (PPHMF͕ఏڙ͢Δ #BB4 #BDLFOEBTB4FSWJDF  όοΫΤϯυͷ։ൃʢೝূɺετϨʔδͷ؅ཧɺσʔλϕʔεͷ؅ ཧͳͲʣ͕جຊతʹෆཁɻ ϑϩϯτΤϯυͷٕज़͚ͩͰΞϓϦ։ൃ͕Ͱ͖Δɻ DIBSJUZDPO@PLJ !@UBLFTIJ@

  6. 'JSFCBTFͰԿ͕Ͱ͖Δͷʁ DIBSJUZDPO@PLJ !@UBLFTIJ@

  7. 'JSFCBTFΛར༻Ͱ͖Δ؀ڥ DIBSJUZDPO@PLJ !@UBLFTIJ@

  8. 'JSFCBTFͰ ؆୯Ϣʔβʔೝূ ܚࠤ࣍઒ϚϯάϩʔϒΧψʔ

  9. 'JSFCBTFͰ؆୯Ϣʔβʔೝূ ϝʔϧ ύεϫʔυೝূ΍ ֤छ 4/4ೝূ͕؆୯ DIBSJUZDPO@PLJ !@UBLFTIJ@

  10. 'JSFCBTFͰ؆୯Ϣʔβʔೝূ firebase .auth() .createUserWithEmailAndPassword(email, password) ϝʔϧΞυϨεͱύεϫʔυͰϢʔβʔ࡞੒ firebase .auth() .signInWithEmailAndPassword(email, password)

    ϝʔϧΞυϨεͱύεϫʔυͰϩάΠϯ var provider = new firebase.auth.TwitterAuthProvider() firebase.auth().signInWithPopup(provider) 4/4ϩάΠϯ DIBSJUZDPO@PLJ !@UBLFTIJ@
  11. 'JSFCBTFͰ؆୯Ϣʔβʔೝূ <template> <div id="firebaseui-auth-container"></div> </template> <script> const ui = new

    firebaseui.auth.AuthUI(firebase.auth()) ui.start("#firebaseui-auth-container", { signInOptions: [ firebase.auth.GoogleAuthProvider.PROVIDER_ID, firebase.auth.FacebookAuthProvider.PROVIDER_ID, firebase.auth.TwitterAuthProvider.PROVIDER_ID, //ɾɾɾ ] }) </script> 'JSFCBTF6*ͰϩάΠϯը໘΋؆୯࣮૷ DIBSJUZDPO@PLJ !@UBLFTIJ@
  12. 'JSFCBTFͰ؆୯Ϣʔβʔೝূ 'JSFCBTF6*ͰϩάΠϯը໘΋؆୯࣮૷ DIBSJUZDPO@PLJ !@UBLFTIJ@

  13. 'JSFTUPSFͱ 3FBMUJNF%BUBCBTF ੢ࢅڮ ஛෋ౡ 

  14. DIBSJUZDPO@PLJ !@UBLFTIJ@ 'JSFTUPSFͱ3FBMUJNF%BUBCBTF w ϨΠςϯγ͕ඇৗʹ௿͍ w සൟͳঢ়ଶಉظʹ࠷ద w ΫΤϦ͕ශऑ w

    ෳࡶͳσʔλʹ͸ෆ޲͖ w ଳҬ෯ͱετϨʔδʹͷΈ՝ۚ w 3FBMUJNF%BUBCBTFʹൺ΂ɺ ॊೈͳΫΤϦ w ෳࡶͳσʔλʹରԠ w ΦϖϨʔγϣϯ ಡΈऔΓॻ͖ࠐΈ࡟আ ɺ ଳҬ෯ɺετϨʔδʹ՝ۚ ࠓ೔͸ͪ͜Βͷ࿩ɻ
  15. DIBSJUZDPO@PLJ !@UBLFTIJ@ 'JSFTUPSF ίϨΫγϣϯυΩϡϝϯτϑΟʔϧυ͔ΒͳΔ/P42-σʔλϕʔε

  16. DIBSJUZDPO@PLJ !@UBLFTIJ@ 'JSFTUPSF import firebase from 'firebase' const db =

    firebase.firestore() db.collection('information').add({ title: 'λΠτϧ', description: 'ຊจ' }) ௥Ճ
  17. DIBSJUZDPO@PLJ !@UBLFTIJ@ 'JSFTUPSF ߋ৽ db.collection('information') .doc(this.id) .update( {readAt: firebase.firestore.FieldValue .serverTimestamp()

    } ) ࡟আ db.collection('information') .doc(this.id) .delete()
  18. DIBSJUZDPO@PLJ !@UBLFTIJ@ 'JSFTUPSF const informationQuery = await db .collection('information') .where('company',

    '==', company) .orderBy('visibleAt', 'desc') .limit(10) const informationSnapshot = await informationQuery.get() ݕࡧ
  19. /P42-σʔλϕʔεͷ ઃܭύλʔϯղઆ ΨϯΨϥʔͷ୩

  20. w ඇਖ਼نԽ w 4VC$PMMFDUJPO w ΩʔࢀরϞσϧ w 3FGFSFODFܕ DIBSJUZDPO@PLJ !@UBLFTIJ@

    'JSFTUPSFͷϦϨʔγϣϯσʔλͷऔΓѻ͍ 3%#ͱ͸ҧ͍ɺ +0*/͠ͳ͍ ϦϨʔγϣϯσʔλͲ͏औΓѻ͏ʁ
  21. DIBSJUZDPO@PLJ !@UBLFTIJ@ ඇਖ਼نԽ *E OBNF BHF QSFGFDUVSF@JE  ʹ͠ 

     *E OBNF  ෱Ԭݝ VTFSTςʔϒϧ QSFGFDUVSFTςʔϒϧ VTFST 㸉<6TFS*%> 㸉OBNFlʹ͠z 㸉BHF 㸉QSFGFDUVSFl෱Ԭݝz
  22. VTFST 㸉<6TFS*%> 㸉OBNFlʹ͠z 㸉BHF 㸉EJBSJFT 㸉<%JBSZ*%> c㸉UJUMFlλΠτϧz c㸉DPOUFOUlຊจɻຊจɻz 㸉<%JBSZ*%> c㸉UJUMFlλΠτϧz

    c㸉DPOUFOUlຊจɻຊจɻz wσʔλͷؔ܎͕Θ͔Γ΍͍͢ɻ w ֊૚͕ਂ͍ͱΘ͔Γʹ͘͘ͳΔɻ DIBSJUZDPO@PLJ !@UBLFTIJ@ 4VC$PMMFDUJPO
  23. const snapshot = await app.firestore() .collectionGroup("diaries") .where() .get() wDPMMFDUJPO(SPVQͰ4VC$PMMFDUJPOΛԣஅతʹݕࡧՄೳɻ DIBSJUZDPO@PLJ

    !@UBLFTIJ@ $PMMFDUJPO(SPVQ
  24. VTFST 㸉<6TFS*%> 㸉OBNFlʹ͠z 㸉BHF EJBSJFT 㸉<%JBSZ*%> c㸉VTFS*E c㸉UJUMFlλΠτϧz c㸉DPOUFOUlຊจɻຊจɻz 㸉<%JBSZ*%>

    c㸉VTFS*E c㸉UJUMFlλΠτϧz c㸉DPOUFOUlຊจɻຊจɻz DIBSJUZDPO@PLJ !@UBLFTIJ@ ,FZࢀরϞσϧ
  25.  ୯७ͳ਌ࢠؔ܎ͩͬͨΒɺ4VC$PMMFDUJPO  ݕࡧͷύλʔϯ͕ෳ਺͋Ε͹ɺ,FZࢀরϞσϧ  ֊૚͕ਂ͘ͳΔ΋ͷ͸ɺ,FZࢀরϞσϧ 4VC$PMMFDUJPOͱ,FZࢀরϞσϧͲͪΒΛ࢖͏͔ʁ ൑அج४͸ʁʁ ࢲͷ൑அج४ DIBSJUZDPO@PLJ

    !@UBLFTIJ@ 4VC$PMMFDUJPOPS,FZࢀরϞσϧ
  26. EJBSJFT 㸉<%JBSZ*%> c㸉UJUMFlλΠτϧz c㸉DPOUFOUlຊจɻຊจɻz c㸉VTFSVTFST<VTFS*%> 㸉<%JBSZ*%> c㸉UJUMFlλΠτϧz c㸉DPOUFOUlຊจɻຊจɻz c㸉VTFSVTFST<VTFS*%> w/ͷ਌ࢠؔ܎ͷσʔλͰ͸3FGFSFODFܕΛར༻ɻ

    DIBSJUZDPO@PLJ !@UBLFTIJ@ 3FGFSFODFܕ
  27. const diary = await app.firestore() .collection("diaries") .doc([diaryID]) .get() const user

    = diary.data().user.get() w ࢠͷσʔλͷऔಘ͕༰қɻ DIBSJUZDPO@PLJ !@UBLFTIJ@ 3FGFSFODFܕͷσʔλऔಘ
  28. db.collection(“diaries”).get() .then(function(snapshot) { snapshot(function(diary) { diary.data().user.get() }) }) / ໰୊ɻϧʔϓ͝ͱʹϦΫΤετɻ

    ˠύϑΥʔϚϯε௿Լɻ w ໾৬໊ͳͲ਺͕ݶఆతͳσʔλ͸ϧʔϓͷલʹऔಘͯ͠อ͓࣋ͯ͠ ͍ͯɺϧʔϓ಺ͰϚʔδɻ w ໊ࣾͳͲ΄ͱΜͲมߋ͞Εͳ͍σʔλ͸ඇਖ਼نԽ͓ͯ࣋ͬͯ͘͠ɻ มߋ͕͋ͬͨ৔߹͸ɺόονॲཧͰߋ৽ɻ DIBSJUZDPO@PLJ !@UBLFTIJ@ / ໰୊
  29. w ϖʔδωʔγϣϯ͢ΔͨΊʹ͸શମͷσʔλαΠζ͕ඞཁɻ w 'JSFTUPSFͰ͸શ݅औಘͯ͠ΫϥΠΞϯτଆͰDPVOU͢Δ͔͠ͳ͍ɻ w ݕࡧ৚݅Ͱ࠷దʹߜΓࠐΉ͜ͱΛߟ͑Δํ͕#FUUFSɻ DIBSJUZDPO@PLJ !@UBLFTIJ@ ϖʔδωʔγϣϯ͠ͳ͍

  30. w ॻ͖ࠐΈ࣌ʹ݅਺ϑΟʔϧυΛΠϯΫϦϝϯτ͠ɺ݅਺ϑΟʔϧυ͔ Βσʔλ݅਺Λऔಘɻ w લϖʔδɺ࣍ϖʔδͷΈͷϖʔδωʔγϣϯɺ΋͘͠͸ʮ͞ΒʹಡΈ ࠐΉʯͰදࣔɻ DIBSJUZDPO@PLJ !@UBLFTIJ@ Ͳ͏ͯ͠΋ϖʔδωʔγϣϯ͍ͨ͠ this.lastVisibles[this.page]

    = snapshot.docs[snapshot.docs.length - 1] db.collection('information') .startAfter(this.lastVisibles[this.page - 1]) .limit(10) ϖʔδ͝ͱʹ࠷ऴߦͷϊʔυΛอ࣋ TUBSU"GUFSͰɺอ࣋ͨ͠࠷ऴߦϊʔυҎ߱ͷσʔλऔಘ
  31. 'JSFCBTF։ൃʹ͓͚Δ ΞΫηεݖݶͳͲͷ ஫ҙ఺ղઆ ྻं͕૸Δ৿ΧϑΣʮHZVUUPʯ

  32. service cloud.firestore { match /databases/{database}/documents { match /users/{userId} { allow

    read; allow create: if request.auth.uid != null; allow update, delete: if request.auth.uid == userId; } ɹ} } جຊతʹϑϩϯτΤϯυ͔Β௚઀ૢ࡞Ͱ͖ΔͷͰɺϑϩϯτΤ ϯυଆͰ6TFS*%Λِ૷Մೳɻ 'JSFTUPSFͷηΩϡϦςΟϧʔϧͰݖݶͳͲΛ੍ݶɻ ηΩϡϦςΟϧʔϧͷઃఆ DIBSJUZDPO@PLJ !@UBLFTIJ@
  33. ྫ Ϣʔβʔͷࢯ໊ɺॅॴɺ࿈བྷઌͳͲͷݸਓ৘ใͱɺχοΫωʔ ϜɺϓϩϑΟʔϧࣸਅͳͲͷެ։৘ใΛ࣋ͭσʔλ VTFST 㸉<6TFS*%> 㸉OJDLOBNFlʹ͠z 㸉QSPpMFlYYYYYYYKQHz 㸉OBNFl੢෢࢙z 㸉BEESFTTl෱Ԭࢢ౦۠ɾɾɾz 㸉FNBJMlUBLFTIJ!IPHFIPHFDPNz

     ϑΟʔϧυ୯ҐͰηΩϡϦςΟϧʔϧઃఆ Ͱ͖ͳ͍ͷͰɺެ։͞Εͯ͠·͏ʂʂ ηΩϡΞͳσʔλઃܭ DIBSJUZDPO@PLJ !@UBLFTIJ@
  34. VTFST 㸉<6TFS*%> 㸉OJDLOBNFlʹ͠z 㸉QSPpMFlYYYYYYYKQHz QSJWBUFT 㸉<6TFS*%> 㸉OBNFl੢෢࢙z 㸉BEESFTTl෱Ԭࢢ౦۠ɾɾɾz 㸉FNBJMlUBLFTIJ!IPHFIPHFDPNz 

    DPMMFDUJPOΛ෼͚Δɻ ηΩϡΞͳσʔλઃܭ DIBSJUZDPO@PLJ !@UBLFTIJ@
  35. ྫ BENJOϢʔβʔ͸؅ཧऀը໘Λར༻Ͱ͖ͯɺҰൠϢʔβʔ͸ ؅ཧऀը໘Λར༻Ͱ͖ͳ͍ɻ await admin.auth().setCustomUserClaims( uid, { admin: true }

    ) if(user.customClaims.admin) { } ΧελϜΫϨʔϜʹʮBENJOʯΛઃఆ BENJOͷ৔߹ͷΈॲཧΛߦ͏ w ΧελϜΫϨʔϜ͸ݖݶ؅ཧͷ৘ใͷΈʹར༻͢Δɻ w Ϣʔβʔͷ৘ใ͸DPMMFDUJPOΛར༻͢Δɻ DIBSJUZDPO@PLJ !@UBLFTIJ@ $VTUPN$MBJNͰݖݶ؅ཧ
  36. $MPVE'VODUJPOT ͰαʔόʔϨε։ൃ ѥ೤ଳ஡԰

  37. $MPVE'VODUJPOTͱ͸ʁ DIBSJUZDPO@PLJ !@UBLFTIJ@ )5514ͳͲͷϦΫΤετΛτϦΨʔʹ όοΫΤϯυίʔυΛ࣮ߦͰ͖ΔαʔόʔϨεϑϨʔϜϫʔΫ

  38. exports.updateUser = https.onCall( async (data, context) => { if (context

    && context.auth) { await db.collection(“users”) .doc(context.auth.uid) .update({ name: data.name }) return { status: "success" } } } ) $MPVE'VODUJPOTΛ࣮ߦ $MPVE'VODUJPOTʹVQEBUF6TFSΛ௥Ճ DIBSJUZDPO@PLJ !@UBLFTIJ@
  39. $MPVE'VODUJPOTΛ࣮ߦ $MPVE'VODUJPOTͷVQEBUF6TFSΛ࣮ߦ DIBSJUZDPO@PLJ !@UBLFTIJ@ import { functions } from '@/plugins/firebase'

    const updateUser = functions.httpsCallable('updateUser') const result = await updateUser({ name: ‘੢ɹ෢࢙' }) w )551ϦΫΤετ $034ೝূͷτʔΫϯ ͳͲҙࣝ͠ͳͯ͘ྑ͍ɻ w ΫϥΠΞϯτଆͷؔ਺ͱಉ͡Α͏ʹ࣮ߦͰ͖Δɻ
  40. $MPVE'VODUJPOTͷ࢖͍Ͳ͜Ζ DIBSJUZDPO@PLJ !@UBLFTIJ@ w $MPVE42-ͳͲ($1ͷػೳΛ࢖͍͍ͨɻ w 'JSFTUPSF΁ͷෳࡶͳτϥϯβΫγϣϯͷॻ͖ࠐΈɻ w 'JSFTUPSBHF΁ͷϑΝΠϧΞοϓϩʔυͱ'JSFTUPSF΁ͷॻ͖ࠐΈɻ w

    ֎෦αʔϏεͱͷ࿈ܞɻ
  41. $MPVE'VODUJPOTͷτϦΨʔ 'JSFTUPSF΁ͷॻ͖ࠐΈΛτϦΨʔʹ$MPVE'VODUJPOTΛىಈ DIBSJUZDPO@PLJ !@UBLFTIJ@ exports.sendPushMessage = firestore .document("messages/{userId}") .onWrite(async (change,

    context) => { const data = change.after.data(); const previousData = change.before.data(); // uid͔Β௨஌ઌͷϢʔβʔ৘ใΛऔಘ const userRef = await admin .firestore() .collection("users") .doc(context.params.userId); . . .
  42. $MPVE'VODUJPOTͰఆظ࣮ߦ $MPVE'VODUJPOTͰఆظ࣮ߦ DIBSJUZDPO@PLJ !@UBLFTIJ@ exports.setConfig = pubsub .schedule("every 5 minutes")

    .onRun(async (context) => { . . .
  43. ·ͱΊ ݹӉརౡ

  44. ·ͱΊ DIBSJUZDPO@PLJ !@UBLFTIJ@ w 'JSFCBTFΛ࢖͏͜ͱͰɺجຊతʹϑϩϯτΤϯυͷ։ൃٕज़͚ͩͰ ӬଓతͳσʔλΛ࢖ͬͨΞϓϦ։ൃՄೳɻ w αʔόʔͷεέʔϧ΍؅ཧ͕ෆཁͳͷͰɺΞϓϦ։ൃʹूதͰ͖Δɻ w ݕࡧͷॊೈੑ͸௿͍ɻ

    w 3%#ʹ׳Ε਌͠Μͩਓͩͱɺ/P42-ͷσʔλߏ଄΁ͷରԠʹ͕࣌ؒ ͔͔Δɻ w %#ઃܭ͕ॏཁɻ w ϕϯμʔϩοΫΠϯ
  45. ࠓ೔ͷ࿩͕͋ͳͨͷখ͞ͳҰาʹ ͭͳ͕Γ·͢Α͏ʹɻ

  46. એ఻ !@UBLFTIJ@ ڞײͰະདྷͷ஥ؒͱܨ͕Δ
 (͚Ͳͳ͔ͳ͔ܨ͕Βͳ͍)SNS FLAPTALKۙ೔ެ։ ࣾһืूதʂʂ