Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

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

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

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

TakeshiNishi

June 21, 2020
Tweet

More Decks by TakeshiNishi

Other Decks in Programming

Transcript

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

    View Slide

  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

    View Slide

  3. DIBSJUZDPO@PLJ !@UBLFTIJ@
    ΋͘͡
    w 'JSFCBTFͰԿ͕Ͱ͖Δͷ͔ʁ
    w 'JSFCBTFͰ؆୯Ϣʔβʔೝূ
    w 'JSFTUPSFͱ3FBMUJNF%BUBCBTF
    w $MPVE'VODUJPOTͰαʔόʔϨε։ൃ
    w /P42-σʔλϕʔεͷઃܭύλʔϯղઆ
    w 'JSFCBTF։ൃʹ͓͚ΔΞΫηεݖݶͳͲͷ஫ҙ఺
    w ·ͱΊ

    View Slide

  4. 'JSFCBTFͱ͸ʁ
    උ੉ͷϑΫΪฒ໦

    View Slide

  5. 'JSFCBTFͱ͸ʁ
    (PPHMF͕ఏڙ͢Δ
    #BB4 #BDLFOEBTB4FSWJDF

    όοΫΤϯυͷ։ൃʢೝূɺετϨʔδͷ؅ཧɺσʔλϕʔεͷ؅
    ཧͳͲʣ͕جຊతʹෆཁɻ
    ϑϩϯτΤϯυͷٕज़͚ͩͰΞϓϦ։ൃ͕Ͱ͖Δɻ
    DIBSJUZDPO@PLJ !@UBLFTIJ@

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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@

    View Slide

  11. 'JSFCBTFͰ؆୯Ϣʔβʔೝূ



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

    View Slide

  12. 'JSFCBTFͰ؆୯Ϣʔβʔೝূ
    'JSFCBTF6*ͰϩάΠϯը໘΋؆୯࣮૷
    DIBSJUZDPO@PLJ !@UBLFTIJ@

    View Slide

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

    View Slide

  14. DIBSJUZDPO@PLJ !@UBLFTIJ@
    'JSFTUPSFͱ3FBMUJNF%BUBCBTF
    w ϨΠςϯγ͕ඇৗʹ௿͍
    w සൟͳঢ়ଶಉظʹ࠷ద
    w ΫΤϦ͕ශऑ
    w ෳࡶͳσʔλʹ͸ෆ޲͖
    w ଳҬ෯ͱετϨʔδʹͷΈ՝ۚ
    w 3FBMUJNF%BUBCBTFʹൺ΂ɺ
    ॊೈͳΫΤϦ
    w ෳࡶͳσʔλʹରԠ
    w ΦϖϨʔγϣϯ
    ಡΈऔΓॻ͖ࠐΈ࡟আ
    ɺ
    ଳҬ෯ɺετϨʔδʹ՝ۚ
    ࠓ೔͸ͪ͜Βͷ࿩ɻ

    View Slide

  15. DIBSJUZDPO@PLJ !@UBLFTIJ@
    'JSFTUPSF
    ίϨΫγϣϯυΩϡϝϯτϑΟʔϧυ͔ΒͳΔ/P42-σʔλϕʔε

    View Slide

  16. DIBSJUZDPO@PLJ !@UBLFTIJ@
    'JSFTUPSF
    import firebase from 'firebase'
    const db = firebase.firestore()
    db.collection('information').add({
    title: 'λΠτϧ',
    description: 'ຊจ'
    })
    ௥Ճ

    View Slide

  17. DIBSJUZDPO@PLJ !@UBLFTIJ@
    'JSFTUPSF
    ߋ৽
    db.collection('information')
    .doc(this.id)
    .update(
    {readAt: firebase.firestore.FieldValue
    .serverTimestamp()
    }
    )
    ࡟আ
    db.collection('information')
    .doc(this.id)
    .delete()

    View Slide

  18. DIBSJUZDPO@PLJ !@UBLFTIJ@
    'JSFTUPSF
    const informationQuery = await db
    .collection('information')
    .where('company', '==', company)
    .orderBy('visibleAt', 'desc')
    .limit(10)
    const informationSnapshot
    = await informationQuery.get()
    ݕࡧ

    View Slide

  19. /P42-σʔλϕʔεͷ
    ઃܭύλʔϯղઆ
    ΨϯΨϥʔͷ୩

    View Slide

  20. w ඇਖ਼نԽ
    w 4VC$PMMFDUJPO
    w ΩʔࢀরϞσϧ
    w 3FGFSFODFܕ
    DIBSJUZDPO@PLJ !@UBLFTIJ@
    'JSFTUPSFͷϦϨʔγϣϯσʔλͷऔΓѻ͍
    3%#ͱ͸ҧ͍ɺ
    +0*/͠ͳ͍
    ϦϨʔγϣϯσʔλͲ͏औΓѻ͏ʁ

    View Slide

  21. DIBSJUZDPO@PLJ !@UBLFTIJ@
    ඇਖ਼نԽ
    *E OBNF BHF QSFGFDUVSF@JE
    ʹ͠
    *E OBNF
    ෱Ԭݝ
    VTFSTςʔϒϧ
    QSFGFDUVSFTςʔϒϧ
    VTFST
    㸉<6TFS*%>
    㸉OBNFlʹ͠z
    㸉BHF
    㸉QSFGFDUVSFl෱Ԭݝz

    View Slide

  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

    View Slide

  23. const snapshot = await app.firestore()
    .collectionGroup("diaries")
    .where()
    .get()
    wDPMMFDUJPO(SPVQͰ4VC$PMMFDUJPOΛԣஅతʹݕࡧՄೳɻ
    DIBSJUZDPO@PLJ !@UBLFTIJ@
    $PMMFDUJPO(SPVQ

    View Slide

  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ࢀরϞσϧ

    View Slide

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

    View Slide

  26. EJBSJFT
    㸉<%JBSZ*%>
    c㸉UJUMFlλΠτϧz
    c㸉DPOUFOUlຊจɻຊจɻz
    c㸉VTFSVTFST
    㸉<%JBSZ*%>
    c㸉UJUMFlλΠτϧz
    c㸉DPOUFOUlຊจɻຊจɻz
    c㸉VTFSVTFST
    w/ͷ਌ࢠؔ܎ͷσʔλͰ͸3FGFSFODFܕΛར༻ɻ
    DIBSJUZDPO@PLJ !@UBLFTIJ@
    3FGFSFODFܕ

    View Slide

  27. const diary = await app.firestore()
    .collection("diaries")
    .doc([diaryID])
    .get()
    const user = diary.data().user.get()
    w ࢠͷσʔλͷऔಘ͕༰қɻ
    DIBSJUZDPO@PLJ !@UBLFTIJ@
    3FGFSFODFܕͷσʔλऔಘ

    View Slide

  28. db.collection(“diaries”).get()
    .then(function(snapshot) {
    snapshot(function(diary) {
    diary.data().user.get()
    })
    })
    /໰୊ɻϧʔϓ͝ͱʹϦΫΤετɻ
    ˠύϑΥʔϚϯε௿Լɻ
    w ໾৬໊ͳͲ਺͕ݶఆతͳσʔλ͸ϧʔϓͷલʹऔಘͯ͠อ͓࣋ͯ͠
    ͍ͯɺϧʔϓ಺ͰϚʔδɻ
    w ໊ࣾͳͲ΄ͱΜͲมߋ͞Εͳ͍σʔλ͸ඇਖ਼نԽ͓ͯ࣋ͬͯ͘͠ɻ
    มߋ͕͋ͬͨ৔߹͸ɺόονॲཧͰߋ৽ɻ
    DIBSJUZDPO@PLJ !@UBLFTIJ@
    /໰୊

    View Slide

  29. w ϖʔδωʔγϣϯ͢ΔͨΊʹ͸શମͷσʔλαΠζ͕ඞཁɻ
    w 'JSFTUPSFͰ͸શ݅औಘͯ͠ΫϥΠΞϯτଆͰDPVOU͢Δ͔͠ͳ͍ɻ
    w ݕࡧ৚݅Ͱ࠷దʹߜΓࠐΉ͜ͱΛߟ͑Δํ͕#FUUFSɻ
    DIBSJUZDPO@PLJ !@UBLFTIJ@
    ϖʔδωʔγϣϯ͠ͳ͍

    View Slide

  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Ͱɺอ࣋ͨ͠࠷ऴߦϊʔυҎ߱ͷσʔλऔಘ

    View Slide

  31. 'JSFCBTF։ൃʹ͓͚Δ
    ΞΫηεݖݶͳͲͷ
    ஫ҙ఺ղઆ
    ྻं͕૸Δ৿ΧϑΣʮHZVUUPʯ

    View Slide

  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@

    View Slide


  33. Ϣʔβʔͷࢯ໊ɺॅॴɺ࿈བྷઌͳͲͷݸਓ৘ใͱɺχοΫωʔ
    ϜɺϓϩϑΟʔϧࣸਅͳͲͷެ։৘ใΛ࣋ͭσʔλ
    VTFST
    㸉<6TFS*%>
    㸉OJDLOBNFlʹ͠z
    㸉QSPpMFlYYYYYYYKQHz
    㸉OBNFl੢෢࢙z
    㸉BEESFTTl෱Ԭࢢ౦۠ɾɾɾz
    㸉FNBJMlUBLFTIJ!IPHFIPHFDPNz

    ϑΟʔϧυ୯ҐͰηΩϡϦςΟϧʔϧઃఆ
    Ͱ͖ͳ͍ͷͰɺެ։͞Εͯ͠·͏ʂʂ
    ηΩϡΞͳσʔλઃܭ
    DIBSJUZDPO@PLJ !@UBLFTIJ@

    View Slide

  34. VTFST
    㸉<6TFS*%>
    㸉OJDLOBNFlʹ͠z
    㸉QSPpMFlYYYYYYYKQHz
    QSJWBUFT
    㸉<6TFS*%>
    㸉OBNFl੢෢࢙z
    㸉BEESFTTl෱Ԭࢢ౦۠ɾɾɾz
    㸉FNBJMlUBLFTIJ!IPHFIPHFDPNz

    DPMMFDUJPOΛ෼͚Δɻ
    ηΩϡΞͳσʔλઃܭ
    DIBSJUZDPO@PLJ !@UBLFTIJ@

    View Slide


  35. BENJOϢʔβʔ͸؅ཧऀը໘Λར༻Ͱ͖ͯɺҰൠϢʔβʔ͸
    ؅ཧऀը໘Λར༻Ͱ͖ͳ͍ɻ
    await admin.auth().setCustomUserClaims(
    uid, { admin: true }
    )
    if(user.customClaims.admin) {
    }
    ΧελϜΫϨʔϜʹʮBENJOʯΛઃఆ
    BENJOͷ৔߹ͷΈॲཧΛߦ͏
    w ΧελϜΫϨʔϜ͸ݖݶ؅ཧͷ৘ใͷΈʹར༻͢Δɻ
    w Ϣʔβʔͷ৘ใ͸DPMMFDUJPOΛར༻͢Δɻ
    DIBSJUZDPO@PLJ !@UBLFTIJ@
    $VTUPN$MBJNͰݖݶ؅ཧ

    View Slide

  36. $MPVE'VODUJPOT
    ͰαʔόʔϨε։ൃ
    ѥ೤ଳ஡԰

    View Slide

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

    View Slide

  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@

    View Slide

  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 ΫϥΠΞϯτଆͷؔ਺ͱಉ͡Α͏ʹ࣮ߦͰ͖Δɻ

    View Slide

  40. $MPVE'VODUJPOTͷ࢖͍Ͳ͜Ζ
    DIBSJUZDPO@PLJ !@UBLFTIJ@
    w $MPVE42-ͳͲ($1ͷػೳΛ࢖͍͍ͨɻ
    w 'JSFTUPSF΁ͷෳࡶͳτϥϯβΫγϣϯͷॻ͖ࠐΈɻ
    w 'JSFTUPSBHF΁ͷϑΝΠϧΞοϓϩʔυͱ'JSFTUPSF΁ͷॻ͖ࠐΈɻ
    w ֎෦αʔϏεͱͷ࿈ܞɻ

    View Slide

  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);
    . . .

    View Slide

  42. $MPVE'VODUJPOTͰఆظ࣮ߦ
    $MPVE'VODUJPOTͰఆظ࣮ߦ
    DIBSJUZDPO@PLJ !@UBLFTIJ@
    exports.setConfig = pubsub
    .schedule("every 5 minutes")
    .onRun(async (context) => {
    . . .

    View Slide

  43. ·ͱΊ
    ݹӉརౡ

    View Slide

  44. ·ͱΊ
    DIBSJUZDPO@PLJ !@UBLFTIJ@
    w 'JSFCBTFΛ࢖͏͜ͱͰɺجຊతʹϑϩϯτΤϯυͷ։ൃٕज़͚ͩͰ
    ӬଓతͳσʔλΛ࢖ͬͨΞϓϦ։ൃՄೳɻ
    w αʔόʔͷεέʔϧ΍؅ཧ͕ෆཁͳͷͰɺΞϓϦ։ൃʹूதͰ͖Δɻ
    w ݕࡧͷॊೈੑ͸௿͍ɻ
    w 3%#ʹ׳Ε਌͠Μͩਓͩͱɺ/P42-ͷσʔλߏ଄΁ͷରԠʹ͕࣌ؒ
    ͔͔Δɻ
    w %#ઃܭ͕ॏཁɻ
    w ϕϯμʔϩοΫΠϯ

    View Slide

  45. ࠓ೔ͷ࿩͕͋ͳͨͷখ͞ͳҰาʹ
    ͭͳ͕Γ·͢Α͏ʹɻ

    View Slide

  46. એ఻
    !@UBLFTIJ@
    ڞײͰະདྷͷ஥ؒͱܨ͕Δ

    (͚Ͳͳ͔ͳ͔ܨ͕Βͳ͍)SNS
    FLAPTALKۙ೔ެ։
    ࣾһืूதʂʂ

    View Slide