การสร้างแอปพลิเคชัน Front-End แบบไร้เซิร์ฟเวอร์โดยใช้ Google Cloud Platform
เผยแพร่แล้ว: 2022-03-10เมื่อเร็ว ๆ นี้ กระบวนทัศน์การพัฒนาของแอปพลิเคชันได้เริ่มเปลี่ยนจากการที่ต้องปรับใช้ ปรับขนาด และอัปเดตทรัพยากรที่ใช้ภายในแอปพลิเคชันด้วยตนเอง เป็นการพึ่งพาผู้ให้บริการระบบคลาวด์บุคคลที่สามเพื่อจัดการทรัพยากรเหล่านี้ส่วนใหญ่
ในฐานะนักพัฒนาหรือองค์กรที่ต้องการสร้างแอปพลิเคชันที่เหมาะกับตลาดภายในเวลาที่รวดเร็วที่สุด เป้าหมายหลักของคุณอาจอยู่ที่การให้บริการแอปพลิเคชันหลักแก่ผู้ใช้ของคุณ ในขณะที่คุณใช้เวลาน้อยลงในการกำหนดค่า การปรับใช้ และการทดสอบความเครียด ใบสมัครของคุณ. หากเป็นกรณีการใช้งานของคุณ การจัดการตรรกะทางธุรกิจของแอปพลิเคชันของคุณแบบไร้เซิร์ฟเวอร์อาจเป็นทางเลือกที่ดีที่สุดของคุณ แต่อย่างไร
บทความนี้มีประโยชน์สำหรับวิศวกรส่วนหน้าที่ต้องการสร้างฟังก์ชันบางอย่างภายในแอปพลิเคชันของตนหรือวิศวกรส่วนหลังที่ต้องการแยกและจัดการฟังก์ชันบางอย่างจากบริการแบ็คเอนด์ที่มีอยู่โดยใช้แอปพลิเคชันแบบไร้เซิร์ฟเวอร์ที่ปรับใช้กับ Google Cloud Platform
หมายเหตุ : หากต้องการได้รับประโยชน์จากสิ่งที่จะกล่าวถึงในที่นี้ คุณต้องมีประสบการณ์ในการทำงานกับ React ไม่จำเป็นต้องมีประสบการณ์ในแอปพลิเคชันแบบไร้เซิร์ฟเวอร์มาก่อน
ก่อนที่เราจะเริ่มต้น มาทำความเข้าใจว่าแท้จริงแล้วแอปพลิเคชัน แบบไร้เซิร์ฟเวอร์ คืออะไร และสถาปัตยกรรมแบบไร้เซิร์ฟเวอร์นั้นสามารถใช้ในการสร้างแอปพลิเคชันภายในบริบทของวิศวกรส่วนหน้าได้อย่างไร
แอพพลิเคชั่นไร้เซิร์ฟเวอร์
แอปพลิเคชันแบบไร้เซิร์ฟเวอร์คือแอปพลิเคชันที่แบ่งออกเป็นฟังก์ชันเล็กๆ ที่ขับเคลื่อนด้วยเหตุการณ์ที่นำกลับมาใช้ใหม่ได้ ซึ่งโฮสต์และจัดการโดยผู้ให้บริการระบบคลาวด์บุคคลที่สามภายในคลาวด์สาธารณะในนามของผู้สร้างแอปพลิเคชัน สิ่งเหล่านี้ถูกกระตุ้นโดยเหตุการณ์บางอย่างและดำเนินการตามต้องการ แม้ว่าคำต่อท้าย “ less ” ที่แนบมากับคำที่ ไม่มีเซิร์ฟเวอร์ แสดงว่าไม่มีเซิร์ฟเวอร์ แต่ก็ไม่เป็นเช่นนั้น 100% แอปพลิเคชันเหล่านี้ยังคงทำงานบนเซิร์ฟเวอร์และทรัพยากรฮาร์ดแวร์อื่นๆ แต่ในกรณีนี้ ทรัพยากรเหล่านั้นไม่ได้ถูกจัดเตรียมโดยนักพัฒนา แต่จะถูกจัดเตรียมโดยผู้ให้บริการคลาวด์บุคคลที่สาม ดังนั้นจึงเป็นเซิร์ฟเวอร์ น้อยกว่า สำหรับผู้สร้างแอปพลิเคชัน แต่ยังคงทำงานบนเซิร์ฟเวอร์และเข้าถึงได้ทางอินเทอร์เน็ตสาธารณะ
ตัวอย่างกรณีการใช้งานของแอปพลิเคชันแบบไร้เซิร์ฟเวอร์คือการส่งอีเมลไปยังผู้มีโอกาสเป็นผู้ใช้ที่เข้าชมหน้า Landing Page ของคุณและสมัครรับอีเมลเปิดตัวผลิตภัณฑ์ ในขั้นตอนนี้ คุณอาจไม่มีบริการแบ็คเอนด์ที่ทำงานอยู่ และไม่ต้องการเสียเวลาและทรัพยากรที่จำเป็นในการสร้าง ปรับใช้ และจัดการบริการเหล่านี้ เนื่องจากคุณต้องส่งอีเมล ที่นี่ คุณสามารถเขียนไฟล์เดียวที่ใช้ไคลเอ็นต์อีเมลและปรับใช้กับผู้ให้บริการระบบคลาวด์ใดๆ ที่สนับสนุนแอปพลิเคชันแบบไร้เซิร์ฟเวอร์ และปล่อยให้พวกเขาจัดการแอปพลิเคชันนี้ในนามของคุณในขณะที่คุณเชื่อมต่อแอปพลิเคชันแบบไร้เซิร์ฟเวอร์นี้กับหน้า Landing Page ของคุณ
แม้ว่าจะมีสาเหตุมากมายที่คุณอาจพิจารณาใช้ประโยชน์จากแอปพลิเคชันแบบไร้เซิร์ฟเวอร์หรือ Functions As A Service (FAAS) สำหรับแอปพลิเคชันส่วนหน้าของคุณ ต่อไปนี้คือเหตุผลสำคัญบางประการที่คุณควรพิจารณา:
- แอปพลิเคชันการปรับขนาดอัตโนมัติ
แอปพลิเคชันแบบไร้เซิร์ฟเวอร์ได้รับการปรับขนาดในแนวนอน และผู้ให้บริการระบบคลาวด์จะทำการ " ขยายออก " โดยอัตโนมัติตามจำนวนการเรียกใช้ ดังนั้นนักพัฒนาจึงไม่ต้องเพิ่มหรือลบทรัพยากรด้วยตนเองเมื่อแอปพลิเคชันมีภาระงานมาก - ลดค่าใช้จ่าย
แอปพลิเคชันแบบไร้เซิร์ฟเวอร์ที่ขับเคลื่อนด้วยเหตุการณ์จะทำงานเมื่อจำเป็นเท่านั้น และสิ่งนี้จะสะท้อนถึงค่าบริการตามที่จะถูกเรียกเก็บเงินตามจำนวนเวลาที่เรียกใช้ - ความยืดหยุ่น
แอปพลิเคชันแบบไร้เซิร์ฟเวอร์ถูกสร้างขึ้นเพื่อให้ใช้ซ้ำได้อย่างมาก ซึ่งหมายความว่าแอปพลิเคชันจะไม่ถูกผูกไว้กับโปรเจ็กต์หรือแอปพลิเคชันเดียว สามารถแยกฟังก์ชันการทำงานเฉพาะลงในแอปพลิเคชันแบบไร้เซิร์ฟเวอร์ ปรับใช้และใช้งานในหลายโครงการหรือแอปพลิเคชัน แอปพลิเคชันแบบไร้เซิร์ฟเวอร์สามารถเขียนในภาษาที่ต้องการของผู้เขียนแอปพลิเคชันได้ แม้ว่าผู้ให้บริการระบบคลาวด์บางรายจะสนับสนุนภาษาจำนวนน้อยกว่าเท่านั้น
เมื่อใช้งานแอพพลิเคชั่นแบบไร้เซิร์ฟเวอร์ นักพัฒนาทุกรายจะมีผู้ให้บริการคลาวด์มากมายภายในคลาวด์สาธารณะให้ใช้งาน ภายในบริบทของบทความนี้ เราจะเน้นที่แอปพลิเคชันแบบไร้เซิร์ฟเวอร์บน Google Cloud Platform — วิธีสร้าง จัดการ ปรับใช้ และวิธีผสานรวมกับผลิตภัณฑ์อื่นๆ บน Google Cloud ในการทำเช่นนี้ เราจะเพิ่มฟังก์ชันการทำงานใหม่ให้กับแอปพลิเคชัน React ที่มีอยู่ในขณะที่ดำเนินการผ่านกระบวนการของ:
- การจัดเก็บและดึงข้อมูลของผู้ใช้บนคลาวด์
- การสร้างและจัดการงาน cron บน Google Cloud;
- การปรับใช้ Cloud Functions กับ Google Cloud
หมายเหตุ : แอปพลิเคชันแบบไร้เซิร์ฟเวอร์ไม่ได้ผูกติดกับ React เท่านั้น ตราบใดที่เฟรมเวิร์กส่วนหน้าหรือไลบรารี่ที่คุณต้องการสามารถสร้างคำขอ HTTP
ได้ ก็สามารถใช้แอปพลิเคชันแบบไร้เซิร์ฟเวอร์ได้
ฟังก์ชั่น Google Cloud
Google Cloud ช่วยให้นักพัฒนาสามารถสร้างแอปพลิเคชันแบบไร้เซิร์ฟเวอร์โดยใช้ Cloud Functions และเรียกใช้โดยใช้ Functions Framework ตามที่มีการเรียก ฟังก์ชันระบบคลาวด์เป็นฟังก์ชันที่ขับเคลื่อนด้วยเหตุการณ์ที่นำกลับมาใช้ใหม่ได้ ซึ่งปรับใช้กับ Google Cloud เพื่อฟังทริกเกอร์เฉพาะจากทริกเกอร์เหตุการณ์ที่มีอยู่ 6 ตัว จากนั้นจึงดำเนินการตามที่เขียนไว้เพื่อดำเนินการ
ฟังก์ชันระบบคลาวด์ที่มีอายุสั้น ( โดยมีการหมดเวลาดำเนินการเริ่มต้น 60 วินาทีและสูงสุด 9 นาที ) สามารถเขียนได้โดยใช้ JavaScript, Python, Golang และ Java และดำเนินการโดยใช้รันไทม์ ใน JavaScript สามารถดำเนินการได้โดยใช้ Node runtime บางเวอร์ชันที่มีให้เท่านั้น และเขียนในรูปแบบของโมดูล CommonJS โดยใช้ JavaScript ธรรมดาขณะที่ส่งออกเป็นฟังก์ชันหลักเพื่อเรียกใช้บน Google Cloud
ตัวอย่างของฟังก์ชันระบบคลาวด์คือตัวอย่างด้านล่างซึ่งเป็นต้นแบบที่ว่างเปล่าสำหรับฟังก์ชันเพื่อจัดการข้อมูลของผู้ใช้
// index.js exports.firestoreFunction = function (req, res) { return res.status(200).send({ data: `Hello ${req.query.name}` }); }
ด้านบนเรามีโมดูลที่ส่งออกฟังก์ชัน เมื่อดำเนินการ จะได้รับอาร์กิวเมนต์คำขอและการตอบสนองที่คล้ายกับเส้นทาง HTTP
หมายเหตุ : ฟังก์ชันระบบคลาวด์จะจับคู่ทุกโปรโตคอล HTTP
เมื่อมีการร้องขอ สิ่งนี้ควรค่าแก่การสังเกตเมื่อคาดหวังข้อมูลในอาร์กิวเมนต์คำขอ เนื่องจากข้อมูลที่แนบเมื่อทำการร้องขอเพื่อเรียกใช้ฟังก์ชันระบบคลาวด์จะแสดงอยู่ในเนื้อหาคำขอสำหรับคำขอ POST
ในขณะที่อยู่ในเนื้อหาการสืบค้นสำหรับคำขอ GET
ฟังก์ชันระบบคลาวด์สามารถเรียกใช้งานในเครื่องได้ในระหว่างการพัฒนาโดยติดตั้งแพ็กเกจ @google-cloud/functions-framework
ภายในโฟลเดอร์เดียวกับที่มีการวางฟังก์ชันที่เขียนไว้หรือทำการติดตั้งทั่วโลกเพื่อใช้สำหรับหลายฟังก์ชันโดยเรียกใช้ npm i -g @google-cloud/functions-framework
จากบรรทัดคำสั่งของคุณ เมื่อติดตั้งแล้ว ควรเพิ่มสคริปต์ package.json
ด้วยชื่อโมดูลที่ส่งออกคล้ายกับชื่อด้านล่าง:
"scripts": { "start": "functions-framework --target=firestoreFunction --port=8000", }
ด้านบน เรามีคำสั่งเดียวภายในสคริปต์ของเราในไฟล์ package.json
ซึ่งรัน functions-framework และยังระบุ firestoreFunction
เป็นฟังก์ชันเป้าหมายที่จะเรียกใช้ในเครื่องบนพอร์ต 8000
เราสามารถทดสอบจุดสิ้นสุดของฟังก์ชันนี้ได้โดยส่งคำขอ GET
ไปยังพอร์ต 8000
บน localhost โดยใช้ curl การวางคำสั่งด้านล่างในเทอร์มินัลจะเป็นการทำเช่นนั้นและส่งคืนการตอบกลับ
curl https://localhost:8000?name="Smashing Magazine Author"
คำสั่งด้านบนส่งคำขอด้วย GET HTTP
และตอบกลับด้วยรหัสสถานะ 200
และข้อมูลออบเจ็กต์ที่มีชื่อที่เพิ่มในการสืบค้น
การปรับใช้ฟังก์ชันคลาวด์
จากวิธีการปรับใช้ที่มีอยู่ วิธีหนึ่งที่รวดเร็วในการปรับใช้ฟังก์ชันระบบคลาวด์จากเครื่องในเครื่องคือใช้ cloud Sdk หลังจากติดตั้ง การเรียกใช้คำสั่งด้านล่างจากเทอร์มินัลหลังจากตรวจสอบสิทธิ์ gcloud sdk กับโปรเจ็กต์ของคุณบน Google Cloud จะปรับใช้ฟังก์ชันที่สร้างขึ้นในเครื่องกับบริการ Cloud Function
gcloud functions deploy "demo-function" --runtime nodejs10 --trigger-http --entry-point=demo --timeout=60 --set-env-vars=[name="Developer"] --allow-unauthenticated
การใช้แฟล็กที่อธิบายด้านล่าง คำสั่งด้านบนจะปรับใช้ฟังก์ชันที่ทริกเกอร์ HTTP กับ Google Cloud ด้วยชื่อ “ demo-function ”
- ชื่อ
นี่คือชื่อที่กำหนดให้กับฟังก์ชันระบบคลาวด์เมื่อปรับใช้และจำเป็น -
region
นี่คือภูมิภาคที่จะปรับใช้ฟังก์ชันระบบคลาวด์ โดยค่าเริ่มต้น จะถูกปรับใช้กับus-central1
-
trigger-http
ซึ่งจะเลือก HTTP เป็นประเภททริกเกอร์ของฟังก์ชัน -
allow-unauthenticated
ซึ่งช่วยให้เรียกใช้ฟังก์ชันภายนอก Google Cloud ผ่านอินเทอร์เน็ตโดยใช้ปลายทางที่สร้างขึ้นโดยไม่ต้องตรวจสอบว่าผู้โทรได้รับการตรวจสอบสิทธิ์หรือไม่ -
source
พาธในเครื่องจากเทอร์มินัลไปยังไฟล์ซึ่งมีฟังก์ชันที่จะปรับใช้ -
entry-point
นี่เป็นโมดูลที่ส่งออกเฉพาะที่จะปรับใช้จากไฟล์ที่มีการเขียนฟังก์ชัน -
runtime
นี่คือรันไทม์ของภาษาที่จะใช้สำหรับฟังก์ชันในรายการรันไทม์ที่ยอมรับนี้ -
timeout
นี่เป็นเวลาสูงสุดที่ฟังก์ชันสามารถทำงานได้ก่อนหมดเวลา โดยค่าเริ่มต้นคือ 60 วินาทีและสามารถตั้งค่าได้สูงสุด 9 นาที
หมายเหตุ : การสร้างฟังก์ชันอนุญาตคำขอที่ไม่ผ่านการตรวจสอบสิทธิ์ หมายความว่าใครก็ตามที่มีจุดสิ้นสุดของฟังก์ชันของคุณสามารถส่งคำขอโดยที่คุณไม่ต้องอนุญาต เพื่อลดปัญหานี้ เราสามารถตรวจสอบให้แน่ใจว่าปลายทางยังคงเป็นส่วนตัวโดยใช้ผ่านตัวแปรสภาพแวดล้อม หรือโดยการขอส่วนหัวการอนุญาตในแต่ละคำขอ
ตอนนี้ฟังก์ชันสาธิตของเราได้รับการปรับใช้และเรามีจุดสิ้นสุดแล้ว เราสามารถทดสอบฟังก์ชันนี้ราวกับว่ากำลังถูกใช้ในแอปพลิเคชันในโลกแห่งความเป็นจริงโดยใช้การติดตั้งออโต้แคนนอนทั่วโลก การเรียกใช้ autocannon -d=5 -c=300 CLOUD_FUNCTION_URL
จากเทอร์มินัลที่เปิดอยู่จะสร้างคำขอ 300 รายการพร้อมกันไปยังฟังก์ชันคลาวด์ภายในระยะเวลา 5 วินาที สิ่งนี้มากเกินพอที่จะเริ่มต้นฟังก์ชั่นคลาวด์และสร้างตัวชี้วัดบางอย่างที่เราสามารถสำรวจได้บนแดชบอร์ดของฟังก์ชั่น
หมายเหตุ : ปลายทางของฟังก์ชันจะถูกพิมพ์ออกมาในเทอร์มินัลหลังจากการปรับใช้ หากไม่เป็นเช่นนั้น ให้เรียกใช้ gcloud function describe FUNCTION_NAME
จากเทอร์มินัลเพื่อรับรายละเอียดเกี่ยวกับฟังก์ชันที่ปรับใช้รวมถึงปลายทาง
เมื่อใช้แท็บเมตริกบนแดชบอร์ด เราจะเห็นการแสดงภาพจากคำขอล่าสุดซึ่งประกอบด้วยจำนวนการเรียกใช้ ระยะเวลาที่ใช้ หน่วยความจำของฟังก์ชัน และจำนวนอินสแตนซ์ที่หมุนเพื่อจัดการคำขอ

