Callable Function in Cloud Functions for Firebase

รู้จัก Callable Function ใน Cloud Functions for Firebase ตั้งแต่ Zero จนเป็น Hero

Jirawatee
Firebase Thailand

--

พูดถึงการรับส่งข้อมูลระหว่าง client และ server ในยุคนี้ เชื่อว่าหลายๆคนก็เลือกใช้บริการที่เป็น BaaS(Backend-as-a-Service) เพราะมันช่วยประหยัดเรื่องของ resource แถมลดเวลาในการพัฒนาได้เยอะ และผมเชื่อว่า Cloud Functions for Firebase ก็เป็นหนึ่งตัวเลือกที่นักพัฒนาไทยนิยมใช้กัน

โดยวิธีที่นักพัฒนาส่วนมากใช้ในการพัฒนาเพื่อรับส่งข้อมูลระหว่าง client และ server ด้วย Cloud Functions ที่ผมได้เจอมา ก็จะเป็นการสร้าง API แบบที่เรียกว่า HTTP Function ซึ่งรองรับ GET, POST, PUT, DELETE และ OPTIONS แต่…คุณรู้หรือไม่ว่า มันมีวิธีที่ง่ายกว่าในการพัฒนา ที่รู้จักกันในชื่อว่า Callable Function

Callable Function

คือฟังก์ชันที่ให้ client อย่าง Web, Android, iOS, Unity และ C++ สามารถเรียกใช้งานได้ตรงๆ โดยการ deploy ฟังก์ชันแบบ Callable Function คุณจะไม่ได้ Endpoint ของ API ออกมา แต่คุณสามารถเชื่อมต่อ client กับ server ได้ง่ายกว่าเดิมผ่าน SDK โดยความพิเศษใน Callable Function มีดังนี้

  • Firebase Auth token, FCM token และ App Check token จะแนบมากับ request ให้อัตโนมัติ(ถ้ามีการ implement บริการเหล่านี้ไว้ที่ฝั่ง client)
  • ถ้ามี Firebase Auth token หรือ App Check token แนบมากับ request มันจะถูก validate ให้อัตโนมัติที่ฝั่ง server ซึ่งแปลว่า ถ้า token ถูก revoke หรือ expire มา ตัว request นั้นจะถูก reject กลับไป

เอาหละครับ บทความนี้ผมจะพาทุกคนไปเรียนรู้เรื่องราวของ Callable Function ตามหัวข้อดังต่อไปนี้

  1. การพัฒนาฟังก์ชันแบบ Callable Function
  2. การเชื่อมต่อ Client เพื่อรับส่งข้อมูล
  3. Tip สำหรับคนอยาก Run ใน Local ด้วย Firebase Emulator Suite

1. การพัฒนาฟังก์ชันแบบ Callable Function

1.1 เตรียมความพร้อม

ขั้นตอนนี้นักพัฒนาจะต้องมีโปรเจค Firebase, ต้องติดตั้ง Firebase CLI และ Cloud Functions ให้เรียบร้อยก่อน โดยใครที่ยังไม่เคยพัฒนาฟังก์ชันใน Cloud Functions มาก่อน ผมแนะนำให้อ่านและทำตามหัวข้อ “การติดตั้ง” ในบทความด้านล่างนี้

แต่สำหรับใครที่มีประสบการณ์การพัฒนาฟังก์ชันใน Cloud Functions มาแล้วให้ข้ามไปข้อถัดไปได้เลย

1.2 เริ่มต้นเขียนฟังก์ชัน

โครงสร้างของ Callable Function จะมีรูปแบบที่ขึ้นต้นด้วย functions.https.onCall และจะมี params คืนมา 2 ตัวคือ data และ context โดยโค้ดที่จะเขียนลงไปใน index.js ก็จะเป็นประมาณนี้

const functions = require("firebase-functions")exports.myCallable = functions.https.onCall((data, context) => {
// ...
})

โดยใน data คุณสามารถดึงค่าต่างๆที่ถูกส่งมาจากฝั่ง client ได้

