iT邦幫忙

0

【kintone 外部串接】Google 表單自動登錄至 kintone - 實作範例

  • 分享至 

  • xImage
  •  

Google 表單對個人與企業而言,都是一項相當便利的工具,常被用於活動報名、問卷調查、意見回饋等各式情境。
若能在使用者填寫表單後,自動將資料同步至 kintone 應用程式中集中管理,即可大幅減少人工整理與轉錄的成本。

本篇文章將介紹如何透過 Google Apps Script(GAS),在 Google 表單送出時,自動呼叫 kintone REST API,將填答內容新增為一筆 kintone 記錄,並提供一個可直接實作的完整範例。

💡本範例需準備可使用的 kintone 帳號,以及一組 Google 帳號。

開發流程概覽

整體實作流程可分為以下三個步驟:

  1. 製作 Google 表單
  2. 製作對應的 kintone 應用程式
  3. 撰寫 Google Apps Script,將表單資料送至 kintone

本次範例將示範製作一份「活動報名表」,並將其填答內容自動登錄至 kintone。

表單與欄位設計

以下為本範例中,Google 表單題目與 kintone 欄位之對應關係:

項目 Google 表單問題類型 kintone 欄位類型 kintone 欄位代碼
姓名 簡答 單行文字方塊 name
手機號碼 簡答 單行文字方塊 phone
電子信箱 簡答 單行文字方塊 email
報名場次 選擇題 選項按鈕 session
如何得知 核取方塊 複選 source
如何得知(其他) 核取方塊 單行文字方塊 source_other

💡「如何得知(其他)」為 Google 表單中「如何得知」題目勾選「其他」並填寫自訂內容時的答案,會另存至 kintone 的單行文字欄位。

製作 Google 表單

登入 Google 帳號後,進入 Google 表單,依照上述欄位設計建立一份活動報名表。


製作 kintone 應用程式

建立應用程式表單

回到 kintone,建立一個新的應用程式,並新增與 Google 表單對應的欄位。

💡 特別注意「報名場次」與「如何得知」等選項型欄位,其選項值需與 Google 表單中的選項文字完全一致,否則在新增記錄時將發生錯誤。

取得 API 權杖(API Token)

接著,至 設定 → 自訂/服務整合 → API 權杖 中新增一個 API 權杖,並且賦予其「新增記錄」的存取權限。
此 API 權杖將用於 GAS 呼叫 kintone REST API 時進行身分驗證。

撰寫 Google Apps Script

從表單開啟 Apps Script 編輯器

回到 Google 表單,點擊右上角的「更多選項」,選擇 指令碼編輯器(Apps Script)

專案設定

進入 Apps Script 專案後,可先調整專案名稱,接著點擊側邊欄的「專案設定」。

在專案設定中,勾選 「在編輯器中顯示 appsscript.json 資訊清單檔案」。

回到編輯器後,打開 appsscript.json,並加入以下 OAuth scopes 設定:

"oauthScopes": [
  "https://www.googleapis.com/auth/forms",
  "https://www.googleapis.com/auth/forms.currentonly",
  "https://www.googleapis.com/auth/script.external_request",
  "https://www.googleapis.com/auth/drive"
],

💡 若追加在 JSON 中的最後一項,請去除結尾的逗號 ,

此設定用於授權 GAS 存取表單內容,以及透過 UrlFetchApp 呼叫外部 API(kintone)。

撰寫程式碼

回到 程式碼.gs 檔案中,貼上並完成以下程式碼,完成後記得儲存。

範例程式碼如下:

/*
 * Google 表單與 kintone 連動 - 範例程式碼
 * Copyright (c) 2025 Cybozu
 *
 * Licensed under the MIT License
 * https://opensource.org/license/mit/
 */

// ===== 表單題目文字與 kintone 欄位代碼的對應 =====
const FORM_TITLES = {
  name: '姓名',
  phone: '手機號碼',
  email: '電子信箱',
  session: '報名場次',
  source: '如何得知',
  source_other: '如何得知(其他)',
}

// ===== kintone 環境資訊 =====
const KINTONE_DOMAIN = 'EXAMPLE.cybozu.com'   // 不要加 https://
const KINTONE_APP_ID = '123'
const KINTONE_API_TOKEN = 'YOUR_API_TOKEN'

