ทดลองทำ API ด้วย Express กับ Cloud Function for Firebase
ความเดิมตอนที่แล้ว … ที่เรียนรู้การใช้งาน Cloud Firestore เพื่อนำไปเขียน API และเอาให้พี่หลังบ้านในทีมช่วยดู และพี่เขาแนะนำมาว่า ใช้ Express ช่วยดีกว่านะ ดังนั้นเราจึงลองใช้เจ้านี่ช่วยดู
ว่าแต่เจ้า Express คืออะไรง่ะ?
มันคือ framework หนึ่งของ node.js ที่เอาไว้ทำ webservice โดยเฉพาะ เช่นไว้ทำ RESTful API นั่นเอง
ปล. เว็บเขามีภาษาไทยจากการที่คนไทยใจดีแปลเว็บอ่ะ แต่อ่านภาษาอังกฤษดีกว่านะ ข้อมูลอัพเดตกว่า
Express - เว็บแอปพลิเคชันเฟรมเวอร์คสำหรับ Node.js
Express เป็น Node.js เว็บแอปพลิเคชันเฟรมเวอร์คที่เรียบง่ายและคล่องตัว สำหรับเว็บและแอปพลิเคชันบนมือถือ
ซึ่งแน่นอนก่อนอื่นก็ต้องติดตั้ง node.js มาก่อนเนอะ เราคิดว่าส่วนใหญ่ก็ติดตั้งกันในเครื่องแล้วอะเนอะ ในส่วนนี้ขอข้ามไปเนอะ google เอา
การ install ตัว Express ไปที่ folder ที่มี node_module
อยู่ แล้วก็พิมพ์คำสั่งนี้ลงไปใน Terminal
npm install express --save
ทดลองใช้ Express กันเถอะ!
จากนั้นไปที่ไฟล์ index.js
ของเรา สร้างตัวแปรโดยประกาศเจ้า express
เข้ามา
const express = require('express')
const app = express()
สมมุติแบบง่ายๆ เราแค่อยากลองยิงอะไรสักอย่างเล่นๆอยู่ ซึ่งเราจะอธิบายการสร้าง Cloud Function แบบง่ายๆ แค่เรียกใช้แล้วพ่นคำว่า “Hello World!” ซึ่งถ้าหลังจาก init อะไรเสร็จแล้ว ไฟล์ index.js
ก็จะมีคำสั่งตัวอย่างที่ถูก comment code ไว้ เอาออกมาหน้าตาจะเป็นแบบนี้
exports.hello = builderFunction.onRequest((request, response) => {
response.send('Hello World!')
});
จากการแกะคร่าวๆเราพบว่า ชื่อ function นี้ชื่อว่า hello
และส่ง response เป็นคำว่า “Hello World!”
หลังจากการ deploy ขึ้นไปแบบนี้
firebase deploy --only functions:hello
เราจะได้ชื่อ function นามว่า hello
ซึ่งมี url คือ
https://{region}-{project_name}.cloudfunctions.net/hello
ผลที่ได้จะเป็นแบบนี้
งั้นลองมาเปลี่ยนใช้ Express กันเถอะ เมื่อเราลองศึกษาจาก Document ในส่วนของ basic routing การเขียนจะเป็นแบบนี้
app.get('/', function (req, res) {
res.send('Hello World!')
});
อธิบายกันหน่อยจ้า ปกติเจ้า routing ธรรมดาๆ จะมีหน้าตาเป็นแบบนี้
app.METHOD(PATH, HANDLER)
app
ก็คือconst app = express()
ด้านบนจ้า เป็น instance ของ Expressmethod
ก็คือ HTTP request method เช่น get put post ที่เราเห็นได้ทั่วไปpath
ก็คือ path อ่ะ ใน server ของเรา ในที่นี้ก็คือ path ของ function ใน Cloud Function ไม่งงเนอะhandler
ในที่นี้คือ function ว่าจะให้ทำอะไร
แน่นอนว่า ถ้าใช้ routing แบบนี้ขึ้นไป deploy มันผ่านแหละ แต่ function hello ที่เพิ่งสร้างจะถูกลบไปหล่ะสิ เพราะเราไม่ได้ export ออกมานี่สิ ดังนั้นจึงต้องใส่ export ไปด้วยนะ
exports.hello = builderFunction.onRequest(app);
จากนั้นนำไปใช้งานจะต้องได้แบบอันแรกนะ
มาสรุปในแบบฉบับครูกวดวิชากันสักนิดนึงนะ
- ไฮไลท์สีแดง จากด้านบนที่เขียนแบบ default พอมาในรูปของ Express นั้นจะต้องอยู่ใน handler ของ Express
- ไฮไลท์สีฟ้าๆหลัง exports คือชื่อ function ใน Cloud Function for Firebase ของเราจ้า และเอาเจ้า
app
มาเสียบใส่ใน parameteronRequest()
ถ้าซนหน่อย ใส่ path เพิ่ม จากเดิมที่ไม่มีอะไรเลย /
เป็น /hi
จากนั้น deploy ไป และลองเรียกใหม่ พบว่าเจอปัญหานี้ Cannot GET /
งงอ่ะดิ สาเหตุเพราะว่า path ที่เราใส่ไปเพิ่มยังไงหล่ะ ดังนั้นถ้าจะเรียกใช้ก็ต่อใส่ /hi
ต่อท้ายด้วย ถึงจะเรียกใช้ได้
Reference:
Express basic routing
Routing refers to determining how an application responds to a client request to a particular endpoint, which is a URI…expressjs.com
Express routing
Routing refers to how an application’s endpoints (URIs) respond to client requests. For an introduction to routing, see…expressjs.com
การใช้งานแบบจริงจัง
เนื่องจากโปรเจกของเรานั้นมีของเดิมที่เชื่อมต่อกับ Realtime Database ที่เอาไว้ทำเล่นๆตอนที่ลอง streaming audio แล้วก็อันที่ใช้กับบล็อกใหม่เราที่มี data ไปเก็บไว้ที่ Cloud Firestore ดังนั้นเราจะเปลี่ยนทุกอันมาเป็น Express ให้หมดเลย เย้เย้~
ใช้ Express กับ Cloud Function ที่ดึงข้อมูลจาก Realtime Database
แปลงเจ้า API ที่เขียนเชื่อมต่อกับ Realtime Database จากของเดิมที่เป็นแบบนี้
เปลี่ยนมาใช้เจ้า Express ซึ่งการทำ API แบบพื้นฐานจะเริ่มที่ method GET ก่อนเป็นอันดับแรกเนอะจากที่เล่าไปเมื่อสักครู่ เมื่อเข้าสูตรแบบกวดวิชา เราจะต้องเอาพวก function ต่างๆ มาไว้ใน handler ในรูปแบบของ Express จะได้แบบนี้
ผลที่ได้จะเป็นดังนี้
ถ้าเราเขียนตามปกตินั้น ไม่ว่าจะ method อะไร มันก็จะคืนค่า json ที่เป็น response ที่ได้หมดเลย แต่การใช้ Express จะแก้ปัญหาตรงนั้น ทำให้เราสามารถใช้ method ได้หลากหลายขึ้นใน path เดียว อีกทั้ง ยังทำงานได้ตรงตามวัตถุประสงค์ด้วยนะ เช่น GET ก็พ่น data ออกมาอย่างเดียว method อื่นก็คือไม่มีเพราะเราไม่ได้ทำไว้
ใช้ Express กับ Cloud Function ที่ดึงข้อมูลจาก Cloud Firestore
จากที่เมื่อกี้ที่เราพูดถึงว่าการ implement ด้วย Cloud Function แบบปกติ ไม่ว่าจะเป็น method อะไร ก็พ่น data ออกมาเหมือน GET
ไปหม๊ดดดด เช่น
ซึ่งจริงๆมันควรพ่นข้อมูลแค่ method GET
อย่างเดียวเน๊าะ
ในแต่ละ API ของบล็อกเรานั้น เราสามารถใส่เจ้า routing ได้แบบนี้
- get all blog :
/blog
- get tag blog :
/blog/tag/:tag
- get blog by id :
/blog/id/:id
โดยเจ้า lazy load ที่ทำค้างไว้ จะใส่ parameter เป็น lastPublished เหมือนเดิม
ทริคในการทำในช่วงนี้ เรามีหลายๆ API ดังนั้นเราควร init เจ้า express()
ใหม่แบบนี้
const appBlog = express();
เมื่อเข้าสู่การทำ routing นั้น path ที่ใส่ในแต่ละอันจะเป็นแบบนี้
- get all blog :
appBlog.get(‘/’
เวลาเรียกนั้นจะเป็น <root_path>/blog จะได้ list ของบล็อกรวม - get tag blog :
appBlog.get(‘/tag/:tag’
เวลาเรียกนั้นจะเป็น <root_path>/blog/tag/{tag} เช่น <root_path>/blog/tag/firebase จะได้ list ของบล็อกราย tag - get blog by id :
appBlog.get(‘/id/:id’
เวลาเรียกนั้นจะเป็น <root_path>/blog/id/{id} เช่น <root_path>/blog/id/5030646897553777270 จะได้บล็อกตาม id นั้นๆ
ของเดิมขอยกโค้ดที่เป็น get blog by id ตอนเขียนจะเป็นแบบนี้
แต่เนื่องจากเราเอาทั้ง 3 อันมาอยู่ใน Path เดียวกัน
ซึ่งการทำจะคล้ายๆด้านบนที่เป็น Realtime Database ที่เอา fucntion เดิมไปใส่แทนที่จ้า
การนำ request ไปใช้งานต่อ ถ้าในกรณีของ tag หรือ id ที่อยู่ใน Path
นั้น เราสามารถเรียกออกมาใช้ได้เป็น request.params.id
หรือ request.params.tag
อย่าลืม params มี s ต่อหลังเน้อ ไม่งั้นผิดตัวใช้ไม่ได้นะ
ส่วนพวก parameter สามารถเรียกได้แบบเดิมคือ request.query.published
ผลการใช้งาน เราสามารถดึงข้อมูลมาเฉพาะ GET เท่านั้นเน้อ
Reference:
4.x API
Creates an Express application. The express() function is a top-level function exported by the express module. var…expressjs.com
แต่ถ้าอย่างนี้โค้ดมันจะรกไหมนะ
กลับไปดูที่ index.js โค้ดทั้งหมดมีทั้งสิ้น 262 บรรทัด ซึ่งเรามี 4 functions บน Cloud Function ด้วยกัน ถ้าสมมุติเรามีมากกว่านี้หล่ะ แล้วยัดอยู่ไฟล์เดียวแบบนี้ด้วย โค้ดบวมตัวแตกกันพอดี
ตอนแรกเราก็พักเรื่องนี้ไป จนวันหนึ่งมาเจอโพสนี้เข้า
ใน document ก็จะเป็นประมาณนี้เนอะ
https://firebase.google.com/docs/functions/organize-functions
ก่อนอื่นสร้างไฟล์แยกออกมาก่อน เช่น musiclist.js
ถ้าท่าปกติจะเป็นแบบนี้
const musiclist = require('./musiclist');
exports.musiclist = musiclist.musiclist;
และถ้ามาเป็นหมู่คณะ เช่น ไฟล์ metrics.js
แบบไฟล์นี้มีหลาย function ก็จะเป็นแบบนี้
exports.metrics = require('./metrics');
ถ้าใช้ express.js หล่ะ จะเป็นแบบนี้
เราจะ move หลายๆ function ไปใน file ใหม่ ในที่นี้มีดังนี้
sample.js
อันนี้แค่ให้มัน print 'Hello World!' เฉยๆmusiclist.js
ดึงข้อมูลไฟล์ music streaming จาก Realtime Databaseblog.js
ดึงข้อมูลบล็อกของเรา จาก Cloud Firestore
ข้อควรระวัง : เราจะ admin.initializeApp()
เพียงครั้งเดียวเท่านั้น ห้าม init ซํ้า
ดังนั้นโค้ดเราจะไม่บวมตัวแตกแล้วนะ
ด้วยความที่ตอนนี้เรามี use case แค่นี้ ดังนั้นขออนุญาติจบบล็อกเพียงแค่นี้จ้า แฮร่~~
Reference:
สุดท้ายฝากร้านกันสักนิด ฝากเพจด้วยนะจ๊ะ