รู้จัก Cloud Functions for Firebase ตั้งแต่ Zero จนเป็น Hero
ในการพัฒนาแอพพลิเคชันนั้น บางครั้งเราก็ไม่สามารถใส่ Logic ทั้งหมดไปไว้ใน Client แอพได้ ไม่ว่าจะด้วยเหตุผลเรื่องความปลอดภัย, การทำงานที่ซับซ้อน หรือจะเป็นการลดการใช้งานทรัพยากรในเครื่อง Client ก็ตาม ทางออกของเราโดยทั่วไปก็คือ จัดเตรียม server เองซึ่งอาจต้องคำนึงถึงการ scalable ความปลอดภัย จากนั้นก็ลง server script ให้เรียบร้อย แล้วก็ลงมือเขียนโปรแกรมไม่ว่าจะเป็น API หรือ SDK ที่ต้องรองรับทั้ง iOS, Android และ Web ตามภาพด้านล่างนี้
เป็นไง แค่คิดก็เยอะละ จริงไหม เอาหละในบทความนี้เรามาทำความรู้จักกับบริการที่จะช่วยเตรียมทุกสิ่งทุกอย่างที่เล่ามาให้คุณพร้อมใช้งาน เพียงแค่คุณเขียนโปรแกรม ตาม Logic หรือ Business logic ที่คุณต้องการก็พอ และนั่นก็คือ Cloud Functions for Firebase
Cloud Functions for Firebase
คือบริการที่ทำงานในฝั่ง server เพื่อตอบรับการ trigger จากบริการต่างๆใน Firebase โดยฟังก์ชันที่เราเขียนขึ้นมาทั้งหมดจะถูกเก็บไว้ที่ Google Cloud ซึ่งจะดูแลเรื่องความปลอดภัย ความเสถียร และการ scalable ให้เราแล้ว
Run functions server side in a scalable Cloud triggered by Firebase events.
การทำงานหลังจากเรา deploy โค้ดขึ้นไปที่ Cloud Functions for Firebase แล้ว ตัว Cloud Functions จะทำตัวเป็นนักดักฟัง (Listener) และรอรับ trigger จากบริการของ Firebase ทันที และจะทำงานตามฟังก์ชันที่เราเขียนไว้ เช่น ส่งอีเมล, ส่ง Push Notification, สร้าง Thumbnail, กรองคำหยาบ, ต่อ Google APIs, ต่อ Third-party APIs(แพ๊คเกจที่ไม่ใช่ Spark), นับจำนวน Child ที่มี และอื่นๆอีกเพียบ
ปัจจุบันมีบริการของ Firebase ที่ Integrate เข้ากับ Cloud Functions for Firebase แล้วด้วยกัน 5 บริการตามภาพด้านล่างเลย
การติดตั้ง
ก่อนจะไปดูตัวอย่างการทำงานของทั้ง 5 บริการที่ Integrate เข้ากับ Cloud Functions for Firebase แล้วเรามาดูวิธีการ ซีตุ๊บ(Setup) กันก่อน
1. Setup a Firebase Project
ขั้นตอนนี้ ถ้าใครมีโปรเจคกับ Firebase อยู่แล้วให้ข้ามขั้นตอนนี้ไป
ส่วนใครที่ยังไม่เคยมีหรือต้องกาสร้างใหม่ให้ไปที่ Firebase Console จากนั้นกด add project ระบุรายละเอียด และกด Creat project
เมื่อเข้าสู่โปรเจคที่เราสร้าง สิ่งที่แรกที่ต้องทำคือ…ให้ไปเปลี่ยนแพลนจาก Spark เป็นเป็น Blaze(Pay as you go) ก่อน เนื่องจากการจะ deploy ฟังก์ชันขึ้น production จะต้องใช้แพลนนี้
เมื่อคุณเปลี่ยนมาใช้ Blaze plan นอกจากที่คุณจะสามารถ request ตัว APIs ที่อยู่ภายนอก Google ได้แล้ว คุณยังจะได้โค้วต้าในการเรียกใช้งานฟังก์ชันฟรี 2,000,000 ครั้ง/เดือน
2. Install Node.js and npm
ปัจจุบัน Cloud Functions for Firebase รองรับการพัฒนาด้วย Javascript ใน Environment ที่เป็น Node.js เพราะฉะนั้นเราจะต้องมี Node.js และ npm(node package manager) ในเครื่องเราซะก่อน วิธีลงก็มี 2 วิธีคือดาวน์โหลดจากหน้าเว็บ https://nodejs.org/
หรือ จะ install ผ่าน command line ก็ได้ ด้วยคำสั่ง
brew install node
จากนั้นก็เช็คหน่อยว่าเครื่องเรามีทั้ง node และ npm แล้ว(ปกติต้องมาด้วยกัน) ด้วยคำสั่ง
node --version
npm --version
หมายเหตุ เวอร์ชันที่เป็นค่าเริ่มต้นและแนะนำสำหรับ Node.js คือ v14
3. Install Firebase CLI
run คำสั่งด้านล่างนี้ใน command line เพื่อติดตั้ง Firebase CLI ได้เลย
npm install -g firebase-tools
จากนั้นก็เช็คหน่อยว่าเราติดตั้ง Firebase CLI เรียบร้อยแล้วด้วยคำสั่ง
firebase --version
4. Initial Project
เริ่มต้นให้เรา run คำส่ัง login ใน command line โดยจะมี browser เด้งมาให้เรา login ก็ให้เรา login ด้วย account ที่เราใช้สร้างโปรเจคใน Firebase ในขั้นตอนที่ 1
firebase login
จากนั้นให้เราสร้าง directory ที่จะไว้เก็บไฟล์ แล้วก็ command line เข้าไปที่ directory นั้น ตัวอย่าง
cd Desktop/
mkdir functions-firebase
cd functions-firebase/
เมื่อเข้าที่ directory ที่ต้องการแล้ว ก็ให้ใช้คำสั่ง
firebase init functions
จะเจอคำว่า Firebase เป็นไฟเท่ๆแบบนี้
หลังจากนั้นให้เลือก Use an existing project แล้วจะเจอโปรเจค Firebase ที่เราได้สร้างเอาไว้
ถัดไปก็ให้เราเลือกเอาโปรเจคที่ต้องการจะทำงานด้วย(กรณีมีหลายโปรเจค) แล้วก็กด enter เบาๆ แล้วจะมีคำถามขึ้นมาถามว่าจะเขียนฟังก์ชันด้วยภาษาอะไรระหว่าง JavaScript และ TypeScript
แล้วมันจะถามต่อว่าจะลง ESLint ไหม ตรงจุดนี้สำหรับมือใหม่ผมก็แนะนำให้ตอบ N ไปก่อน เพราะถ้าตอบ Y คุณอาจติดปัญหาการเขียนโปรแกรมให้มีมาตรฐานในหลายๆข้อ แต่ถ้าคุณคุ้นเคยกับการเขียนโค้ดที่มีมาตรฐานอยู่แล้วก็ตอบ Y โลด
ยัง ยัง ยังไม่จบอีก ตัว CLI จะถามต่อว่าลง dependencies เริ่มต้นเลยมั้ย แนะนำให้ตอบ Y ไป เพราะจะช้าหรือเร็วก็ต้องลงอยู่ดี
เมื่อ Initial เรียบร้อย ให้เข้าไปใน directory จะพบว่ามีโครงสร้างดังรูป เป็นอันว่าจบกระบวนการ Initial แล้วนะเออ
ในการพัฒนาโดยทั่วไป เราจะไปเกี่ยวข้องกับไฟล์ในโฟลเดอร์ functions นี่หละ จำแนกโดย
- package.json เราจะเอาไว้ระบุเวอร์ชันของ Node.js, dependencies ต่างๆที่จะใช้งานร่วมกับฟังก์ชันที่เราจะพัฒนาขึ้นมา
- index.js จะเป็นเหมือน controller ที่บรรจุฟังก์ชันที่เราเขียนไว้
- node_modules จะเป็นที่เก็บ dependencies ต่างๆที่เรา import เข้ามา
5. อัพเดท Dependencies ใน package.json ของคุณ
การอัพเดท dependencies ทั้งหลายที่เราระบุใน package.json ให้เราเข้าไปที่โฟลเดอร์ functions ผ่าน command line โดยให้มั่นใจว่า จะต้องพบไฟล์ package.json ในนั้น
เมื่อมั่นใจแล้ว ก็ใช้ให้คำสั่งตรวจสอบ dependency ใน command line โลด
npm-check-updates หรือ ncu
หากมี dependency มีเวอร์ชันใหม่ก็จะแสดงตามตัวอย่างภาพด้านล่างนี้
จากนั้นให้เราอัพเดท dependency โดยใช้คำสั่ง
npm-check-updates -u หรือ ncu -u
จากนั้นลองใช้คำสั่ง ncu เพื่อเช็คอีกครั้งก็จะพบรูปยิ้มแล้ว
แต่ถึงกระนั้นก็หาใช่ว่าจะจบไม่ แม้ตัวเลขเวอร์ชันในไฟล์ package.json จะอัพเดทแล้ว เราก็ต้อง install พวก dependencies เหล่านั้นด้วย โดยให้เรา run คำสั่งเหล่านี้
npm install firebase-functions@latest firebase-admin@latest --save
npm install -g firebase-tools
คราวนี้หละ สมบูรณ์อัพเดทจริงๆสักที
การทำงานของ Cloud Functions for Firebase กับบริการทั้ง 5 ของ Firebase
มาดูตัวอย่างการทำงานกับบริการต่างๆที่พร้อมส่ง Trigger มาให้ Cloud Functions for Firebase ทำงานต่อกันเลย
1. การทำงานร่วมกับ Firebase Authentication
Firebase Authentication สามารถส่ง Trigger ให้ Cloud Functions for Firebase ได้ 2 กรณีคือ เมื่อผู้ใช้ Sign up เข้าสู่ระบบ(Create) และเมื่อผู้ใช้ถูกลบออกจากระบบ(Delete)
exports.sendWelcomeEmail = functions.auth.user().onCreate(event => {
// เขียนฟังก์ชันในการส่งอีเมลไปหาผู้ใช้คนนั้นๆ
});exports.sendByeEmail = functions.auth.user().onDelete(event => {
// เขียนฟังก์ชันในการส่งอีเมลไปหาผู้ใช้คนนั้นๆ
});
จากตัวอย่างโค้ดด้านบน sendWelcomeEmail กับ sendByeEmail จะเป็นชื่อของฟังก์ชันที่จะใช้อ้างอิงใน Firebase Console ส่วน onCreate และ onDelete คือ Listener ที่รอรับ Trigger จาก Firebase Authentication นั่นเอง
โค้ดตัวอย่างการทำงานระหว่าง Firebase Authentication และ Cloud Functions for Firebase
2. การทำงานร่วมกับ Firebase Realtime Database
Firebase Realtime Database สามารถส่ง Trigger ให้ Cloud Functions for Firebase ได้โดยเมื่อมีข้อมูลใหม่ถูกเขียนลง database
exports.makeUppercase = functions.database.ref('/messages/{langId}/{pushId}/message').onWrite(event => {
// จัดการข้อความให้เป็นอักษรตัวใหญ่
});
จากโค้ดด้านบน makeUppercase ก็คือชื่อของฟังก์ชันที่ใช้อ้างอิงใน Firebase Console ส่วน onWrite คือ Listener ที่รองรับ Trigger จาก Firebase Realtime Database
{langId} และ {pushId} จากตัวอย่างด้านบน เป็น wildcard ที่สามารถระบุได้ตาม event ที่เกิดขึ้นใน object นั้นๆ
โดยหลักการเมื่อมีข้อมูลถูกเขียนลง Database ตัว RTDB ก็จะ Trigger ไปให้ Cloud Functions for Firebase ทำงาน ตัว Cloud Functions ก็จะอ่านข้อความที่เข้ามา จากนั้นก็ขึ้นอยู่กับเราว่าจะทำอะไรกับข้อความนั้น เช่น กรองคำหยาบ, ทำอักษรตัวใหญ่, ส่งไป Translate เป็นต้น จากนั้นก็จะ Write ข้อมูลกลับไปที่ RTDB ต่อไป
โค้ดตัวอย่างการทำงานระหว่าง Firebase Realtime Database และ Cloud Functions for Firebase
3. การทำงานร่วมกับ Cloud Storage for Firebase
Cloud Storage for Firebase สามารถส่ง Trigger ให้ Cloud Functions for Firebase ได้โดยเมื่อไฟล์ใหม่เพิ่มเข้ามาใน Storage
exports.generateThumbnail = functions.storage.object().onChange(event => {
// สร้าง Thumbnail
});
จากโค้ดด้านบน generateThumbnail ก็คือชื่อของฟังก์ชันที่ใช้อ้างอิงใน Firebase Console ส่วน onChange คือ Listener ที่รองรับ Trigger จาก Cloud Storage for Firebase
โดยหลักการ เมื่อมีไฟล์เข้ามาใหม่ใน Storage ตัว Storage ก็จะ Trigger ไปหา Cloud Functions for Firebase ทำงาน จากนั้นตัว Cloud Functions ก็สามารถจัดการกับไฟล์ กรณีรูปภาพ เช่น ทำลายน้ำ, สร้าง thumbnail, Blur รูป, หรือตรวจสอบว่ารูปที่อัพโหลดมาเป็นรูปอะไร(ผ่าน Google Cloud Vision API) จากนั้นก็ upload ไฟล์กลับไป พร้อมกับ write ข้อมูลไปที่ Realtime Database ได้เช่นกัน
เรื่องการจัดการกับรูปใน Client แอพ ไม่ใช่เรื่องง่ายที่จะบริหารจัดการ memory เอง ซึ่งนักพัฒนาส่วนใหญ่คงเคยประสบปัญหา Out of memory กันมาแล้ว
*เคล็ดลับ ปกติเมื่อเราสร้างฟังก์ชันในการจัดการรูป เราจะได้ memory มา 256MB ซึ่งหากเราต้องการจัดการไฟล์ที่ใหญ่และมีความซับซ้อน และเจอปัญหา memory ไม่พอ เราสามารถเข้าไปที่ https://console.cloud.google.com/ เลือกเมนู Cloud Functions และเลือกชื่อฟังก์ชันที่เราต้องการจะปรับ memory จากนั้นก็ปรับ memory ได้แล้วจ้า
โค้ดตัวอย่างการทำงานระหว่าง Cloud Storage for Firebase และ Cloud Functions for Firebase
4. การทำงานร่วมกับ Firebase Analytics
Firebase Analytics สามารถส่ง Trigger ให้ Cloud Functions for Firebase ได้โดยเมื่อมี event เกิดขึ้น
exports.sendAppGreeting = functions.analytics.event('first_open').onLog(event => {
// ส่งอีเมล หรือ ส่ง push notifications
});
จากโค้ดด้านบน sendAppGreeting ก็คือชื่อของฟังก์ชันที่ใช้อ้างอิงใน Firebase Console ส่วน first_open คือ event ที่เกิดขึ้นเมื่อผู้ใช้คนนั้นๆเปิดแอพครั้งแรก และ onLog คือ Listener ที่รองรับ Trigger จาก Cloud Storage for Firebase
หมายเหตุ เนื่องด้วยปัจจุบัน Cloud Functions for Firebase ยังอยู่ในสถานะ beta บาง event จาก Firebase Analytics อาจต้องใช้เวลา 2–3 ชั่วโมง เพื่อ log event และ Trigger ตัว Cloud Functions ให้ทำงาน
โค้ดตัวอย่างการทำงานระหว่าง Firebase Analytics และ Cloud Functions for Firebase
5. การทำงานร่วมกับ Firebase Cloud Messaging
มาถึง Firebase Cloud Messaging ตัวนี้จะเป็นการที่ Cloud Functions for Firebase รับ Trigger มาได้จาก 4 บริการข้างต้น จากนั้นเราเขียนฟังก์ชันเพื่อยิง Push Notification ไปหาผู้ใช้โดยอัตโนมัติทั้งแบบรายคน หรือ Topic ก็ได้ (Firebase Cloud Messaging ไม่ได้เป็นคน Trigger) ตามตัวอย่างรูปด้านล่างนี้
exports.sendNotification = functions.database.ref("/topics/{pushId}").onWrite((event) => {
// เตรียม payload และ options
return admin.messaging().sendToTopic("sample", payload, options);
});
จากโค้ดด้านบน sendNotification ก็คือชื่อของฟังก์ชันที่ใช้อ้างอิงใน Firebase Console ส่วน onWrite คือ Listener ที่รองรับ Trigger จาก Firebase Realtime Database จากนั้น ก็สั่งยิง Push Notification ไปหา Topic ที่ชื่อ Sample
การเก็บ Server Key ไว้ใน Client แอพโดยเฉพาะ Android เป็นเรื่องที่เสี่ยง เนื่องจากถ้ามีคน decompile APK เราไป ก็มีโอกาสที่เขาจะได้ server key เราไป และหากเขาได้ server key ไป นั่นหมายความว่า เขาจะสามารถส่งข้อความหาผู้ใช้ของเราได้
โค้ดตัวอย่างการทำงานระหว่าง Firebase Realtime Database, Firebase Cloud Messaging และ Cloud Functions for Firebase
6. แถมด้วย API
อีกหนึ่งความสามารถที่ Cloud Functions for Firebase เตรียมมาให้คือ บริการสร้าง REST API ให้เราเชื่อมต่อกับบริการของ Firebase อย่าง Realtime Database ได้ ซึ่งเราอาจใช้ในการ Migrate ข้อมูลจากฐานข้อมูลเดิม หรือใช้งานร่วมกันเลยก็ได้ในกรณีอยากให้ลูกค้าฝั่ง Mobile App ใช้งาน Firebase Realtime Database
exports.addMessage = functions.https.onRequest((request, response) => {
// เขียนข้อมูลลง RTDB ตาม path ที่ต้องการ
});
จากโค้ดด้านบน addMessage ก็คือชื่อของฟังก์ชันที่ใช้อ้างอิงใน Firebase Console ส่วน onRequest คือ Listener ที่รองรับการ Request ซึ่งเมื่อเรา deploy ฟังก์ชันนี้แล้ว เราจะได้ endpoint คืนกลับมา ตัวอย่าง https://us-central1-your-project.cloudfunctions.net/addMessage
โค้ดตัวอย่างการสร้าง API และเขียนข้อมูลลง Firebase Realtime Database ด้วย Cloud Functions for Firebase
การ Deploy
เมื่อเราเขียนฟังก์ชันตามต้องการเรียบร้อยแล้ว ก็มาถึงขั้นตอนการ deploy ละ เริ่มต้นให้เรา command line ไปที่ directory ที่เราสร้างไว้ จากนั้นพิมพ์คำสั่ง
firebase deploy --only functions
จากนั้นก็นับหนึ่งถึงสามล้าน
ประมาณ 1–2 เลยทีเดียว นานนิสนึง(ทีม Firebase บอกกำลังพัฒนาให้การ deploy เร็วขึ้น)
Cloud Functions for Firebase ใน Firebase Console
เมื่อเราเข้าไปใน Firebase Console เลือกโปรเจคที่ต้องการจากนั้นเลือก Functions ที่เมนูซ้าย เราก็จะพบหน้า DASHBOARD ที่แสดงฟังก์ชันทั้งหมดที่เราได้ deploy ไป, จำนวนครั้งที่ทำงาน และระยะเวลาเฉลี่ยในการทำงานแต่ละครั้ง
นอกจากนั้นเราสามารถดู Log ที่เกิดขึ้นทั้งหมดใน tab ที่ชื่อ LOGS ซึ่งเราสามารถเลือกดูตามฟังก์ชัน หรือประเภทของ log ได้
และ Tab สุดท้ายคือ USAGE คือสถิติการใช้งานของเรา เพื่อไว้ใช้ประเมินค่าใช้จ่ายที่อาจเกิดขึ้นได้
สำหรับรายละเอียดแพลนของ Cloud Functions for Firebase
เพิ่มเติมที่ https://firebase.google.com/pricing/
เมื่อคุณอ่านบทความมาถึงตรงนี้แล้ว เกิดไฟลุกอยากลองเล่นแบบจริงจัง ผู้เขียนแนะนำให้ผู้อ่านไปดูตัวอย่างของ Cloud Functions for Firebase ใน GitHub ซึ่งมีตัวอย่างเกือบ 30 ตัวอย่างหลากหลายการใช้งานให้เป็นไอเดีย
และบทความนี้ไม่ได้สอนเรื่องการเขียน Node.js ก็ต้องรบกวนผู้อ่านไปศึกษาการพัฒนา Node.js กันเองนาจา(ผู้เขียนก็ศึกษาจาก sample ใน GitHub นี่หละ)
หวังว่าบทความนี้จะทำให้ทุกท่านรู้จัก Cloud Functions for Firebase และเกิดไอเดียในการเอาไปใช้งานกับโปรเจคที่ท่านทำอยู่ วันนี้ขอตัวลาไปดูการถ่ายทอดสดงาน Google I/O ’17 ก่อน บทความหน้าคงได้มีอะไรใหม่ๆอัพเดทจากเวทีนี้ ราตรีสวัสดิ์พี่น้องชาว Firebase Developers