// ===== 主程式:表單送出時自動執行 =====
function onFormSubmit(e) {
  // 用來存放「題目文字 → 回答」的對照表
  const formData = {}

  // 取得「這一次送出」的所有題目與回答
  const itemResponses = e.response.getItemResponses()

  itemResponses.forEach(ir => {
    // 表單題目文字
    const title = ir.getItem().getTitle()
    // 使用者的回答
    const response = ir.getResponse()

    // ---- 特別處理「如何得知(核取方塊 + 其他)」 ----
    if (title === FORM_TITLES.source) {
      // 取得此題在表單中設定的「固定選項」
      const choiceValues = ir
        .getItem()
        .asCheckboxItem()
        .getChoices()
        .map(c => c.getValue())

      let sourceValues = []        // 固定選項(給 kintone 多選)
      let sourceOtherValue = ''    // 其他自填(給單行文字)
      response.forEach(value => {
        if (choiceValues.includes(value)) {
          // 在固定選項中 → 多選欄位
          sourceValues.push(value)
        } else {
          // 不在固定選項中 → 視為「其他」
          sourceOtherValue = value
        }
      })

      // 存入 formData
      formData[FORM_TITLES.source] = sourceValues
      formData[FORM_TITLES.source_other] = sourceOtherValue
      return
    }

    // ---- 其他題目直接存入 formData ----
    formData[title] = response
  })

  // 檢查表單資料長什麼樣(除錯用)
  console.log('formData:', formData)

  // ===== 組成 kintone record 格式 =====
  const record = {
    name: { value: formData[FORM_TITLES.name] || '' },
    phone: { value: formData[FORM_TITLES.phone] || '' },
    email: { value: formData[FORM_TITLES.email] || '' },
    session: { value: formData[FORM_TITLES.session] || '' },
    source: { value: formData[FORM_TITLES.source] || [] },
    source_other: { value: formData[FORM_TITLES.source_other] || '' }
  }

  console.log('kintone record:', record)

  // ===== 呼叫 kintone API 新增記錄 =====
  const url = `https://${KINTONE_DOMAIN}/k/v1/record.json`
  const payload = {
    app: KINTONE_APP_ID,
    record
  }

  const res = UrlFetchApp.fetch(url, {
    method: 'post',
    contentType: 'application/json',
    headers: {
      'X-Cybozu-API-Token': KINTONE_API_TOKEN
    },
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  })

  const code = res.getResponseCode()
  const text = res.getContentText()
  console.log('kintone response:', code, text)

  if (code < 200 || code >= 300) {
    throw new Error(`kintone API error (${code}): ${text}`)
  }
}

程式碼說明

以下將針對程式碼的整體結構與關鍵處理邏輯進行說明。

表單題目與欄位對應設定

const FORM_TITLES = {
  name: '姓名',
  phone: '手機號碼',
  email: '電子信箱',
  session: '報名場次',
  source: '如何得知',
  source_other: '如何得知(其他)',
}

此物件用來定義 Google 表單題目文字 與 kintone 欄位代碼 的對應關係,後續在解析表單回傳資料時,能避免直接寫死題目文字,提升可讀性與維護性。

kintone 環境設定

此區段用於設定呼叫 kintone REST API 所需的環境資訊,請依實際使用環境替換。