การดูแผนภูมิอินสแตนซ์ที่ใช้งานอยู่อย่างละเอียดในภาพด้านบนจะแสดงความสามารถในการปรับขนาดแนวนอนของ Cloud Functions ดังที่เราจะเห็นได้ว่าอินสแตนซ์ 209 รายการถูกปั่นขึ้นภายในไม่กี่วินาทีเพื่อจัดการกับคำขอที่ทำโดยใช้ปืนใหญ่อัตโนมัติ
บันทึกฟังก์ชันระบบคลาวด์
ทุกฟังก์ชันที่ปรับใช้กับ Google Cloud จะมีบันทึกและทุกครั้งที่เรียกใช้ฟังก์ชันนี้ จะมีการสร้างรายการใหม่ในบันทึกนั้น จากแท็บ บันทึก บนแดชบอร์ดของฟังก์ชัน เราจะเห็นรายการบันทึกทั้งหมดจากฟังก์ชันระบบคลาวด์
ด้านล่างนี้คือรายการบันทึกจาก demo-function
ปรับใช้ของเรา ซึ่งสร้างขึ้นจากคำขอที่เราทำโดยใช้ autocannon

รายการบันทึกด้านบนแต่ละรายการจะแสดงอย่างชัดเจนเมื่อเรียกใช้ฟังก์ชัน ระยะเวลาในการดำเนินการ และรหัสสถานะที่สิ้นสุดด้วย หากมีข้อผิดพลาดที่เกิดจากฟังก์ชัน รายละเอียดของข้อผิดพลาดรวมถึงบรรทัดที่เกิดขึ้นจะแสดงในบันทึกที่นี่
คุณสามารถใช้ Logs Explorer บน Google Cloud เพื่อดูรายละเอียดที่ครอบคลุมมากขึ้นเกี่ยวกับบันทึกจากฟังก์ชันระบบคลาวด์
ฟังก์ชันระบบคลาวด์ด้วยแอปพลิเคชันส่วนหน้า
ฟังก์ชันระบบคลาวด์มีประโยชน์มากและทรงพลังสำหรับวิศวกรส่วนหน้า วิศวกรส่วนหน้าที่ไม่มีความรู้ในการจัดการแอปพลิเคชันส่วนหลังสามารถดึงฟังก์ชันการทำงานลงในฟังก์ชันระบบคลาวด์ ปรับใช้กับ Google Cloud และใช้ในแอปพลิเคชันส่วนหน้าโดยส่งคำขอ HTTP
ไปยังฟังก์ชันระบบคลาวด์ผ่านปลายทาง
เพื่อแสดงให้เห็นว่าฟังก์ชั่นคลาวด์สามารถนำมาใช้ในแอพพลิเคชั่นฟรอนท์เอนด์ได้อย่างไร เราจะเพิ่มคุณสมบัติเพิ่มเติมให้กับแอพพลิเคชั่น React นี้ แอปพลิเคชันมีการกำหนดเส้นทางพื้นฐานระหว่างการตรวจสอบสิทธิ์และการตั้งค่าโฮมเพจอยู่แล้ว เราจะขยายให้ใช้ React Context API เพื่อจัดการสถานะแอปพลิเคชันของเรา เนื่องจากการใช้ฟังก์ชันคลาวด์ที่สร้างขึ้นจะเสร็จสิ้นภายในตัวลดแอปพลิเคชัน
ในการเริ่มต้น เราสร้างบริบทของแอปพลิเคชันโดยใช้ createContext
API และสร้างตัวลดขนาดสำหรับจัดการการดำเนินการภายในแอปพลิเคชันของเรา
// state/index.js import { createContext } from “react”;
ส่งออก const UserReducer = (การกระทำ, สถานะ) => { สวิตช์ (action.type) { กรณี "สร้างผู้ใช้": แตก; กรณี "UPLOAD-USER-IMAGE": แตก; case “FETCH-DATA” : แตก case “LOGOUT” : แตก; ค่าเริ่มต้น: console.log(
${action.type} is not recognized
) } };ส่งออก const userState = { ผู้ใช้: null, isLoggedIn: false };
ส่งออก const UserContext = createContext (userState);
ข้างต้น เราเริ่มต้นด้วยการสร้างฟังก์ชัน UserReducer
ซึ่งมีคำสั่ง switch ซึ่งช่วยให้ดำเนินการตามประเภทของการดำเนินการที่ส่งไป คำสั่ง switch มีสี่กรณี และนี่คือการดำเนินการที่เราจะจัดการ สำหรับตอนนี้พวกเขายังไม่ได้ทำอะไรเลย แต่เมื่อเราเริ่มบูรณาการกับฟังก์ชันระบบคลาวด์ของเรา เราจะนำการดำเนินการต่างๆ
นอกจากนี้เรายังสร้างและส่งออกบริบทของแอปพลิเคชันของเราโดยใช้ React createContext API และให้ค่าเริ่มต้นของวัตถุ userState
ซึ่งมีค่าผู้ใช้ในปัจจุบันซึ่งจะได้รับการอัปเดตจาก null เป็นข้อมูลของผู้ใช้หลังจากการตรวจสอบสิทธิ์และยังเป็นค่าบูลีน isLoggedIn
ที่จะทราบว่า ผู้ใช้จะเข้าสู่ระบบหรือไม่
ตอนนี้ เราสามารถดำเนินการตามบริบทของเราได้ แต่ก่อนที่เราจะทำเช่นนั้น เราจำเป็นต้องรวมโครงสร้างแอปพลิเคชันทั้งหมดของเราด้วยผู้ให้บริการที่แนบมากับ UserContext
เพื่อให้ส่วนประกอบย่อยสามารถสมัครรับการเปลี่ยนแปลงค่าของบริบทของเราได้
// index.js import React from "react"; import ReactDOM from "react-dom"; import "./index.css"; import App from "./app"; import { UserContext, userState } from "./state/"; ReactDOM.render( <React.StrictMode> <UserContext.Provider value={userState}> <App /> </UserContext.Provider> </React.StrictMode>, document.getElementById("root") ); serviceWorker.unregister();
เราห่อแอปพลิเคชัน enter ของเราด้วยผู้ให้บริการ UserContext
ที่องค์ประกอบรูท และส่งผ่านค่าเริ่มต้น userState
สร้างไว้ก่อนหน้านี้ใน prop ค่า
ตอนนี้เราได้ตั้งค่าสถานะแอปพลิเคชันอย่างสมบูรณ์แล้ว เราสามารถย้ายไปยังการสร้างโมเดลข้อมูลของผู้ใช้โดยใช้ Google Cloud Firestore ผ่านฟังก์ชันระบบคลาวด์
การจัดการข้อมูลแอปพลิเคชัน
ข้อมูลของผู้ใช้ภายในแอปพลิเคชันนี้ประกอบด้วยรหัสเฉพาะ อีเมล รหัสผ่าน และ URL ของรูปภาพ การใช้ฟังก์ชันระบบคลาวด์ ข้อมูลนี้จะถูกจัดเก็บไว้ในระบบคลาวด์โดยใช้บริการ Cloud Firestore ซึ่งมีให้ใน Google Cloud Platform
Google Cloud Firestore ซึ่งเป็นฐานข้อมูล NoSQL ที่ยืดหยุ่นได้ถูกดึงออกมาจากฐานข้อมูลเรียลไทม์ของ Firebase พร้อมคุณสมบัติที่ได้รับการปรับปรุงใหม่ซึ่งช่วยให้สามารถสืบค้นข้อมูลที่สมบูรณ์และรวดเร็วยิ่งขึ้นควบคู่ไปกับการสนับสนุนข้อมูลออฟไลน์ ข้อมูลภายในบริการ Firestore ถูกจัดระเบียบเป็นคอลเลกชันและเอกสารที่คล้ายกับฐานข้อมูล NoSQL อื่นๆ เช่น MongoDB
คุณสามารถเข้าถึง Firestore ผ่าน Google Cloud Console ได้ หากต้องการเปิดใช้งาน ให้เปิดบานหน้าต่างนำทางด้านซ้ายและเลื่อนลงไปที่ส่วนฐานข้อมูลแล้วคลิก Firestore ที่จะแสดงรายการคอลเลกชันสำหรับผู้ใช้ที่มีข้อมูลที่มีอยู่หรือแจ้งให้ผู้ใช้สร้างคอลเลกชันใหม่เมื่อไม่มีคอลเลกชันที่มีอยู่ เราจะสร้างคอลเล็กชัน ผู้ใช้ เพื่อใช้ในแอปพลิเคชันของเรา
เช่นเดียวกับบริการอื่น ๆ บนแพลตฟอร์ม Google Cloud Cloud Firestore ยังมีไลบรารีไคลเอนต์ JavaScript ที่สร้างขึ้นเพื่อใช้ในสภาพแวดล้อมของโหนด ( ข้อผิดพลาดจะเกิดขึ้นหากใช้ในเบราว์เซอร์ ) ในการด้นสด เราใช้ Cloud Firestore ในฟังก์ชันคลาวด์โดยใช้แพ็คเกจ @google-cloud/firestore
การใช้ Cloud Firestore ด้วยฟังก์ชัน Cloud
ในการเริ่มต้น เราจะเปลี่ยนชื่อฟังก์ชันแรกที่เราสร้างจาก demo-function
เป็น firestoreFunction
แล้วขยายเพื่อเชื่อมต่อกับ Firestore และบันทึกข้อมูลลงในคอลเล็กชันของผู้ใช้
require("dotenv").config(); const { Firestore } = require("@google-cloud/firestore"); const { SecretManagerServiceClient } = require("@google-cloud/secret-manager"); const client = new SecretManagerServiceClient(); exports.firestoreFunction = function (req, res) { return { const { email, password, type } = req.body; const firestore = new Firestore(); const document = firestore.collection("users"); console.log(document) // prints details of the collection to the function logs if (!type) { res.status(422).send("An action type was not specified"); } switch (type) { case "CREATE-USER": break case "LOGIN-USER": break; default: res.status(422).send(`${type} is not a valid function action`) } };
เพื่อจัดการกับการดำเนินงานที่เกี่ยวข้องกับ fire-store มากขึ้น เราได้เพิ่มคำสั่ง switch ที่มีสองกรณีเพื่อจัดการกับความต้องการการรับรองความถูกต้องของแอปพลิเคชันของเรา คำสั่ง switch ของเราจะประเมินนิพจน์ type
ที่เราเพิ่มลงในเนื้อหาคำขอเมื่อส่งคำขอไปยังฟังก์ชันนี้จากแอปพลิเคชันของเรา และเมื่อใดก็ตามที่ข้อมูล type
นี้ไม่มีอยู่ในเนื้อหาคำขอของเรา คำขอจะถูกระบุว่าเป็นคำขอไม่ถูกต้องและรหัสสถานะ 400
ข้างข้อความเพื่อระบุ type
ที่ขาดหายไปจะถูกส่งเป็นการตอบกลับ
เราสร้างการเชื่อมต่อกับ Firestore โดยใช้ไลบรารี Application Default Credentials (ADC) ภายในไลบรารีไคลเอ็นต์ Cloud Firestore ในบรรทัดถัดไป เราเรียกวิธีการรวบรวมในตัวแปรอื่นและส่งผ่านในชื่อของคอลเลกชันของเรา เราจะใช้ข้อมูลนี้เพื่อดำเนินการอื่นๆ เพิ่มเติมเกี่ยวกับการรวบรวมเอกสารที่มีอยู่
หมายเหตุ : ไลบรารีไคลเอนต์สำหรับบริการบน Google Cloud เชื่อมต่อกับบริการที่เกี่ยวข้องโดยใช้รหัสบัญชีบริการที่สร้างขึ้นซึ่งส่งผ่านเมื่อเริ่มต้นตัวสร้าง เมื่อไม่มีรหัสบัญชีบริการ ค่าเริ่มต้นจะใช้ข้อมูลรับรองเริ่มต้นของแอปพลิเคชัน ซึ่งจะเชื่อมต่อโดยใช้บทบาท IAM
ที่กำหนดให้กับฟังก์ชันระบบคลาวด์
หลังจากแก้ไขซอร์สโค้ดของฟังก์ชันที่ปรับใช้ในเครื่องโดยใช้ Gcloud SDK แล้ว เราสามารถเรียกใช้คำสั่งก่อนหน้าจากเทอร์มินัลอีกครั้งเพื่ออัปเดตและปรับใช้ฟังก์ชันระบบคลาวด์อีกครั้ง
เมื่อสร้างการเชื่อมต่อแล้ว เราสามารถใช้เคส CREATE-USER
เพื่อสร้างผู้ใช้ใหม่โดยใช้ข้อมูลจากเนื้อหาคำขอ
require("dotenv").config(); const { Firestore } = require("@google-cloud/firestore"); const path = require("path"); const { v4 : uuid } = require("uuid") const cors = require("cors")({ origin: true }); const client = new SecretManagerServiceClient(); exports.firestoreFunction = function (req, res) { return cors(req, res, () => { const { email, password, type } = req.body; const firestore = new Firestore(); const document = firestore.collection("users"); if (!type) { res.status(422).send("An action type was not specified"); } switch (type) { case "CREATE-USER": if (!email || !password) { res.status(422).send("email and password fields missing"); } const id = uuid() return bcrypt.genSalt(10, (err, salt) => { bcrypt.hash(password, salt, (err, hash) => { document.doc(id) .set({ id : id email: email, password: hash, img_uri : null }) .then((response) => res.status(200).send(response)) .catch((e) => res.status(501).send({ error : e }) ); }); }); case "LOGIN": break; default: res.status(400).send(`${type} is not a valid function action`) } }); };
เราสร้าง UUID โดยใช้แพ็คเกจ uuid เพื่อใช้เป็น ID ของเอกสารที่จะถูกบันทึกโดยส่งต่อไปยังวิธีการ set
ในเอกสารและ ID ของผู้ใช้ด้วย โดยค่าเริ่มต้น ID สุ่มจะถูกสร้างขึ้นในทุกเอกสารที่แทรก แต่ในกรณีนี้ เราจะอัปเดตเอกสารเมื่อจัดการการอัปโหลดรูปภาพ และ UUID คือสิ่งที่จะใช้เพื่อรับเอกสารเฉพาะที่จะอัปเดต แทนที่จะเก็บรหัสผ่านของผู้ใช้เป็นข้อความธรรมดา เราใช้ bcryptjs ก่อน จากนั้นจึงเก็บแฮชผลลัพธ์เป็นรหัสผ่านของผู้ใช้

การรวมฟังก์ชั่นคลาวด์ firestoreFunction
เข้ากับแอพ เราใช้จาก CREATE_USER
ภายในตัวลดผู้ใช้
หลังจากคลิกปุ่ม สร้างบัญชี การดำเนินการจะถูกส่งไปยังตัวลดขนาดด้วยประเภท CREATE_USER
เพื่อสร้างคำขอ POST
ที่มีอีเมลและรหัสผ่านที่พิมพ์ไปยังจุดสิ้นสุดของฟังก์ชัน firestoreFunction
import { createContext } from "react"; import { navigate } from "@reach/router"; import Axios from "axios"; export const userState = { user : null, isLoggedIn: false, }; export const UserReducer = (state, action) => { switch (action.type) { case "CREATE_USER": const FIRESTORE_FUNCTION = process.env.REACT_APP_FIRESTORE_FUNCTION; const { userEmail, userPassword } = action; const data = { type: "CREATE-USER", email: userEmail, password: userPassword, }; Axios.post(`${FIRESTORE_FUNCTION}`, data) .then((res) => { navigate("/home"); return { ...state, isLoggedIn: true }; }) .catch((e) => console.log(`couldnt create user. error : ${e}`)); break; case "LOGIN-USER": break; case "UPLOAD-USER-IMAGE": break; case "FETCH-DATA" : break case "LOGOUT": navigate("/login"); return { ...state, isLoggedIn: false }; default: break; } }; export const UserContext = createContext(userState);
ด้านบน เราใช้ Axios เพื่อส่งคำขอไปยัง firestoreFunction
และหลังจากที่คำขอนี้ได้รับการแก้ไข เราตั้งค่าสถานะเริ่มต้นของผู้ใช้จาก null
เป็นข้อมูลที่ส่งคืนจากคำขอ และสุดท้าย เรากำหนดเส้นทางผู้ใช้ไปยังโฮมเพจในฐานะผู้ใช้ที่ตรวจสอบสิทธิ์ .
ณ จุดนี้ ผู้ใช้ใหม่สามารถสร้างบัญชีได้สำเร็จและเข้าสู่หน้าแรก กระบวนการนี้สาธิตวิธีที่เราใช้ Firestore เพื่อสร้างข้อมูลพื้นฐานจากฟังก์ชันระบบคลาวด์
การจัดการการจัดเก็บไฟล์
การจัดเก็บและเรียกข้อมูลไฟล์ของผู้ใช้ในแอปพลิเคชันมักเป็นคุณลักษณะที่จำเป็นอย่างยิ่งในแอปพลิเคชัน ในแอปพลิเคชันที่เชื่อมต่อกับ node.js แบ็กเอนด์ Multer มักถูกใช้เป็นมิดเดิลแวร์เพื่อจัดการกับ multipart/form-data ซึ่งไฟล์ที่อัปโหลดเข้ามา แต่ในกรณีที่ไม่มี node.js แบ็กเอนด์ เราสามารถใช้ไฟล์ออนไลน์ได้ บริการพื้นที่เก็บข้อมูลเช่น Google Cloud Storage เพื่อจัดเก็บทรัพย์สินแอปพลิเคชันแบบคงที่
Google Cloud Storage เป็นบริการจัดเก็บไฟล์ที่พร้อมใช้งานทั่วโลก ใช้เพื่อจัดเก็บข้อมูลจำนวนเท่าใดก็ได้เป็นออบเจ็กต์สำหรับแอปพลิเคชันลงในบัคเก็ต มีความยืดหยุ่นเพียงพอที่จะจัดการการจัดเก็บทรัพย์สินแบบคงที่สำหรับแอปพลิเคชันทั้งขนาดเล็กและขนาดใหญ่
ในการใช้บริการ Cloud Storage ภายในแอปพลิเคชัน เราสามารถใช้ประโยชน์จากตำแหน่งข้อมูล Storage API ที่มีอยู่ หรือโดยใช้ไลบรารีไคลเอ็นต์ของโหนด Storage อย่างเป็นทางการ อย่างไรก็ตาม ไลบรารีไคลเอนต์ Node Storage ไม่ทำงานภายในหน้าต่างเบราว์เซอร์ ดังนั้นเราจึงสามารถใช้ Cloud Function ซึ่งเราจะใช้ไลบรารีนั้น
ตัวอย่างนี้คือ Cloud Function ด้านล่างซึ่งเชื่อมต่อและอัปโหลดไฟล์ไปยัง Cloud Bucket ที่สร้างขึ้น
const cors = require("cors")({ origin: true }); const { Storage } = require("@google-cloud/storage"); const StorageClient = new Storage(); exports.Uploader = (req, res) => { const { file } = req.body; StorageClient.bucket("TEST_BUCKET") .file(file.name) .then((response) => { console.log(response); res.status(200).send(response) }) .catch((e) => res.status(422).send({error : e})); }); };
จากฟังก์ชันระบบคลาวด์ด้านบน เรากำลังดำเนินการหลักสองอย่างต่อไปนี้:
ขั้นแรก เราสร้างการเชื่อมต่อกับ Cloud Storage ภายในตัว
Storage constructor
และใช้คุณลักษณะ Application Default Credentials (ADC) บน Google Cloud เพื่อตรวจสอบสิทธิ์กับ Cloud Storageประการที่สอง เราอัปโหลดไฟล์ที่รวมอยู่ในเนื้อหาคำขอไปยัง
TEST_BUCKET
ของเราโดยเรียกใช้เมธอด.file
file และส่งผ่านชื่อไฟล์ เนื่องจากเป็นการดำเนินการแบบอะซิงโครนัส เราจึงใช้คำสัญญาว่าจะทราบว่าการดำเนินการนี้ได้รับการแก้ไขเมื่อใด และเราส่งการตอบกลับ200
รายการกลับมา ซึ่งจะเป็นการสิ้นสุดวงจรชีวิตของการร้องขอ
ตอนนี้ เราสามารถขยาย Uploader
Cloud Function ด้านบนเพื่อจัดการการอัปโหลดรูปโปรไฟล์ของผู้ใช้ ฟังก์ชันระบบคลาวด์จะได้รับรูปโปรไฟล์ของผู้ใช้ เก็บไว้ในที่เก็บข้อมูลระบบคลาวด์ของแอปพลิเคชัน จากนั้นอัปเดตข้อมูล img_uri
ของผู้ใช้ภายในคอลเล็กชันของผู้ใช้ในบริการ Firestore
require("dotenv").config(); const { Firestore } = require("@google-cloud/firestore"); const cors = require("cors")({ origin: true }); const { Storage } = require("@google-cloud/storage"); const StorageClient = new Storage(); const BucketName = process.env.STORAGE_BUCKET exports.Uploader = (req, res) => { return Cors(req, res, () => { const { file , userId } = req.body; const firestore = new Firestore(); const document = firestore.collection("users"); StorageClient.bucket(BucketName) .file(file.name) .on("finish", () => { StorageClient.bucket(BucketName) .file(file.name) .makePublic() .then(() => { const img_uri = `https://storage.googleapis.com/${Bucket}/${file.path}`; document .doc(userId) .update({ img_uri, }) .then((updateResult) => res.status(200).send(updateResult)) .catch((e) => res.status(500).send(e)); }) .catch((e) => console.log(e)); }); }); };
ตอนนี้เราได้ขยายฟังก์ชันการอัปโหลดด้านบนเพื่อดำเนินการเพิ่มเติมดังต่อไปนี้:
- ขั้นแรก มันสร้างการเชื่อมต่อใหม่กับบริการ Firestore เพื่อรับคอลเลกชั่น
users
ของเราโดยเริ่มต้นตัวสร้าง Firestore และใช้ Application Default Credentials (ADC) เพื่อตรวจสอบสิทธิ์กับ Cloud Storage - หลังจากอัปโหลดไฟล์ที่เพิ่มในเนื้อหาคำขอแล้ว เรากำหนดให้เป็นสาธารณะเพื่อให้สามารถเข้าถึงได้ผ่าน URL สาธารณะโดยเรียกเมธอด
makePublic
ในไฟล์ที่อัปโหลด ตามการควบคุมการเข้าถึงเริ่มต้นของ Cloud Storage หากไม่มีไฟล์สาธารณะ ไฟล์จะไม่สามารถเข้าถึงได้ทางอินเทอร์เน็ตและสามารถทำได้เมื่อแอปพลิเคชันโหลด
หมายเหตุ : การทำให้ไฟล์เป็นสาธารณะหมายความว่าใครก็ตามที่ใช้แอปพลิเคชันของคุณสามารถคัดลอกลิงก์ของไฟล์และเข้าถึงไฟล์ได้ไม่จำกัด วิธีหนึ่งในการป้องกันสิ่งนี้คือการใช้ URL ที่ลงชื่อเพื่อให้สิทธิ์การเข้าถึงไฟล์ภายในบัคเก็ตของคุณชั่วคราว แทนที่จะทำให้เป็นสาธารณะโดยสมบูรณ์
- ต่อไป เราอัปเดตข้อมูลที่มีอยู่ของผู้ใช้เพื่อรวม URL ของไฟล์ที่อัปโหลด เราค้นหาข้อมูลของผู้ใช้รายใดรายหนึ่งโดยใช้แบบสอบถาม
WHERE
ของ Firestore และเราใช้userId
ที่รวมอยู่ในเนื้อหาคำขอ จากนั้นเราตั้งค่าฟิลด์img_uri
ให้มี URL ของรูปภาพที่อัปเดตใหม่
ฟังก์ชัน Upload
บนคลาวด์ด้านบนสามารถใช้ได้ในแอปพลิเคชันใดๆ ก็ตามที่มีผู้ใช้ที่ลงทะเบียนในบริการ Firestore ทั้งหมดที่จำเป็นในการส่งคำขอ POST
ไปยังปลายทาง โดยใส่ IS ของผู้ใช้และรูปภาพลงในเนื้อหาคำขอ
ตัวอย่างของสิ่งนี้ภายในแอปพลิเคชันคือกรณี UPLOAD-FILE
ซึ่งส่งคำขอ POST
ไปยังฟังก์ชันและทำให้ลิงก์รูปภาพที่ส่งคืนจากคำขออยู่ในสถานะแอปพลิเคชัน
# index.js import Axios from 'axios' const UPLOAD_FUNCTION = process.env.REACT_APP_UPLOAD_FUNCTION export const UserReducer = (state, action) => { switch (action.type) { case "CREATE-USER" : # .....CREATE-USER-LOGIC .... case "UPLOAD-FILE": const { file, id } = action return Axios.post(UPLOAD_FUNCTION, { file, id }, { headers: { "Content-Type": "image/png", }, }) .then((response) => {}) .catch((e) => console.log(e)); default : return console.log(`${action.type} case not recognized`) } }
จากกรณีสวิตช์ด้านบน เราส่งคำขอ POST
โดยใช้ Axios ไปยัง UPLOAD_FUNCTION
ที่ส่งผ่านในไฟล์ที่เพิ่มเข้ามาเพื่อรวมไว้ในเนื้อหาคำขอ และเรายังเพิ่มรูปภาพ Content-Type
ในส่วนหัวของคำขอ
หลังจากการอัปโหลดสำเร็จ การตอบสนองที่ส่งคืนจากฟังก์ชันระบบคลาวด์จะมีเอกสารข้อมูลของผู้ใช้ซึ่งได้รับการอัปเดตให้มี URL ที่ถูกต้องของรูปภาพที่อัปโหลดไปยังที่เก็บข้อมูลบนคลาวด์ของ Google จากนั้น เราสามารถอัปเดตสถานะของผู้ใช้เพื่อให้มีข้อมูลใหม่ และจะอัปเดตองค์ประกอบ src
รูปโปรไฟล์ของผู้ใช้ในองค์ประกอบโปรไฟล์ด้วย

