iT邦幫忙

2025 iThome 鐵人賽

DAY 20
0
佛心分享-IT 人自學之術

我只是不想加班:一名客服人員的GAS自救之路系列 第 20

Day20|實作回顧:doGet(番外篇:與Deno共舞)

  • 分享至 

  • xImage
  •  

GAS環境有點小眾,Deno環境則更加冷門。
那如果在Node.js叫Deno去拿GAS的資料,會發生什麼事?
走過路過不要錯過,沒什麼用但很可愛的Deno,現在開始表演~

前言

前天(Day18)聊到,我當時試用GAS環境的Web App服務,用doGet設計了一個可以回傳資料的HTTP-based API。換言之,我把GAS環境部署為serverless function hosting,接著就要用Deno環境來玩玩看、向GAS環境發送HTTP GET請求。

正文

實作目標

使用Deno執行TypeScript腳本,向指定的GAS Web App發送HTTP GET請求,將Google Sheet的特定欄位之資料抓下來,並儲存為JSON格式。

專案既有配置

  • Node.js[^1]
  • Vite
  • Vue
  • TypeScript
  • Dart Sass

實作步驟

ℹ️筆者使用的IDE兼容VS Code。

①安裝Deno

// MacOS/Linux
curl -fsSL https://deno.land/install.sh | sh

// Windows
irm https://deno.land/install.ps1 | iex

②安裝IDE插件

安裝Open VSX extension:Deno

③撰寫IDE設定檔

.vscode/settings.json加上:

{
  "deno.enable": true,
  "deno.lint": true,
  
  // 請自行修改為你存放Deno腳本的目錄路徑
  "deno.enablePaths": [
    "denoScripts"
  ]
}

④撰寫Deno腳本

首先,用TypeScript撰寫文件[^2],讓未來的我可以知道這個API會回傳什麼:

type ApiResponse = OkResponse | ErrorResponse;

interface OkResponse {
  status: "ok";
  code: 200;
  message: string;
  data: { toolstackLink: string };
}

interface ErrorResponse {
  status: "error";
  code: 400 | 404 | 500;
  message: string;
}

接著,填寫必要的config constants:[^3]

const OUTPUT_PATH: string = "./src/data/fetched/toolstackLink.json";

const BASE_API_URL: string = "https://script.google.com/macros/s/<ID>/exec";
const QUERY_KEY: string = "toolstackLink";
const url = new URL(BASE_API_URL);
url.searchParams.set("key", QUERY_KEY);
const API_URL: string = url.toString();

最後,寫個fetcher:

async function fetchLink(): Promise<void> {
  try {
    const response: Response = await fetch(API_URL, {
      method: "GET",
      headers: {
        Accept: "application/json",
      },
    });

    if (!response.ok) {
      throw new Error(
        `Fetch failed: ${response.status} ${response.statusText}`
      );
    }

    const responseData: ApiResponse = await response.json();

    if (responseData.status !== "ok") {
      console.log(responseData.message);
      Deno.exit(1);
    }

    if (!responseData.data) {
      console.log("No data received from API");
      Deno.exit(1);
    }

    const jsonString: string = JSON.stringify(responseData.data, null, 2);
    await Deno.writeTextFile(OUTPUT_PATH, jsonString);

    console.log(`Saved toolstack link JSON to ${OUTPUT_PATH}`);
  } catch (error: unknown) {
    console.error(
      "Error:",
      error instanceof Error ? error.message : String(error)
    );
    Deno.exit(1);
  }
}

await fetchLink();

這裡麻煩的是,由於我的doGet會回傳一個假的HTTP response,所以我同樣的動作要額外再做一次🙃

個人覺得美好的地方是,我可以使用Deno.writeTextFileDeno.exit,語法上比Node.js簡潔乾淨,不用import { writeFile } from "fs/promises"。在小腳本的差別不大,如果是更複雜的腳本應該會很有感。

⑤撰寫指令

由於這個專案主要是基於Node.js與pnpm,所以這裡就依然讓pnpm來幫我代為呼叫Deno:
"get-link": "deno run --allow-net --allow-write ./denoScripts/fetch-link.ts",

這裡指令顯得很冗長,是因為Deno安全機制的flag。[^4][^5]

由於腳本裡使用了fetch,所以我們必須明確給予Deno--allow-net的權限才能執行;同樣的,由於腳本裡使用了Deno.writeTextFile,所以必須給予--allow-write

後話

當下覺得挺好玩的,嘗試後確定可以無痛地於Node.js環境的專案,額外呼叫Deno來執行小腳本。Deno因為內建TypeScript,所以可以直接run TypeScript腳本。

同時,儘管只是小練習,我也可以想像為什麼那天那位德國工程師不喜歡Deno。畢竟工作上的專案都有時程壓力,Deno的安全機制會帶來小不便,反之Bun則可能更快更快樂。

總之,如果是work project,還是乖乖用Node.js就好。

Annotations

[^1]: 是的,我當時嘗試在以Node.js為主環境的專案上使用Deno🤩

[^2]: 當下我覺得在這個小練習用TypeScript真的太heavy。

[^3]: 由於這是prebuild時執行的腳本,所以我把URL直接寫在腳本裡。

[^4]: Node.js誕生初期,Ryan只是想實作出讓JavaScript可以跑I/O,那時原本以為只是個人小專案,所以讓Node.js擁有完整系統存取權限,殊不知Node.js最終變成JavaScript生態系如此關鍵的環境。現在Ryan重頭來過,Deno就改成預設無權限,需要開發者與腳本使用者在每次執行腳本時主動給予權限。

[^5]: Node.js也採納了Deno這套機制,目前在最新版本有Permission Model


上一篇
Day19|歡迎來到Deno主題樂園
系列文
我只是不想加班:一名客服人員的GAS自救之路20
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言