const KINTONE_DOMAIN = 'EXAMPLE.cybozu.com'
const KINTONE_APP_ID = '123'
const KINTONE_API_TOKEN = 'YOUR_API_TOKEN'
  • KINTONE_DOMAIN:kintone 網域名稱(不需包含 https://)
  • KINTONE_APP_ID:目標應用程式的 App ID
  • KINTONE_API_TOKEN:具備「新增記錄」權限的 API 權杖

主程式:onFormSubmit(e)

function onFormSubmit(e) {

此函式為 Google 表單送出觸發 的進入點。
當使用者送出表單時,GAS 會自動將事件資訊包裝成 e 物件傳入。

透過 e.response.getItemResponses(),即可取得「本次送出」的所有題目與對應回答。

表單資料解析與整理

const formData = {}

程式中會先建立一個 formData 物件,用來暫存「題目文字 → 回答內容」的對照結果,方便後續組合成 kintone 記錄格式。

onFormSubmit(e) 中,這段是整個資料取得的核心:

const itemResponses = e.response.getItemResponses()

itemResponses.forEach(ir => {
  // 表單題目文字
  const title = ir.getItem().getTitle()
  // 使用者的回答
  const response = ir.getResponse()
e.response.getItemResponses() 取得的是什麼?
  • e 是 Google 表單「送出事件」的 event 物件。
  • e.response 代表「這一次送出」的表單回覆(不是整份表單所有回覆)。
  • getItemResponses() 會回傳一個陣列,每一個元素都是一題的回答物件(ItemResponse)。

因此 itemResponses 可以理解為:
本次送出中,每一道題目(Item)及其回答(Response)的集合

接著便可以透過 itemResponses.forEach(ir => ...) 來處理每一個回答物件(ir)。
ir 是單一題目的回答物件(ItemResponse),你可以從中分別取得:

題目文字:ir.getItem().getTitle()
const title = ir.getItem().getTitle()
  • ir.getItem() 取得此回答對應的題目(Item)
  • getTitle() 取得題目在表單畫面上顯示的「題目文字」
使用者回答:ir.getResponse()
const response = ir.getResponse()

getResponse() 會回傳「該題型對應的回答格式」,依題目類型不同而不同,常見情況如下:

  • 簡答 / 段落:回傳字串(string
  • 選擇題(單選):回傳字串(string
  • 核取方塊(多選):回傳陣列(string[]

在本範例中:

  • 姓名 / 手機號碼 / 電子信箱 / 報名場次 → 回傳字串
  • 如何得知 → 回傳包含字串的陣列(就算只勾一個,也仍是陣列)

「如何得知」的回答拆分

由於 Google 表單核取方塊的回答值是字串陣列(string[]),資料格式和 kintone 複選欄位相同,在所有選項皆一致,且沒有其他自填選項的情況下,其實可以直接使用 getResponse() 的回傳值。

不過,在本範例中有加入「其他」的自填選項,讓填表者可以自行填寫其他項目,因此必須要把自填的值從陣列中拆分出來,後續才能順利更新至 kintone 記錄中。

if (title === FORM_TITLES.source) {
  // 取得此題在表單中設定的「固定選項」
  const choiceValues = ir
    .getItem()
    .asCheckboxItem()
    .getChoices()
    .map(c => c.getValue())

  let sourceValues = []        // 固定選項(給 kintone 多選)
  let sourceOtherValue = ''    // 其他自填(給單行文字)
  response.forEach(value => {
    if (choiceValues.includes(value)) {
      // 在固定選項中 → 多選欄位
      sourceValues.push(value)
    } else {
      // 不在固定選項中 → 視為「其他」
      sourceOtherValue = value
    }
  })

  // 存入 formData
  formData[FORM_TITLES.source] = sourceValues
  formData[FORM_TITLES.source_other] = sourceOtherValue
  return
}

kintone REST API 新增記錄的處理流程

本次範例使用到 kintone REST API 的「新增記錄」端點:

  • API:新增記錄(Add Record)
  • PathPOST /k/v1/record.json

程式碼對應段落如下:

const url = `https://${KINTONE_DOMAIN}/k/v1/record.json`
const payload = {
  app: KINTONE_APP_ID,
  record
}

const res = UrlFetchApp.fetch(url, {
  method: 'post',
  contentType: 'application/json',
  headers: {
    'X-Cybozu-API-Token': KINTONE_API_TOKEN
  },
  payload: JSON.stringify(payload),
  muteHttpExceptions: true
})
Request Body(payload)的結構
const payload = {
  app: KINTONE_APP_ID,
  record
}

這裡的 payload 會被轉成 JSON 送出,必要參數如下:

  • app:目標應用程式 ID
  • record:要新增的那一筆記錄內容(每個欄位都要符合 kintone 的資料格式)

在前面的處理中,已經將 Google 表單整理成 formData,接著必須再轉成 kintone 需要的 record

const record = {
  name: { value: formData[FORM_TITLES.name] || '' },
  phone: { value: formData[FORM_TITLES.phone] || '' },
  email: { value: formData[FORM_TITLES.email] || '' },
  session: { value: formData[FORM_TITLES.session] || '' },
  source: { value: formData[FORM_TITLES.source] || [] },
  source_other: { value: formData[FORM_TITLES.source_other] || '' }
}
驗證方式:X-Cybozu-API-Token
headers: {
  'X-Cybozu-API-Token': KINTONE_API_TOKEN
}

存取 kintone 環境需要身份驗證,這裡採用應用程式 API 權杖,僅授權必要的權限(針對報名表應用程式的新增記錄權限)。

送出 JSON 的設定:contentTypepayload
contentType: 'application/json',
payload: JSON.stringify(payload),
  • contentType 告訴伺服器這是一段 JSON
  • UrlFetchApp.fetchpayload 需要以 JSON.stringify() 轉換成字串

設定觸發條件

編輯完程式碼後,請先儲存變更。
接著進入側邊欄的「觸發條件」設定頁面,新增一個觸發條件,設定內容如下:

  • 選擇您要執行的功能:onFormSubmit(剛才撰寫的主程式)
  • 選擇應執行的部署作業:上端(Head)
  • 選取活動來源:表單
  • 選取活動類型:提交表單時

💡 若發現執行功能沒有 onFormSubmit 可選,可能是因為沒有儲存到程式碼變更,請回到編輯器儲存後,再進行觸發條件設定。


點擊儲存後,如果是第一次進行設定,可能會跳出需要登入與授權的對話窗,請選擇專案所在的登入帳號,並信任此應用程式存取表單。




設定完成後就大功告成!可以實際發送表單,確認資料是否正確上傳至 kintone。

補充:查看執行記錄

點進側邊欄的「執行項目」,可以檢視程式是否有被觸發,以及觸發的記錄內容。
如果有發生錯誤,通常可以在此查看到錯誤訊息,可以利用 console.log() 檢查各階段的資料格式來排除問題。

結語

本篇範例示範了如何僅透過 Google Apps Script(GAS),在 Google 表單送出時直接呼叫 kintone REST API,將填答內容自動登錄至 kintone 應用程式中。
由於不需要另外架設中介伺服器,對開發者而言是一個導入門檻低、實作成本小的選項。

透過這樣的方式,能快速將表單蒐集到的資料整合至 kintone 進行後續管理與運用,期望本篇內容能作為實務上的參考,協助你更有效率地完成系統串接與資料自動化。


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言