const msg = data.msg

ส่วนใน context คุณก็สามารถดึงข้อมูลจาก Firebase Auth, FCM และ Firebase App Check ได้(ข้อมูลจะแนบมาอัตโนมัติ กรณีที่มีการใช้งานที่ฝั่ง client)

// Firebase Auth
const uid = context.auth.uid
const name = context.auth.token.name || null
const picture = context.auth.token.picture || null
const email = context.auth.token.email || null
// FCM
const fcmToken = context.instanceIdToken || null
// Firebase App Check
if (context.app) {
const rawToken = context.rawRequest.header['X-Firebase-AppCheck']
}

1.3 การตอบกลับข้อมูล

การ return ข้อมูลกลับไปที่ client เราสามารถส่งข้อมูลกลับไปเป็นรูปแบบของ text หรือจะเป็นแบบ JSON ก็ได้ง่ายๆแบบนี้เลย

return {
result: data.msg + ', Feb 14th, 2022'
}

1.4 การจัดการ Error

เราสามารถ throw error ไปพร้อมกับข้อความ กรณีที่ต้องการ return promise reject ได้ด้วย functions.https.HttpsError(FunctionsErrorCode, ErrorMessage) เพื่อให้ฝั่ง client สามารถเข้าใจ error และนำไปแสดงผลได้ เช่น

if (!context.auth) {
throw new functions.https.HttpsError(
'failed-precondition',
'You must be authenticated'
)
}

1.5 เลือก Location เพื่อเพิ่มความเร็ว

เมื่อคุณ deploy ฟังก์ชัน โค้ดของคุณจะถูกส่งไปทำงานที่เมกา(us-central1) เป็นค่าเริ่มต้น ซึ่งแน่นอนว่าการรับส่งข้อมูลจากไทยไปเมกาจะมี network latency เกิดขึ้น

ดังนั้นเพื่อลด latency ที่อาจเกิดขึ้น นักพัฒนาสามารถเลือก location ที่จะทำการ deploy โดยคำนึงถึงระยะทาง และบริการอื่นๆที่เกี่ยวข้องในฟังก์ชันนั้น เพื่อให้ได้ประสิทธิภาพดีที่สุด ซึ่งวิธีการหลังจากเลือก location ได้แล้วก็เพียงเพิ่ม region ไปในตอนประกาศฟังก์ชันแบบนี้

exports.myCallable = functions.region('asia-southeast1').https.onCall((data, context) => {
// ...
})

1.6 Deploy

เมื่อเขียนโค้ดได้ตามต้องการแล้ว ก็ให้เปิด Terminal แล้วเข้าไปที่โฟลเดอร์ /functions ของโปรเจคจากนั้นก็เรียกคำสั่ง deploy ได้เลย

firebase deploy

2. การเชื่อมต่อ Client เพื่อรับส่งข้อมูล

การเชื่อมต่อเพื่อรับส่งข้อมูลระหว่าง client กับ Callable Function เราจะใช้ Firebase client SDK เข้ามาเป็นตัวกลางในการเชื่อมต่อ โดย minimum SDK ที่รองรับมีดังนี้

  • Firebase SDK for Apple platforms 8.11.0
  • Firebase SDK for Android 20.0.1
  • Firebase JavaScript SDK 8.10.1
  • Firebase Modular Web SDK v. 9.0

สำหรับตัวอย่างในบทความนี้ ผมจะทำการเชื่อมต่อด้วย Firebase Modular Web SDK นะครับ

สำหรับ JavaScript SDK v8, Android, iOS, Unity และ C++ ดูวิธีการเชื่อมต่อที่นี่

2.1 ลงทะเบียน Web app

ให้เข้าไปที่ Firebase console เลือกโปรเจคที่เราสร้างไว้ โดยเมื่อเข้าสู่หน้า Overview ให้คลิก Add appตามรูปด้านล่าง

ต่อไปให้คลิกเลือก Web