การจัดการงาน Cron
งานอัตโนมัติที่ทำซ้ำๆ เช่น การส่งอีเมลไปยังผู้ใช้หรือการดำเนินการภายในในช่วงเวลาที่กำหนด มักเป็นคุณลักษณะของแอปพลิเคชันที่รวมอยู่ด้วย ในแอปพลิเคชัน node.js ปกติ งานดังกล่าวสามารถจัดการเป็นงาน cron โดยใช้ node-cron หรือ node-schedule เมื่อสร้างแอปพลิเคชันแบบไร้เซิร์ฟเวอร์โดยใช้ Google Cloud Platform Cloud Scheduler ได้รับการออกแบบมาเพื่อดำเนินการ cron ด้วย
หมายเหตุ : แม้ว่า Cloud Scheduler จะทำงานคล้ายกับยูทิลิตี้ Unix cron ในการสร้างงานที่จะดำเนินการในอนาคต สิ่งสำคัญคือต้องสังเกตว่า Cloud Scheduler ไม่ได้รันคำสั่งเหมือนที่ยูทิลิตี้ cron ทำ ค่อนข้างจะดำเนินการโดยใช้เป้าหมายที่ระบุ
ตามความหมายของชื่อ Cloud Scheduler อนุญาตให้ผู้ใช้กำหนดเวลาการดำเนินการที่จะดำเนินการในอนาคตได้ การดำเนินการแต่ละครั้งเรียกว่า งาน และสามารถสร้าง อัปเดต และแม้กระทั่งทำลายงานด้วยภาพได้จากส่วนเครื่องมือจัดกำหนดการของ Cloud Console นอกเหนือจากฟิลด์ชื่อและคำอธิบายแล้ว งานบน Cloud Scheduler ยังประกอบด้วยสิ่งต่อไปนี้:
- ความถี่
ใช้เพื่อกำหนดเวลาการดำเนินการของงาน Cron กำหนดการถูกระบุโดยใช้รูปแบบ unix-cron ซึ่งเดิมใช้เมื่อสร้างงานพื้นหลังบนตาราง cron ในสภาพแวดล้อม Linux รูปแบบ unix-cron ประกอบด้วยสตริงที่มีค่าห้าค่าแต่ละค่าแทนจุดเวลา ด้านล่าง เราจะเห็นแต่ละสตริงทั้งห้าและค่าที่แสดง
- - - - - - - - - - - - - - - - minute ( - 59 ) | - - - - - - - - - - - - - hour ( 0 - 23 ) | | - - - - - - - - - - - - day of month ( 1 - 31 ) | | | - - - - - - - - - month ( 1 - 12 ) | | | | - - - - - -- day of week ( 0 - 6 ) | | | | | | | | | | | | | | | | | | | | | | | | | * * * * *
เครื่องมือสร้าง Crontab มีประโยชน์เมื่อพยายามสร้างค่าเวลาความถี่สำหรับงาน หากคุณพบว่ามันยากที่จะรวมค่าเวลาเข้าด้วยกัน ตัวสร้าง Crontab จะมีเมนูแบบเลื่อนลงซึ่งคุณสามารถเลือกค่าที่ประกอบเป็นตารางเวลาได้ และคุณสามารถคัดลอกค่าที่สร้างและใช้เป็นความถี่ได้
- เขตเวลา
เขตเวลาที่งาน cron ถูกดำเนินการ เนื่องจากความแตกต่างของเวลาระหว่างเขตเวลา งาน cron ที่ดำเนินการกับเขตเวลาที่ระบุต่างกันจะมีเวลาดำเนินการต่างกัน - เป้า
นี่คือสิ่งที่ใช้ในการดำเนินงานของงานที่ระบุ เป้าหมายอาจเป็นประเภทHTTP
ที่งานส่งคำขอในเวลาที่กำหนดไปยัง URL หรือหัวข้อ Pub/Sub ซึ่งงานสามารถเผยแพร่ข้อความหรือดึงข้อความจากแอปพลิเคชัน App Engine ได้
Cloud Scheduler ผสมผสานอย่างลงตัวกับฟังก์ชั่นคลาวด์ที่ทริกเกอร์ HTTP เมื่อมีการสร้างงานภายใน Cloud Scheduler โดยมีเป้าหมายที่ตั้งค่าเป็น HTTP งานนี้สามารถใช้เพื่อเรียกใช้ฟังก์ชันระบบคลาวด์ได้ สิ่งที่ต้องทำคือระบุจุดสิ้นสุดของฟังก์ชันระบบคลาวด์ ระบุกริยา HTTP ของคำขอ จากนั้นเพิ่มข้อมูลใดๆ ที่จำเป็นในการส่งผ่านไปยังฟังก์ชันในฟิลด์เนื้อหาที่แสดง ดังแสดงในตัวอย่างด้านล่าง:

