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. /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
  2. DIBSJUZDPO@PLJ !@UBLFTIJ@ ΋͘͡ w 'JSFCBTFͰԿ͕Ͱ͖Δͷ͔ʁ w 'JSFCBTFͰ؆୯Ϣʔβʔೝূ w 'JSFTUPSFͱ3FBMUJNF%BUBCBTF w

    $MPVE'VODUJPOTͰαʔόʔϨε։ൃ w /P42-σʔλϕʔεͷઃܭύλʔϯղઆ w 'JSFCBTF։ൃʹ͓͚ΔΞΫηεݖݶͳͲͷ஫ҙ఺ w ·ͱΊ
  3. '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@
  4. '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@
  5. DIBSJUZDPO@PLJ !@UBLFTIJ@ 'JSFTUPSFͱ3FBMUJNF%BUBCBTF w ϨΠςϯγ͕ඇৗʹ௿͍ w සൟͳঢ়ଶಉظʹ࠷ద w ΫΤϦ͕ශऑ w

    ෳࡶͳσʔλʹ͸ෆ޲͖ w ଳҬ෯ͱετϨʔδʹͷΈ՝ۚ w 3FBMUJNF%BUBCBTFʹൺ΂ɺ ॊೈͳΫΤϦ w ෳࡶͳσʔλʹରԠ w ΦϖϨʔγϣϯ ಡΈऔΓॻ͖ࠐΈ࡟আ ɺ ଳҬ෯ɺετϨʔδʹ՝ۚ ࠓ೔͸ͪ͜Βͷ࿩ɻ
  6. DIBSJUZDPO@PLJ !@UBLFTIJ@ 'JSFTUPSF import firebase from 'firebase' const db =

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

    '==', company) .orderBy('visibleAt', 'desc') .limit(10) const informationSnapshot = await informationQuery.get() ݕࡧ
  8. w ඇਖ਼نԽ w 4VC$PMMFDUJPO w ΩʔࢀরϞσϧ w 3FGFSFODFܕ DIBSJUZDPO@PLJ !@UBLFTIJ@

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

     *E OBNF  ෱Ԭݝ VTFSTςʔϒϧ QSFGFDUVSFTςʔϒϧ VTFST 㸉<6TFS*%> 㸉OBNFlʹ͠z 㸉BHF 㸉QSFGFDUVSFl෱Ԭݝz
  10. 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ࢀরϞσϧ
  11. const diary = await app.firestore() .collection("diaries") .doc([diaryID]) .get() const user

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

    ˠύϑΥʔϚϯε௿Լɻ w ໾৬໊ͳͲ਺͕ݶఆతͳσʔλ͸ϧʔϓͷલʹऔಘͯ͠อ͓࣋ͯ͠ ͍ͯɺϧʔϓ಺ͰϚʔδɻ w ໊ࣾͳͲ΄ͱΜͲมߋ͞Εͳ͍σʔλ͸ඇਖ਼نԽ͓ͯ࣋ͬͯ͘͠ɻ มߋ͕͋ͬͨ৔߹͸ɺόονॲཧͰߋ৽ɻ DIBSJUZDPO@PLJ !@UBLFTIJ@ / ໰୊
  13. 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Ͱɺอ࣋ͨ͠࠷ऴߦϊʔυҎ߱ͷσʔλऔಘ
  14. 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@
  15. ྫ BENJOϢʔβʔ͸؅ཧऀը໘Λར༻Ͱ͖ͯɺҰൠϢʔβʔ͸ ؅ཧऀը໘Λར༻Ͱ͖ͳ͍ɻ await admin.auth().setCustomUserClaims( uid, { admin: true }

    ) if(user.customClaims.admin) { } ΧελϜΫϨʔϜʹʮBENJOʯΛઃఆ BENJOͷ৔߹ͷΈॲཧΛߦ͏ w ΧελϜΫϨʔϜ͸ݖݶ؅ཧͷ৘ใͷΈʹར༻͢Δɻ w Ϣʔβʔͷ৘ใ͸DPMMFDUJPOΛར༻͢Δɻ DIBSJUZDPO@PLJ !@UBLFTIJ@ $VTUPN$MBJNͰݖݶ؅ཧ
  16. 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@
  17. $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 ΫϥΠΞϯτଆͷؔ਺ͱಉ͡Α͏ʹ࣮ߦͰ͖Δɻ
  18. $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); . . .