你會看到一位旅人手握粗糙的藍圖,滿懷期待地踏入doGet的迷宮,沒有後端常識的他,既偷懶又小心翼翼地取得URL之杖,研讀古代石碑後,詠唱咒語生成藤蔓,最後他將前往……
接續昨天(Day17)煞有其事但漏洞百出的計畫,今天繼續回顧我嘗試doGet
的失敗歷程。
🔰如果你想立刻takeaway什麼,可以往下滑直接複製我的prompt。
我決定在下班時間,在我自己的side project試試看沒用過的GAS環境的Web App服務與doGet
函式,確定可行後再於上班時間優化客服工具箱。
由於我對於doGet
怎麼回傳資料、回傳什麼格式都很陌生,於是決定先把練習題目設定為:
「透過一個query key去抓Google Sheet一個欄位的資料。」
這個小練習在實作上分成兩端:
doGet
設計一個可以抓取Google Sheets資料的API。ℹ️如何部署:
可以參閱ugmさん於2023年鐵人賽的這篇文章,有詳細的圖文介紹。
這裡我遇到的第一個抉擇點是:
我要在編譯前預先抓取資料,還是使用者點擊內容時才動態抓取資料。
這牽涉到我的GAS Web App要設定成所有人都可以存取,還是只有我自己。
以目前的練習的side project來說,雖然有透過類Apache環境設置帳號密碼,但我不想讓使用者能讀取該Google Sheet的所有資料,所以應當設定成只有我自己可以讀取。
可由於我沒有後端開發經驗,休假時間有限,我決定先不探索如何保管金鑰,而是在prebuild時先fetch資料。換言之,我決定設成所有人都可以存取,並選擇編譯前預先抓取資料,就能避免公開URL於前端環境。[^1]
我原本幻想的,以為doGet
可以讓我自己設計一個RESTful API,在查閱doGet
可以return哪些資料時,才發現我無法透過doGet
自訂HTTP狀態碼與標頭。[^2]
於是,在決定了我想要的資料結構後,我給了一個指引文件給GenAI :
# Response Format
All Google Apps Script web app endpoints must return responses in the following standardized JSON format:
{
"status": "ok" | "error", // Request result status
"code": 200 | 400 | 404 | 500, // HTTP-like response code
"message": "Description text", // Human-readable message
"data": { ... } // Only included when successful
}
# HTTP Status Code Mapping
- **200**: Success - Operation completed successfully
- **400**: Bad Request - Invalid or missing required parameters
- **404**: Not Found - Requested resource, sheet, or data not found
- **500**: Internal Server Error - System errors, spreadsheet access failures, unexpected errors
# MIME Type
All responses must use JSON MIME type:
.setMimeType(ContentService.MimeType.JSON);
# Implementation Template
function doGet(event) {
try {
// logic
// Success response
return ContentService
.createTextOutput(JSON.stringify({
status: "ok",
code: 200,
message: "Operation completed successfully",
data: {
// data
}
}))
.setMimeType(ContentService.MimeType.JSON);
} catch (error) {
// Error response
return ContentService
.createTextOutput(JSON.stringify({
status: "error",
code: 500,
message: "Unexpected error: " + error.message
}))
.setMimeType(ContentService.MimeType.JSON);
}
}
然後GenAI就非常有效率地幫我生成了200行的腳本,只為了實現如何用一個query key查詢Google Sheet並抓取一個欄位的資料。[^3]
主要是因為GenAI非常仔細地依照我給的規範,在每個可能出錯的小環節,都設計了error message。
一來是不會寫測試的我自嘆不如,二來是覺得這樣也太麻煩了吧,感覺doGet
不適合太複雜的場景。
我當下跟工程師的朋友閒聊一下我正在玩什麼,他就嗆我說:
「很久以前我就說過了吧?不要浪費時間摸GAS,直接用Google API~」
所以就產生了《Day04|Google Sheets相關服務》這一篇文哈哈哈……
那怎麼辦?有點不甘心浪費了時間做這個嘗試,那既然都做了一個可以fetch的端點了,那這個部分:
- Node.js環境:使用Node.js向GAS環境發送HTTP GET請求。
就改用很久以前就想玩玩看的Deno環境來fetch吧?
摸索doGet
時,我還沒用過firebase,也還沒實做過任何RESTful API,所以當下剛完成時有短暫覺得:
「耶~我終於做出第一個假API!」
不到一分鐘就馬上覺得荒謬,我竟然試圖在一個HTTP Response裡再包一個假的HTTP Response。
反省一下,這裡至少有兩個可以提早止損的決策點:
doGet
回傳格式的限制後,就應該停手,另外尋找其它方案。另一方面也感到後GenAI時代的好處,只要指定好規格,就可以跳過自己手刻程式碼,專注在程式設計本身,省去很多程式碼撰寫的時間。
[^1]: 雖然只要我保管好Web App的URL,就應該只有我能完整讀取該Google Sheet,但當然這終歸是取巧的做法。
[^2]: GAS環境是少數只能控制本文與content-type的環境;幾乎所有其它主流serverless或後端框架都有setStatusCode()
與setHeader()
。
[^3]: 程式碼的一部分可以參見Day05。