งาน cron ในภาพด้านบนจะทำงานภายในเวลา 9.00 น. ทุกวันโดยส่งคำขอ POST
ไปยังจุดปลายตัวอย่างของฟังก์ชันระบบคลาวด์
กรณีการใช้งาน cron ที่สมจริงยิ่งขึ้นคือการส่งอีเมลตามกำหนดเวลาไปยังผู้ใช้ในช่วงเวลาที่กำหนดโดยใช้บริการส่งจดหมายภายนอก เช่น Mailgun ในการดูการทำงานจริง เราจะสร้างฟังก์ชันระบบคลาวด์ใหม่ที่ส่งอีเมล HTML ไปยังที่อยู่อีเมลที่ระบุโดยใช้แพ็คเกจ JavaScript nodemailer เพื่อเชื่อมต่อกับ Mailgun:
# index.js require("dotenv").config(); const nodemailer = require("nodemailer"); exports.Emailer = (req, res) => { let sender = process.env.SENDER; const { reciever, type } = req.body var transport = nodemailer.createTransport({ host: process.env.HOST, port: process.env.PORT, secure: false, auth: { user: process.env.SMTP_USERNAME, pass: process.env.SMTP_PASSWORD, }, }); if (!reciever) { res.status(400).send({ error: `Empty email address` }); } transport.verify(function (error, success) { if (error) { res .status(401) .send({ error: `failed to connect with stmp. check credentials` }); } }); switch (type) { case "statistics": return transport.sendMail( { from: sender, to: reciever, subject: "Your usage satistics of demo app", html: { path: "./welcome.html" }, }, (error, info) => { if (error) { res.status(401).send({ error : error }); } transport.close(); res.status(200).send({data : info}); } ); default: res.status(500).send({ error: "An available email template type has not been matched.", }); } };
Using the cloud function above we can send an email to any user's email address specified as the receiver value in the request body. It performs the sending of emails through the following steps:
- It creates an SMTP transport for sending messages by passing the
host
,user
andpass
which stands for password, all displayed on the user's Mailgun dashboard when a new account is created. - Next, it verifies if the SMTP transport has the credentials needed in order to establish a connection. If there's an error in establishing the connection, it ends the function's invocation and sends back a
401 unauthenticated
status code. - Next, it calls the
sendMail
method to send the email containing the HTML file as the email's body to the receiver's email address specified in theto
field.
Note : We use a switch statement in the cloud function above to make it more reusable for sending several emails for different recipients. This way we can send different emails based on the type
field included in the request body when calling this cloud function.
Now that there is a function that can send an email to a user; we are left with creating the cron job to invoke this cloud function. This time, the cron jobs are created dynamically each time a new user is created using the official Google cloud client library for the Cloud Scheduler from the initial firestoreFunction
.
We expand the CREATE-USER
case to create the job which sends the email to the created user at a one-day interval.
require("dotenv").config();cloc const { Firestore } = require("@google-cloud/firestore"); const scheduler = require("@google-cloud/scheduler") const cors = require("cors")({ origin: true }); const EMAILER = proccess.env.EMAILER_ENDPOINT const parent = ScheduleClient.locationPath( process.env.PROJECT_ID, process.env.LOCATION_ID ); exports.firestoreFunction = function (req, res) { return cors(req, res, () => { const { email, password, type } = req.body; const firestore = new Firestore(); const document = firestore.collection("users"); const client = new Scheduler.CloudSchedulerClient() if (!type) { res.status(422).send({ error : "An action type was not specified"}); } switch (type) { case "CREATE-USER":
const job = { httpTarget: { uri: process.env.EMAIL_FUNCTION_ENDPOINT, httpMethod: "POST", body: { email: email, }, }, schedule: "*/30 */6 */5 10 4", timezone: "Africa/Lagos", }
if (!email || !password) { res.status(422).send("email and password fields missing"); } return bcrypt.genSalt(10, (err, salt) => { bcrypt.hash(password, salt, (err, hash) => { document .add({ email: email, password: hash, }) .then((response) => {
client.createJob({ parent : parent, job : job }).then(() => res.status(200).send(response)) .catch(e => console.log(`unable to create job : ${e}`) )
}) .catch((e) => res.status(501).send(`error inserting data : ${e}`) ); }); }); default: res.status(422).send(`${type} is not a valid function action`) } }); };
From the snippet above, we can see the following:
- A connection to the Cloud Scheduler from the Scheduler constructor using the Application Default Credentials (ADC) is made.
- We create an object consisting of the following details which make up the cron job to be created:
-
uri
The endpoint of our email cloud function in which a request would be made to. -
body
This is the data containing the email address of the user to be included when the request is made. -
schedule
The unix cron format representing the time when this cron job is to be performed.
-
- After the promise from inserting the user's data document is resolved, we create the cron job by calling the
createJob
method and passing in the job object and the parent. - The function's execution is ended with a
200
status code after the promise from thecreateJob
operation has been resolved.
After the job is created, we'll see it listed on the scheduler page.

From the image above we can see the time scheduled for this job to be executed. We can decide to manually run this job or wait for it to be executed at the scheduled time.
บทสรุป
Within this article, we have had a good look into serverless applications and the benefits of using them. We also had an extensive look at how developers can manage their serverless applications on the Google Cloud using Cloud Functions so you now know how the Google Cloud is supporting the use of serverless applications.
Within the next years to come, we will certainly see a large number of developers adapt to the use of serverless applications when building applications. If you are using cloud functions in a production environment, it is recommended that you read this article from a Google Cloud advocate on “6 Strategies For Scaling Your Serverless Applications”.
The source code of the created cloud functions are available within this Github repository and also the used front-end application within this Github repository. The front-end application has been deployed using Netlify and can be tested live here.
อ้างอิง
- Google Cloud
- ฟังก์ชั่นคลาวด์
- Cloud Source Repositories
- Cloud Scheduler overview
- Cloud Firestore
- “6 Strategies For Scaling Your Serverless Applications,” Preston Holmes