ทดลองทำ API ด้วย Express กับ Cloud Function for Firebase

Jun 5, 2020

ความเดิมตอนที่แล้ว … ที่เรียนรู้การใช้งาน Cloud Firestore เพื่อนำไปเขียน API และเอาให้พี่หลังบ้านในทีมช่วยดู และพี่เขาแนะนำมาว่า ใช้ Express ช่วยดีกว่านะ ดังนั้นเราจึงลองใช้เจ้านี่ช่วยดู

ทำ API กับ Cloud Firestore ด้วย Cloud Functions for Firebase
ด้วยความที่เราอยากย้ายบล็อกใหม่ๆมาลง Firebase Hosting ของเรา เลยเอา data จาก blogspot มาลง Cloud Firestore แล้วก็ต้องสร้าง API…

ว่าแต่เจ้า 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 ของ Express
  • method ก็คือ 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 มาเสียบใส่ใน parameter onRequest()

ถ้าซนหน่อย ใส่ 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 ด้วยกัน ถ้าสมมุติเรามีมากกว่านี้หล่ะ แล้วยัดอยู่ไฟล์เดียวแบบนี้ด้วย โค้ดบวมตัวแตกกันพอดี

ตอนแรกเราก็พักเรื่องนี้ไป จนวันหนึ่งมาเจอโพสนี้เข้า

ขอแชร์บทสนทนาระหว่าง แอ๊ด กับ เสือน้อย เกี่ยวกับ 🔥Cloud Functions for Firebase ที่หลายคนน่าจะอยากรู้ด้วย ^^ . เสือน้อย:...

Posted by Firebase Thailand on Monday, 27 April 2020

ใน 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 Database
  • blog.js ดึงข้อมูลบล็อกของเรา จาก Cloud Firestore
ข้อควรระวัง : เราจะ admin.initializeApp() เพียงครั้งเดียวเท่านั้น ห้าม init ซํ้า

ดังนั้นโค้ดเราจะไม่บวมตัวแตกแล้วนะ

ด้วยความที่ตอนนี้เรามี use case แค่นี้ ดังนั้นขออนุญาติจบบล็อกเพียงแค่นี้จ้า แฮร่~~

Reference:

Express Tutorial Part 4: Routes and controllers
We’ve now created all the routes for our site, along with dummy controller functions that we can populate with a full implementation in later articles. Along the way we’ve learned a lot of fundamental information about Express routes, and some approaches for structuring our routes and controllers.

สุดท้ายฝากร้านกันสักนิด ฝากเพจด้วยนะจ๊ะ

อย่าลืมกด like กด share บทความกันด้วยนะคะ :)

Posted by MikkiPastel on Sunday, 10 December 2017

Tags

Minseo Chayabanjonglerd

I am a full-time Android Developer and part-time contributor with developer community and web3 world, who believe people have hard skills and soft skills to up-skill to da moon.