解決 JSON-SERVER 無法部屬到 Netlify 的問題

2024/01/26閱讀時間約 11 分鐘


前言

在學習 React 與前端開發時,每當完成一個專案,都希望能夠佈署到公開的環境。如果是單純 HTML + CSS + JavaScript 的專案,用 Github Page 的服務就能搞定,比方說這個貪食蛇的複刻專案

而如果是用 React 等框架完成的專案,則可以透過 Netlify 輕鬆佈署,無論是 GUI 或 CLI 的操作方式都非常簡單。

但近期我在嘗試佈署海綿寶寶測驗這個專案時,使用了 json-server 當作假的 API 伺服器,結果遇到了一個問題:

該如何把 Json-Server 佈署到 Netlify 上面?

毫無頭緒之下上網查了些資料,意外發現 Netlify 客戶支援論壇有相關的討論:JSON Server Doesn’t Work on Netlify

簡而言之,官方回覆,開發者無法在 Netlify 上面運行 live server,所以建議改用 Netlify 的 serverless function 服務:

No, you can’t get a live server to work in any case. With Netlify Functions you can get responses as JSON. Each function has its own URL. You can call the function, do some processing on the server and return a JSON response. Try here: https://functions-playground.netlify.app/

這篇文章會來簡單紀錄我是如何透過 Netlify function 服務回傳 JSON 資料,希望能幫助到其他也面臨到類似問題的人。


什麼是 Netlify function?

以前在 Google 經銷商工作時,曾耳聞過 Google Cloud Function 是 function-as-a-service (FaaS) 的服務,但沒有相關的實作經驗。

簡單來說,像這樣的無伺服器 (serverless) 服務,免去了我們準備伺服器的麻煩,也能夠去運行主機端的程式碼。開發者只需要專心建構程式碼即可。


Netlify function 也是類似的服務,它主要提供了以下兩種函式類型:

  • Synchronous function:實現傳統的 client/server 請求回覆模式,連線會維持到函式運作完畢為止。client 端在頁面渲染或移動到下一個任務之前,先等待 response 傳回來。
  • Background function:運行時間比較長的函式,透過非同步呼叫把函示當作背景任務來執行,適合像批量處理 (batch processing)、爬蟲 (scraping) 或是執行速度較慢的 API。

由於本次專案的資料,僅是 15 道海綿寶寶測驗題的 JSON 檔,用第一個選項就綽綽有餘了。

相關領域的資源可以參考:


如何建立簡單的 JSON API

環境建置

首要之務是先去 Netlify 開通帳號,可以直接用 Github 註冊登入。

我的做法是在 Github 建立兩個 repo:

接著在 JSON API 的專案中,安裝 Netlify 的 CLI 工具。非必要,但相信大家都有共識,CLI 用起來效率比較高:

npm install netlify-cli -g


安裝完畢後,輸入 netlify login 進行身分驗證,瀏覽器會自動跳出驗證的視窗。


資料夾結構

your-project
└── netlify
└── functions
└── hello-world.js

2 個資料夾, 1 個檔案


測試函式

我們先在 JSON API 的專案建立上述的資料夾以及檔案,一開始以體驗為主,檔案名稱就取 hello-world.js 吧。現在開啟 hello-world.js,輸入一些測試內容:

export default async (req, context) => {
return new Response("Hello, world!");
};


👉 函式檔案一定要使用 JavaScript modules 的語法,並且做 default export。如果對於 export 很陌生的話,可以參考我之前的文章和裡面的參考資料: 【React 學習】匯出與匯入元件

👉 handler 函式要是一個 async 函式

👉 handler 函式要包含以下兩個參數,相信有和 fetch 打過交道的話不會太陌生:

  • Web API 的 Request 物件,代表傳過來的 HTTP 請求
  • Netlify 提供的 Context API,裡面包含帳戶、佈署相關的資訊

👉 handler 函式回傳一個 HTTP response 物件


測試很單純,因此沒有用到 reqcontext 兩個參數,若需要更客製化地處理,請參考官方文件。恭喜🎊 這就是我們第一個 serverless 函式!

現在回到終端機,輸入以下指令執行 Netlify function 的服務:

netlify functions:serve


當你看到類似以下的回覆,就代表函式成功執行囉,接著到 http://localhost:9999/.netlify/functions/hello-world 應該就能看到 Hello, world! 被列印在畫面上。

raw-image


置入真實的 JSON 資料

我們把 questions.json 檔案加入到資料夾結構當中:

raw-image


這邊 JavaScript 檔案依循 Netlify 官方的建議,修改成了 .mjs,以便使用現代的 ES module 語法。

Naming your function with the .mjs extension lets you use the modern ES modules syntax. To learn more about the different module formats, refer to runtime.

函式程式碼的部分,自然得用 import 的方式來匯入 questions.json

import questions from "../../questions.json";

export default async ({ req, context }) => {
const json = JSON.stringify(questions); // 把 questions 物件轉為 JSON 格式字串
const options = {
status: 200, // 沒什麼,我只是單純喜歡 200
headers: {
"Content-Type": "application/json; charset=utf-8", // 確保不會發生 encoding 問題
"Access-Control-Allow-Origin": "*", // API 開放給所有人,因為大家都愛海綿寶寶
},
};

return new Response(json, options); // 兩個參數,第一個為 body,第二個為 header options
};


更新完成後,回到 http://localhost:9999/.netlify/functions/api 檢查一下,阿呀,成功拿到 JSON 資料了:

raw-image


將 JSON API 專案佈署到 Netlify

測試過沒問題之後,接下來就是把 api 專案 push 到 Github 上,然後佈署至 Netlify 了。這部分可以參考官方教學。做過一次就會驚覺,現在實在有太多方便的工具了......

佈署完成後,Netlify 會產生一組隨機名稱的 URL,像這樣:https://helpful-kleicha-41f04d.netlify.app/.netlify/functions/api

當然如果你有自己的網域,可以在專案建立 netlify.toml 檔案,建立 [[redirect]] 的規則,但筆者是個窮酸小子,沒有購買域名,所以就乖乖維持這個奇奇怪怪的 URL 了。


在 React 專案中呼叫 API

這部分不多加贅述 React 專案的程式碼了,簡單說一下,我在 App 根元件使用 useEffect 來處理 fetch 的副作用。因為只希望 effect 函式在 App 元件掛載 (mount) 時執行,所以依賴陣列 (dependency array) 置入空陣列。

至於 dispatch 是因為我在此專案特意練習 useReducer 進行 state 管理。

  // Handle side effect: fetch questions data
useEffect(() => {
const fetchQuestions = async () => {
try {
const res = await fetch(
"https://helpful-kleicha-41f04d.netlify.app/.netlify/functions/api" 👈
);
const data = await res.json();

dispatch({ type: "dataReceived", payload: data });
} catch (err) {
dispatch({ type: "dataFailed" });
console.log(err);
}
};

fetchQuestions();
}, []);



參考資料


16會員
34內容數
Bonjour à tous,我本身是法文系畢業,這邊會刊登純文組學習網頁開發的筆記。如果能鼓勵更多文組夥伴一起學習,那就太開心了~
留言0
查看全部
發表第一個留言支持創作者!