シナプスの技術部システム開発課の小園です。
Google Apps Scriptでウェブアプリケーションを作ってみました。
- Google Apps Script とは
- GASでウェブアプリケーションとは?
- 作ったもの
- ウェブアプリケーション ブラウザ側の技術要素
- Webサイト内容
- デプロイ
- ブラウザ表示内容
- 良かった事/困った事
- 作ってみての所感
Google Apps Script とは
Google Apps Script(以下GAS)は、Googleが提供するクラウドベースのスクリプト言語で、Googleの各種サービス(Googleドキュメント、Googleスプレッドシート、Googleフォームなど)に統合された独自の開発環境を持つ、サーバーレスなプログラミングプラットフォームです。
www.google.com
developers.google.comGASでは以下のように様々なことができます。
- Googleの各種サービスとの連携
- Gmail、Googleドキュメント、Googleカレンダー、Googleスプレッドシートなど、Googleの様々なサービスと簡単に連携することができます。例えば、Gmailの自動返信や、Googleカレンダーのイベント作成、Googleスプレッドシートのデータ処理などが可能です。
- 外部APIとの連携
- 外部APIとの連携も可能です。例えば、Twitter APIやSlack APIなどのAPIを使って、外部サービスとGASを連携させることができます。
- スプレッドシートの外部関数から外部APIを利用する事で、強力な関数を用意することができます。
- トリガー機能による定時実行
- 指定した時間や条件でスクリプトを実行可能です。
- ウェブアプリケーションの作成
- 簡単なウェブアプリケーションを作成することができます。例えば、Webフォームを作成して、スプレッドシートにデータを保存するアプリケーションを作成することができます。
- Googleの各種サービスとの連携
- GASはV8 ランタイムで動いており、ES2015(ES6)の構文で書けます。
V8 ランタイムの概要 | Apps Script | Google Developers - GASは無償のGoogleアカウント、有償のGoogle Workspaceアカウント どちらでも利用できます。
- リソース枠に違いがあります。変更されることもあるので、現在の状況は公式サイトを確認してください。
Google サービスの割り当て | Apps Script | Google Developers
- リソース枠に違いがあります。変更されることもあるので、現在の状況は公式サイトを確認してください。
GASでウェブアプリケーションとは?
GASでは、ブラウザからのGET/POSTを処理する機能やデータソースにアクセスできる機能があります。
その機能を使う事で、GASで簡単なウェブアプリケーションを作成する事ができます。
- HTML、CSS、JavaScriptを使用して、ウェブアプリケーションのフロントエンドを作成することができる HTMLサービスというサービスがあります。
ウェブアプリ | Apps Script | Google Developers - Googleが提供するサービスとのシームレスな統合を可能にするAPIを提供しており、Googleサービスの機能を呼び出して、データの取得、処理、更新、保存を行うことができます。
- Google スプレッドシート ファイルの作成、アクセス、変更が可能になるサービスが提供されています。
Spreadsheet Service | Apps Script | Google Developers - JDBC サービスを使用して外部データベースに接続できます。JDBC サービスは、Google Cloud SQL for MySQL、MySQL、Microsoft SQL Server、Oracle データベースをサポートしています。
JDBC | Apps Script | Google Developers
- Google スプレッドシート ファイルの作成、アクセス、変更が可能になるサービスが提供されています。
作ったもの
- Googleフォームで入力されたコンテンツをGoogleスプレッドシートに保存し、GASからスプレッドシートにデータを取得し、そのデータを表示するウェブアプリケーションを作成しました。
連携図は以下です。
graph LR; subgraph Googleクラウド Googleフォーム -- データ保存 --> Googleスプレッドシート; GAS -- データ取得 --> Googleスプレッドシート end subgraph ブラウザ vue.js_Webサイト -- HTTPリクエスト --> GAS; vue.js_Webサイト -- JSONデータ取得 --> GAS; end
- この中でGASはウェブアプリケーションのコントローラー機能と、フロントエンドからのデータ取得機能を兼用した機能を持っています。
ウェブアプリケーションで利用するコンテンツ
- Googleスプレッドシートにデータを用意しました。
- データはA列から、index、タイムスタンプ、タイトル、日付、リード文、本文のデータを持っています。
この中で、indexとタイムスタンプは自動的に付与したデータになっています。
- Googleフォームに入力されたコンテンツをGoogleスプレッドシートに保存する箇所は今回の記事内では省略しています。
ウェブアプリケーション ブラウザ側の技術要素
- ウェブアプリケーションは、以下の技術を用いて構築しました。
- Vue.js v2.7
jp.vuejs.org- フロントエンド(ブラウザ)側のJavaScriptフレームワーク
- Vue.js はビルドなしで構築しました。ビルドをする場合には、clasp(google/clasp [Command Line Apps Script Projects])を利用します。
github.com
- Bootstrap5
getbootstrap.jp- フロントエンド(ブラウザ)側のUIフレームワーク
- Vue.js v2.7
Webサイト内容
最終的なファイル構成
- GAS上での最終的なファイル構成は以下のようになります。
- GASでは、.jsや.cssをそのまま保存できません。必ず.htmlという拡張子になります。
- 最初は、
コード.gs
のみが存在していますので、必要なファイルを追加、編集していきます。 - ファイル追加は右側の
+
から行います。- 拡張子 .html, .js , .css のファイルを追加する場合には、
+
押下後にHTML
を選択します。- ブラウザ側で動かすjavascriptファイルは
HTML
を選択します。 index.html
を作成したい場合には、index
と入れます。
拡張子は入力しません。
自動的に拡張子.html
が付加されます。.js
又は.css
ファイルを作成したい場合には、拡張子まで含めて入れます。
index.js
を作成したい場合には、index.js
と入れます。
実際に作成されるファイル名は拡張子.html
が付加され、index.js.html
となります。
- ブラウザ側で動かすjavascriptファイルは
- クラウド側で動かすjavascriptファイルを追加する場合には、
+
押下後にスクリプト
を選択します。
入力するファイル名は拡張子を除いたものにします。
自動的に拡張子.gs
が付加されます。
- 拡張子 .html, .js , .css のファイルを追加する場合には、
appsscript.json
は画面左側の歯車マーク プロジェクトの設定 の"「appsscript.json」マニフェスト ファイルをエディタで表示する"にチェックを入れた場合に表示されるファイルです。
今回の開発内容ではチェックを入れても、入れなくても影響はありません。
GASプロジェクト作成
- 最初にGoogleドライブから GASプロジェクト を作成します。
- その他の中にある Google Apps Script を選択します。
- 紐づくアプリケーションが存在しないGASプロジェクトを作成します。
- スタンドアロンスクリプトと呼ばれます。
スプレッドシート、フォーム等のアプリケーションに紐づいたGASプロジェクトは、コンテナバインドプロジェクトと呼ばれます。
- スタンドアロンスクリプトと呼ばれます。
GASスクリプト
コード.gs
- プロジェクト作成時に作られる(存在している)ファイルです。
ファイル中に最初から存在する関数myFunction
は不要です。削除しても問題ありません。 - doGet関数は、ブラウザからのGETメソッドでアクセスした場合に呼ばれます。
- POSTメソッドに対応したdoPost関数もあります。
- GETやPOSTのパラメータは、
parameter
又はparameters
オブジェクトに格納されています。
コード中のe.parameter.index
の箇所です。 e.parameter.index
はGETの場合、URLクエリパラメータ(クエスチョン以降)のindexの値が設定されています。
http://exapmle.com?index=[この値]
- GASプロジェクト内に作成したjsやcssは外部ファイルとしてhtml(ブラウザ)から呼び出せないので、include関数でhtml内にファイルの中身を展開します。
// コンテンツデータを保存したスプレッドシートのid const sheet_id = "**********************************************************"; // スプレッドシートのシート名 const sheet_name = "*******"; const page_title = "GAS ウェブアプリケーション サンプル"; // GETメソッドでアクセスすると実行される関数 function doGet(e) { const index = Number(e.parameter.index); if(!index) { return HtmlService.createTemplateFromFile('error').evaluate(); } const template = HtmlService.createTemplateFromFile('index'); template.title = page_title; template.index = index; return template.evaluate(); } // htmlから他ファイル(.js、.cssなど)を読み込んで展開するためのHelper関数 function include(filename) { return HtmlService.createHtmlOutputFromFile(filename) .getContent(); } // スプレッドシートからデータ一覧を取得 function getSpreadsheetValues() { const spreadsheet = SpreadsheetApp.openById(sheet_id); const sheet = spreadsheet.getSheetByName(sheet_name); const table = sheet.getDataRange().getDisplayValues().slice(1); const filterd_table = table.filter(a => a[1] != ""); return filterd_table; } // スプレッドシートデータ一覧から指定行を取得 function getSpreadsheetValue(index) { const rows = getSpreadsheetValues(); if(rows.length < index){ return []; } return rows[index-1]; }
htmlファイル
index.html
- コンテンツを表示するHTMLです。
<?!= 関数(); ?>
はGASの関数を実行して、結果を展開します。<?= 変数 ?>
はGASスクリプトで定義した値を展開します。- include関数はサーバサイトでファイルの中身が展開されてブラウザに渡します。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title><?= title ?></title> <?!= include('libraries.css'); ?> <script> const index = new Number(<?= index ?>); </script> </head> <body> <header> <nav class="navbar navbar-dark bg-primary mb-2"> <div class="navbar-header"><a class="navbar-brand" href="#"><?= title ?></a></div> </nav> </header> <div class="container" id="app"> <main role="main" class="container"> <div class="row"> <div class="col-md-8 blog-main"> <div class="blog-post"> <h2 class="blog-post-title">{{ article[2] }}</h2> <p class="blog-post-meta">{{ article[3] }}</p> <div> <p> {{ article[4] }} </p> <p> {{ article[5] }} </p> </div> </div> </div> </div> </main> </div> <?!= include('libraries.js'); ?> <?!= include('index.js'); ?> </body> </html>
error.html
- エラー時に表示されるHTMLです。
- しっかり作る時には、エラー内容などが伝えられるように作りましょう。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>error</title> </head> <body> <div> <span>error</span> </div> </body> </html>
cssファイル
libraries.css.html
- bootstrapのcssを読み込んでいます。
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
jsファイル
libraries.js.html
- Vue.jsとbootstrapを読み込んでいます。
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
index.js.html
- Vue.jsのコードです。
google.script.run
でサーバサイドの関数を呼び出せます。
クラス google.script.run(クライアントサイド API) | Apps Script | Google Developers
<script> var app = new Vue({ el: "#app", data: { article: [], index: index, }, mounted() { const self = this; self.loadSheetIndexValues(); }, methods: { loadSheetIndexValues: async function() { const self = this; self.article = await self.getSheetData(); }, getSheetData: function() { const self = this; return new Promise((resolve, reject) => { google.script.run .withSuccessHandler((result) => resolve(result)) .withFailureHandler((error) => resolve(error)) .getSpreadsheetValue(self.index.valueOf()); }); }, }, }) </script>
デプロイ
- デプロイをする事で、ウェブアプリケーションとしてブラウザからアクセスできるようになります。
- プロジェクト右側の
デプロイ
ボタンから新しいデプロイ
を選びます。 - 新しいデプロイの画面が表示されるので、各項目に値を設定してデプロイを選択します。
- 次のユーザーとして実行は、
自分
又はウェブアプリケーションにアクセスしているユーザ
のどちらかを選択します。
ウェブアプリケーション開発時にアプリ開発/保守/実行をするユーザを作成する方法を取る事が多いと思います。
その場合は自分
を選択します。- ウェブアプリケーションの実行時にリソース(スプレッドシートなど)に対して権限を付与する必要があります。
ウェブアプリケーションにアクセスしているユーザ
を選択すると、アクセスする全ユーザに権限付与を設定する必要があります。
- ウェブアプリケーションの実行時にリソース(スプレッドシートなど)に対して権限を付与する必要があります。
- アクセスできるユーザーは、
自分のみ
又はGoogleアカウントを持つ全員
又は全員
のいずれかを選択します。
有償のGoogle Workspaceアカウントを利用の場合には、そのグループに属する人を対象にする選択子が増えます。
- 次のユーザーとして実行は、
- デプロイをすると以下の画面が表示されます。
ウェブアプリケーションのURLが確認できます。
ブラウザ表示内容
- ブラウザに表示される内容です。
- デプロイ時に表示されるURLをブラウザに入力します。
- 今回はindexというURLクエリパラメータを必要としています。デプロイ時に表示されたURLの最後に
?index=1
のような文字列を付加します。
- 今回はindexというURLクエリパラメータを必要としています。デプロイ時に表示されたURLの最後に
- 画面上部にGoogleからの注記が表示されます。
これは、無償のGoogleアカウントを利用したため、URLのドメインがGoogleの所有物であるscript.google.com
であるためです。
有償のGoogle Workspaceを利用した場合(独自ドメインを利用している場合)はこの注記は表示されません。
良かった事/困った事
良かった事
- 認証が楽。
- 特にGoogleWorkspaceでは、組織単位の公開が簡単にできる。
- 無料で動かせる。
- データソース(スプレッドシート)、Webサーバなども無料で利用可。
- デプロイの概念があり、公開バージョンの管理や、デプロイのテストをする機能がある。
困った事
- URLがGoogle側にて生成するものになる。
- URLが長い。
- URLに処理内容の意味を持たせる事ができない。
- URLからコードに辿る事ができない。
- GASのリソース枠を消費すると使うことができない。
- リソース枠を消費したときに、有償でリソース枠を増やすことはできない。
- 無償のGoogleアカウント、有償のGoogle Workspaceアカウントでリソース枠は異なる。
- Vue.js devtools が使えない。
- 悪意のある HTML や JavaScript がユーザーに提供されないようにするため、iframe を使用して HTML サービスのウェブアプリをサンドボックス化します。
HTML サービス: 制限事項 | Apps Script | Google Developers
- 悪意のある HTML や JavaScript がユーザーに提供されないようにするため、iframe を使用して HTML サービスのウェブアプリをサンドボックス化します。
- スプレッドシートをデータベースとして扱うのは辛い。
- トランザクションが無い。
- インデックスが無い。
- SQLのように条件(WHERE)、並び順(ORDER)を書けない。
- WHEREはQUERY関数を使う方法がある。
- Cloud SQLを使う方法もある(JDBCを使える)が、費用が掛かる。
- Googleフォームズから簡単にデータ投入できるメリットはあるが…
作ってみての所感
小規模で簡易なウェブアプリケーションを作る事は可能。
Googleスプレッドシートをデータソースとするウェブアプリケーションは簡単に作成できるが、ある程度規模が大きくなるとボトルネックになる。
リソース枠を超えての使うことは出来ないので、そこもボトルネックになる。