はじめに (対象読者・この記事でわかること)
この記事は、JavaScriptの基本的な知識があり、Google Apps Script(GAS)に興味がある方を対象としています。また、GASで作成したウェブアプリを外部から呼び出したいと考えている方にもおすすめです。
この記事を読むことで、GASでウェブアプリを作成する方法、JavaScriptのfetch APIを使ってGASウェブアプリを呼び出す方法、CORSエラーの原因と解決策、そして認証が必要なGASウェブアプリの呼び出し方法を理解できます。具体的なコード例と共に実践的な知識を習得し、GASで作成した機能をウェブサイトや他のアプリケーションから利用できるようになります。
前提知識
この記事を読み進める上で、以下の知識があるとスムーズです。 - JavaScriptの基本的な知識 - Googleアカウントの取得 - Google Apps Scriptの基本的な操作
GASウェブアプリの概要とfetch APIの必要性
Google Apps Script(GAS)は、Googleの各サービス(Googleドキュメント、スプレッドシートなど)を自動化するための強力なツールです。GASで作成したスクリプトは、ウェブアプリとしてデプロイすることで、外部からも呼び出すことが可能になります。
しかし、GASウェブアプリを外部のJavaScriptコードから呼び出す際には、いくつかの課題があります。特に、CORS(Cross-Origin Resource Sharing)エラーは多くの開発者が直面する問題です。また、認証が必要なアプリケーションでは、適切な認証方法を実装する必要があります。
fetch APIは、現代のJavaScriptでリソースを取得するための標準的なインターフェースです。Promiseベースで非同期処理を簡単に記述でき、XMLHttpRequestよりも直感的で強力です。GASウェブアプリをfetch APIで呼び出すことで、モダンなJavaScript開発の流れに沿った形でGAS機能を活用できます。
fetch APIを使ったGASウェブアプリの実装方法
ステップ1:GASでウェブアプリを作成する
まずは、GASで簡単なウェブアプリを作成します。ここでは、リクエストを受け取ってJSON形式でレスポンスを返すシンプルなAPIを作成します。
Javascriptfunction doGet(e) { // リクエストパラメータを取得 const param = e.parameter.param || 'default'; // レスポンスオブジェクトを作成 const response = { status: 'success', message: `Hello, ${param}!`, timestamp: new Date().toISOString() }; // JSON形式でレスポンスを返す return ContentService.createTextOutput(JSON.stringify(response)) .setMimeType(ContentService.MimeType.JSON); }
このコードをGASエディタに貼り付け、「ウェブアプリとして導入」を選択してデプロイします。デプロイ時には、「ウェブアプリとしてアクセスできるユーザー」を「自分」「Googleアカウントを持っているすべてのユーザー」「匿名」などから適切に選択してください。
デプロイが完了したら、ウェブアプリのURLをコピーしておきます。このURLは後でfetch APIから呼び出す際に使用します。
ステップ2:JavaScriptでfetch APIを使ってGASウェブアプリを呼び出す
次に、HTMLとJavaScriptを使って、先ほど作成したGASウェブアプリを呼び出すコードを作成します。
Html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>GAS Web App Test</title> </head> <body> <h1>GAS Web App Test</h1> <button id="callApi">Call GAS API</button> <div id="result"></div> <script> document.getElementById('callApi').addEventListener('click', async () => { try { // GASウェブアプリのURL const gasUrl = 'YOUR_GAS_WEB_APP_URL'; // fetch APIでリクエストを送信 const response = await fetch(gasUrl + '?param=World'); // レスポンスをJSONとして解析 const data = await response.json(); // 結果を表示 document.getElementById('result').innerHTML = ` <p>Status: ${data.status}</p> <p>Message: ${data.message}</p> <p>Timestamp: ${data.timestamp}</p> `; } catch (error) { console.error('Error:', error); document.getElementById('result').innerHTML = `<p>Error: ${error.message}</p>`; } }); </script> </body> </html>
上記のコードでは、ボタンがクリックされたときにfetch APIを使ってGASウェブアプリを呼び出しています。URLのYOUR_GAS_WEB_APP_URLの部分には、実際のGASウェブアプリのURLを設定してください。
ステップ3:POSTリクエストを使ったデータ送信
GETリクエストだけでなく、POSTリクエストを使ってデータを送信する方法も紹介します。GAS側でdoPost関数を実装し、クライアント側でfetch APIのオプションを設定します。
GAS側のコード:
Javascriptfunction doPost(e) { // リクエストボディを取得 const requestBody = JSON.parse(e.postData.contents); // レスポンスオブジェクトを作成 const response = { status: 'success', receivedData: requestBody, message: 'Data received successfully', timestamp: new Date().toISOString() }; // JSON形式でレスポンスを返す return ContentService.createTextOutput(JSON.stringify(response)) .setMimeType(ContentService.MimeType.JSON); }
クライアント側のコード:
Javascriptdocument.getElementById('callApi').addEventListener('click', async () => { try { // GASウェブアプリのURL const gasUrl = 'YOUR_GAS_WEB_APP_URL'; // 送信するデータ const postData = { name: 'John Doe', email: 'john@example.com', message: 'Hello from fetch API!' }; // fetch APIでPOSTリクエストを送信 const response = await fetch(gasUrl, { method: 'POST', body: JSON.stringify(postData), headers: { 'Content-Type': 'application/json' } }); // レスポンスをJSONとして解析 const data = await response.json(); // 結果を表示 document.getElementById('result').innerHTML = ` <p>Status: ${data.status}</p> <p>Message: ${data.message}</p> <p>Received Data: ${JSON.stringify(data.receivedData, null, 2)}</p> <p>Timestamp: ${data.timestamp}</p> `; } catch (error) { console.error('Error:', error); document.getElementById('result').innerHTML = `<p>Error: ${error.message}</p>`; } });
ハマった点やエラー解決
GASウェブアプリをfetch APIから呼び出す際には、いくつかのエラーに直面することがあります。特に多いのがCORSエラーです。
CORSエラーの例:
Access to fetch at 'https://script.google.com/macros/s/.../exec' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
このエラーは、ウェブアプリが異なるオリジンからのリクエストを許可していない場合に発生します。特に、ローカル環境(localhost)からGASウェブアプリを呼び出す際によく発生します。
また、認証が必要なGASウェブアプリを呼び出す際には、401 Unauthorizedや403 Forbiddenエラーが発生することがあります。
解決策
CORSエラーの解決策:
GASウェブアプリ側でCORSを許可するように設定する必要があります。以下のコードをGASに追加してください。
Javascriptfunction doGet(e) { // CORSヘッダーを設定 const output = ContentService.createTextOutput(JSON.stringify({ status: 'success', message: 'Hello, World!', timestamp: new Date().toISOString() })); output.setMimeType(ContentService.MimeType.JSON); output.setCorsHeaders(); return output; } // CORSヘッダーを設定する関数 function setCorsHeaders() { const headers = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, Authorization' }; Object.keys(headers).forEach(key => { output.setHeader(key, headers[key]); }); }
上記のコードでは、setCorsHeaders()関数を使ってCORS関連のヘッダーを設定しています。Access-Control-Allow-Origin: *はすべてのオリジンからのリクエストを許可しますが、セキュリティ上の理由から、本番環境では特定のオリジンのみを許可することをお勧めします。
認証が必要なGASウェブアプリの呼び出し:
GASウェブアプリが認証を必要とする場合、OAuth2.0を使用して認証を行う必要があります。以下に、GoogleのOAuth2.0ライブラリを使用した認証の例を示します。
Html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>GAS Web App with Auth</title> </head> <body> <h1>GAS Web App with Auth</h1> <button id="login">Login with Google</button> <button id="callApi" style="display: none;">Call GAS API</button> <div id="result"></div> <script> // GoogleのOAuth2.0クライアントID const CLIENT_ID = 'YOUR_CLIENT_ID'; const API_KEY = 'YOUR_API_KEY'; const DISCOVERY_DOCS = ["https://www.googleapis.com/discovery/v1/apis/drive/v3/rest"]; const SCOPES = "https://www.googleapis.com/auth/drive.metadata.readonly"; let tokenClient; let gapiInited = false; let gisInited = false; // Google APIの初期化 function initializeGapi() { gapi.load('client', initializeGapiClient); } function initializeGapiClient() { gapi.client.init({ apiKey: API_KEY, discoveryDocs: DISCOVERY_DOCS, }).then(() => { gapiInited = true; maybeEnableButtons(); }); } function initializeGis() { tokenClient = google.accounts.oauth2.initTokenClient({ client_id: CLIENT_ID, scope: SCOPES, callback: "", // defined later }); gisInited = true; maybeEnableButtons(); } function maybeEnableButtons() { if (gapiInited && gisInited) { document.getElementById('login').style.display = 'block'; } } // ログイン処理 document.getElementById('login').addEventListener('click', () => { tokenClient.callback = async (resp) => { if (resp.error !== undefined) { throw (resp); } document.getElementById('login').style.display = 'none'; document.getElementById('callApi').style.display = 'block'; }; if (gapi.client.getToken() === null) { tokenClient.requestAccessToken({prompt: 'consent'}); } else { tokenClient.requestAccessToken({prompt: ''}); } }); // GAS API呼び出し処理 document.getElementById('callApi').addEventListener('click', async () => { try { // トークンを取得 const token = gapi.client.getToken(); // GASウェブアプリのURL const gasUrl = 'YOUR_GAS_WEB_APP_URL'; // fetch APIでリクエストを送信(認証トークンをヘッダーに追加) const response = await fetch(gasUrl, { headers: { 'Authorization': `Bearer ${token.access_token}` } }); // レスポンスをJSONとして解析 const data = await response.json(); // 結果を表示 document.getElementById('result').innerHTML = ` <p>Status: ${data.status}</p> <p>Message: ${data.message}</p> <p>Timestamp: ${data.timestamp}</p> `; } catch (error) { console.error('Error:', error); document.getElementById('result').innerHTML = `<p>Error: ${error.message}</p>`; } }); // ページ読み込み時に初期化 document.addEventListener('DOMContentLoaded', () => { // Google APIスクリプトの読み込み const script = document.createElement('script'); script.src = 'https://apis.google.com/js/api.js'; script.onload = initializeGapi; document.head.appendChild(script); // Google Identity Servicesスクリプトの読み込み const gisScript = document.createElement('script'); gisScript.src = 'https://accounts.google.com/gsi/client'; gisScript.onload = initializeGis; document.head.appendChild(gisScript); }); </script> </body> </html>
上記のコードでは、GoogleのOAuth2.0を使用して認証を行い、取得したアクセストークンをGASウェブアプリのリクエストに含めています。YOUR_CLIENT_IDとYOUR_API_KEYには、実際のGoogle Cloud Consoleで取得した値を設定してください。
まとめ
本記事では、JavaScriptのfetch APIを使ってGoogle Apps Scriptで作成したウェブアプリを呼び出す方法を解説しました。具体的には、以下の内容を扱いました。
- GASでウェブアプリを作成する方法
- fetch APIを使ってGASウェブアプリを呼び出す基本的な方法
- POSTリクエストを使ったデータ送信の実装
- CORSエラーの原因と解決策
- 認証が必要なGASウェブアプリの呼び出し方法(OAuth2.0を使用)
これらの知識を活用することで、GASで作成した機能をウェブサイトや他のアプリケーションから利用できるようになります。特に、CORSの設定やOAuth2.0の実装は、実際の開発現場で頻繁に使用される技術なので、ぜひマスターしてください。
今後は、GASウェブアプリのパフォーマンス最適化や、より高度な認証方法についても記事にする予定です。