ตั้งชื่อ Web app ของเราให้ง่ายต่อการจดจำ แล้วกดปุ่ม Register app

ขั้นตอนถัดไป ให้เลือกวิธีการพัฒนาที่ถนัด โดยจะมีแบบ npm และ <script> (ตัวอย่างจะเลือกแบบ <script>) จากนั้นให้ copy ชุด JavaScript เอาไว้ แล้วกด Continue to console ได้เลย

2.2 เชื่อมต่อ Callable Function กับ SDK

จากโค้ดที่เรา copy มาจากขั้นตอนที่ 2.1 ให้เราเพิ่มโค้ดตามขั้นตอนทั้ง 4 นี้ในไฟล์ HTML ที่ต้องการ

สำคัญมาก: กรณี Callable Function ที่สร้างในข้อ 1 มีการระบุ location คุณจำเป็นต้องกำหนด location ให้ฝั่ง client ด้วยโดยการแก้ไขโค้ดด้านบนในบรรทัดที่ 24 เป็นดังนี้

const functions = getFunctions(app, "asia-southeast1");

สำหรับ JavaScript SDK v8, Android, iOS, Unity, C++ ดูวิธีกำหนด location ที่นี่

2.3 Deploy และ Demo

ถ้าโค้ดพร้อมแล้ว ก็ deploy โลด ปล.ถ้าใครยังไม่มีโฮสฟรีไว้ใช้ ผมนี่แนะนำ Firebase Hosting เลยครับ :)

เสร็จแล้วมาลองเอา URL ที่ได้ไปเปิดใน browser ดูผลลัพธ์กัน

3. Tip สำหรับคนอยาก Run ใน Local ด้วย Firebase Emulator Suite

เชื่อว่านักพัฒนาหลายคนที่ใช้ Firebase ต่างก็ใช้ Firebase Emulator Suite เพื่อทดสอบโปรเจคของตัวเองใน local เพราะมันรวดเร็ว แก้ปุ๊บเห็นผลปั๊บไม่ต้อง deploy ไม่กระทบข้อมูลใน Production

สำหรับ Callable Function ก็สามารถพัฒนาใน local ได้เช่นกัน เพียงแต่การ run ฝั่ง client ที่เชื่อมต่อ เราจะต้องมีการเขียนโค้ดเพิ่มเข้าไปนิสนึง โดยโค้ดจะอ้างอิงจากข้อ 2.2 บรรทัดที่ 13 ให้คุณเพิ่ม connectFunctionsEmulatorเข้าไป

import { getFunctions, httpsCallable, connectFunctionsEmulator } from "https://www.gstatic.com/firebasejs/9.6.6/firebase-functions.js";

และเพิ่มคำสั่งด้านล่างนี้ ต่อจากบรรทัดที่ 24

const functions = getFunctions(app);
connectFunctionsEmulator(functions, "localhost", 5001);

สำหรับ JavaScript SDK v8, Android และ iOS ดูวิธีกำหนด config ที่นี่

สรุป

เป็นอย่างไรบ้างครับสำหรับ Callable Function นอกจากจะพัฒนาง่ายแล้ว ส่วนตัวผมยังชอบที่เราไม่ต้องระบุ Endpoint ของ API ลงไปในโค้ดของฝั่ง client เลย(ดูเท่ และปลอดภัยขึ้นไปอีกนิสนึง)

ก่อนจากกันไป หากคุณชอบบทความนี้ก็ฝากกด Clap เป็นกำลังใจให้ผม หากมันเป็นประโยชน์ก็ฝากกด Share ให้เพื่อนของคุณ และหากคุณไม่อยากพลาดบทความถัดๆไปของซีรี่ย์ Zero to Hero ก็ฝากกด Follow: Firebase Thailand ไว้ด้วยนะครับ สำหรับวันนี้ต้องขอตัวลาไปก่อน แล้วพบกันใหม่บทความหน้าครับ

--

--

Jirawatee
Firebase Thailand

Technology Evangelist at LINE Thailand / Google Developer Expert in Firebase