ClaspとZodでGASのバリカタ(Vali型)開発環境を作る
TL;DR
Zod の導入設定をした GAS プロジェクトのテンプレートを作って公開しました。
はじめに
こんにちは、クラウドエースの伊藝です。
突然ですが、皆さんは バリカタ は好きですか?
私は豚骨ラーメン屋さんに行くと、いつも バリカタ を頼んでいます。
家でインスタント麺を作るときも、プログラムを作るときも バリカタ です。
そうです。「Validation 型 (バリデーション・カタ)」、略して バリカタ です。
型ってなに?
型とは、プログラムで扱われるデータがどういう形式のものかを示すものです。
型があることで、我々プログラマーはデータがどういう形式であるかを認識しやすくなります。
バリデーションってなに?
バリデーションとは、入力されたデータが適切な形であるかをチェックして、不適切であれば例外としてエラーを返す処理のことです。
GAS ってなに?
GAS とは Google Apps Script の略称で、JavaScript をベースとしたプログラミング言語です。
GAS は Google のプロダクトと連携することができ、例えば Gmail や Google Sheets の処理を自動化することができます。
また、HTTP API を通して Google 以外が提供している外部サービスと連携することもできます。
Clasp
Clasp とは、GAS を開発するためのツールで、GAS のプロジェクトの作成・編集・ビルド・デプロイをすることができます。
GAS は通常 JavaScript で記述するのですが、Clasp を使うことにより、TypeScript で記述することができます。
TypeScript とは JavaScript の型の機能を強化した言語で、型安全な開発を実現できます。
TypeScript は非常に依存性が高く、一度使ってしまうと二度と普通の JavaScript が書けなくなってしまう安全な代物です。
Clasp の TypeScript で記述できる機能を使うことで、トランスパイルするまではある程度の型安全性が保たれます。
しかし、実行時に違う型のデータを入力されると、そのデータの型が自動で検証 (バリデーション) されることはなく、予期しない動作が発生してしまう恐れがあります。
これでは バリカタ ではなく、ただの カタ です。
Zod
Zod は TypeScript ファーストなバリデーションライブラリです。
サイズがとても小さく、依存関係がゼロとなっています。
他の様々なライブラリやフレームワークでも採用が増えており、注目されています。
GAS をバリカタで書く
本記事では、Clasp と一緒に Zod を導入して作る GAS のバリカタ (Validation 型) 開発環境の手順を紹介していきます。
Clasp
インストール・設定
ディレクトリを作成して yarn
で初期化します。
mkdir gas-zod-template
cd gas-zod-template
yarn init
clasp
をインストールします。
yarn add @google/clasp
TypeScript でソースコードを記述するために typescript
を、GAS 関連の補完を使うために @types/google-apps-script
をインストールします、
yarn add --dev typescript @types/google-apps-script
clasp
コマンドで Google アカウントにログインします。
yarn clasp login
既に GAS のプロジェクトがある場合はクローンします。
[scriptId]
は GAS プロジェクト固有の ID です。
これは GAS プロジェクトを開いたときの URL に含まれています。
https://script.google.com/home/projects/[scriptId]/edit
yarn clasp clone [scriptId]
新しく作成する場合は以下のコマンドを実行します。
yarn clasp create
新規作成すると、.clasp.json
と appsscript.json
が生成されます。
.clasp.json
には、どの GAS プロジェクトと連携されているか、どのスクリプトをアップロードするかなどの Clasp のローカルの設定が記述されています。
詳しい説明は以下のページにあります。(英語)
appsscript.json
には、GAS プロジェクトの設定が記述されています。本記事で編集することはありません。
こちらも詳しい説明は以下のページにあります。(英語)
.clasp.json
を開いて、以下のように変更します。
{
"scriptId": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
- "rootDir": "/XXXX/YYYY/ZZZZ"
+ "rootDir": "./src"
}
src
ディレクトリを作成し、appsscript.json
を src
の下に移動します。
mkdir src
mv ./appsscript.json ./src
src/main.ts
を作成し、以下のように記述します。
type User = {
name: string;
age: number;
};
function main() {
const user: User = {
name: 'John',
age: 20,
};
console.log(`${user.name} is ${user.age}`);
}
この時点でのディレクトリ構成は以下のようになっています。
$ tree -a
.
├── .clasp.json
├── node_modules
~~~
├── package.json
├── src
│ ├── appsscript.json
│ └── main.ts
└── yarn.lock
実行
ソースコードを GAS にプッシュします。
yarn clasp push
以下のコマンドを実行して GAS のプロジェクトの Web エディタを開きます。
yarn clasp open
画面上部の「実行」をクリックすると main
関数を実行することができます。
ソースコードに注目すると、TypeScript が JavaScript にトランスパイルされていることがわかります。
Webpack
インストール・設定
npm パッケージを GAS に導入するために Webpack をインストールします。
yarn add --dev webpack webpack-cli gas-webpack-plugin
Babel をインストールします。
yarn add --dev @babel/core @babel/preset-typescript babel-loader
ESLint と Prettier をインストールします。
yarn add --dev eslint prettier eslint-config-prettier @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-webpack-plugin @babel/eslint-parser
インストールしたツール群の設定を行うために以下のファイルを作成・編集します。
-
tsconfig.json
: TypeScript の設定ファイル -
.babelrc
: Babel の設定ファイル -
.eslintrc.json
: ESLint の設定ファイル -
webpack.config.js
: Webpack の設定ファイル -
package.json
: Node.js の設定ファイル
package.json
に scripts
を追加します。
...
+ "scripts": {
+ "build": "webpack",
+ "push": "yarn build && clasp push",
+ "push:watch": "yarn build --watch & clasp push --watch"
+ },
...
実行
src/main.ts
を src/app/main.ts
に移動して、main
関数の頭に export
を追記します。
mkdir src/app
mv src/main.ts src/app/main.ts
type User = {
name: string;
age: number;
};
-function main() {
+export function main() {
const user: User = {
name: 'John',
age: 20,
};
console.log(`${user.name} is ${user.age}`);
}
src/index.ts
を作成し、以下のように記述します。
このファイルは GAS でのエントリポイントとなり、ここで global.*
に代入された関数は GAS で実行可能となります。
import { main } from "./app/main";
declare const global: {
[x: string]: unknown;
};
global.main = main;
appsscript.json
を dist
の下に移動します。
mkdir dist
mv ./appsscript.json ./dist
Webpack でビルドしたファイルを GAS に push するために .clasp.json
の rootDir
を "./dist"
に変更します。
{
"scriptId": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
- "rootDir": "./src"
+ "rootDir": "./dist"
}
ソースコードをビルドして GAS にプッシュします。
yarn push
Web エディタから実行すると、ソースコードの処理部分に変更はないため、先程と同じ結果が表示されます。
以下のコマンドを実行すると、ファイルの変更が自動的に検知され、ビルドされて GAS にプッシュされて便利です。
yarn push:watch
Zod
本記事の本題の Zod をインストールします。
yarn add zod
バリデーションする値を入力するため、GAS で簡易的なウェブアプリを作成します。
app/src/main.ts
を以下のように変更します。
-type User = {
- name: string;
- age: number;
-};
-
-export function main() {
- const user: User = {
- name: 'John',
- age: 20,
- };
-
- console.log(`${user.name} is ${user.age}`);
-}
+import { z } from "zod";
+
+export const UserSchema = z.object({
+ name: z.string(),
+ age: z.number(),
+});
+
+export function doGet(e: GoogleAppsScript.Events.DoGet): GoogleAppsScript.Content.TextOutput {
+ const user = UserSchema.parse(e.parameter);
+ Logger.log(user);
+ return ContentService.createTextOutput(`${user.name} is ${user.age}`);
+}
doGet(e)
は GAS でウェブアプリを作成するために使う関数名です。
実行したい関数の名前が変わったため、src/app/index.ts
も以下のように変更します。
-import { main } from "./app/main";
+import { doGet } from "./app/main";
declare const global: {
[x: string]: unknown;
};
-global.main = main;
+global.doGet = doGet;
この時点でのディレクトリ構成は以下のようになっています。
$ tree -a
.
├── .babelrc
├── .clasp.json
├── .eslintrc.json
├── dist
│ ├── appsscript.json
│ └── index.js
├── node_modules
~~~
├── package.json
├── src
│ ├── app
│ │ └── main.ts
│ └── index.ts
├── tsconfig.json
├── webpack.config.js
└── yarn.lock
ソースコードをビルドして GAS にプッシュします。
yarn push
以下のコマンドを実行して GAS のプロジェクトを開きます。
yarn clasp open
画面右上の「デプロイ」ボタンをクリックし、「新しいデプロイ」を選択します。
「種類の選択」からウェブアプリを選択し、パラメータはそのままで「デプロイ」ボタンをクリックします。
以下のようなウェブアプリの URL が表示されます。
https://script.google.com/macros/s/XXXXXXXXXX/exec
URL の末尾にパラメータを追記してアクセスします。
https://script.google.com/macros/s/XXXXXXXXXX/exec?name=John&age=20
パラメータが User
としてパースされてメッセージが表示されます。
John is 20
試しに age
を twenty
にします。
https://script.google.com/macros/s/XXXXXXXXXX/exec?name=John&age=twenty
すると、Zod で型のバリデーションが行われてエラーが表示されます。
[ { "code": "invalid_type", "expected": "number", "received": "string", "path": [ "age" ], "message": "Expected number, received string" } ] (line 291, file "index")
これで GAS に Zod を導入し、型のバリデーションをすることができました。
Zod は string
や number
などの単純な型のバリデーションを行うだけでなく、以下のように様々なデータを安全に扱うための機能を備えています。
- 配列以外にも Union や Map、Set などのデータ構造をフィールドに指定する
- 「〇〇以上 & 〇〇以下」などの条件を指定する
- NULL を許容するかしないかを指定する
- etc.
おわりに
設定が少し面倒ですが、これでバリカタな GAS を書けるようになりました。
型のバリデーションを利用することで、Web アプリケーション以外にも、Google Sheets を使ったアプリケーションを型安全に開発することができます。
「こんな設定したくないよ~」という人のために、バリカタな GAS のテンプレートを用意しました。
.clasp.json
にプロジェクト ID を設定して、yarn install
と yarn deploy
をするだけで簡単にデプロイをすることができます。
1 人でも多くの人が GAS プロジェクトをバリカタで開発してくれるようになってくれると嬉しいです。
参考
Discussion