メインコンテンツまでスキップ

Prisma スキーマを利用したローカル開発

GASsma は clasp+esbuild 等を利用し TypeScript を使ってローカルで GAS を開発する際に、Prisma 形式のスキーマファイルから型安全なクライアントコードを自動生成する機能を提供しています。

この機能を利用すると、リレーション定義や defaults・map 等の設定がスキーマから自動生成されるため、GASsma 独自のコンストラクタオプション(relationsdefaultsupdatedAtignoremap など)を手動で記述する必要がなくなります。Prisma のスキーマ構文さえ知っていれば、Prisma と同じ感覚で開発を始められます。

CLI なし(手動設定):

import { Gassma } from "gassma";

const gassma = new Gassma.GassmaClient({
id: "SPREAD_SHEET_ID",
relations: {
User: {
posts: { type: "oneToMany", to: "Post", field: "id", reference: "authorId", onDelete: "Cascade" },
},
Post: {
author: { type: "manyToOne", to: "User", field: "authorId", reference: "id" },
},
},
defaults: { User: { role: "USER" } },
updatedAt: { Post: "updatedAt" },
map: { User: { firstName: "名前" } },
});

CLI あり(スキーマから自動生成):

import { GassmaClient } from "./generated/gassma/schemaClient";

// リレーション・defaults・updatedAt・map 等すべて自動注入済み
const gassma = new GassmaClient();

前提

以下のコマンドで GASsma の CLI ツールをインストールしてください。

$ npm i gassma

スキーマファイルの作成

プロジェクト内に .prisma ファイルを作成します。デフォルトでは ./gassma ディレクトリ配下が探索されます。

my-project/
├── gassma/
│ └── schema.prisma ← ここにスキーマを記述
├── package.json
└── ...

基本的な書き方

Prisma の文法でモデルを定義します。generator ブロックの output で出力先を指定してください。

generator client {
provider = "prisma-client-js"
output = "./generated/gassma"
}

model User {
id Int @id
name String
email String?
age Int
}

previewFeatures

generator ブロックに previewFeatures を指定すると、オプトインの機能を有効化できます(Prisma の previewFeatures と同じ書き方です)。

generator client {
provider = "prisma-client-js"
output = "./generated/gassma"
previewFeatures = ["strictUndefinedChecks"]
}

現在サポートされている機能:

機能説明参照
strictUndefinedChecksクエリ入力の明示的な undefined を実行時エラーにするstrictUndefinedChecks / Gassma.skip

有効化すると、生成されるクライアント JS に strictUndefinedChecks: true が埋め込まれ、生成される型定義にも Gassma.skip を受け付ける型が反映されます。

型マッピング

Prisma の型は以下の TypeScript 型に変換されます。

Prisma 型TypeScript 型
Intnumber
Floatnumber
Decimalnumber
BigIntnumber
Stringstring
Booleanboolean
DateTimeDate
Jsonstring
Bytesstring

? を付けるとオプショナルフィールドになります(null が許容されます)。

リレーション定義

Prisma の @relation 属性を使うと、リレーション情報が自動的に抽出され、生成されたクライアントに注入されます。

generator client {
provider = "prisma-client-js"
output = "./generated/gassma"
}

model User {
id Int @id
name String
posts Post[]
}

model Post {
id Int @id
title String
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
authorId Int
}

上記の定義から以下のリレーション設定が自動生成されます。

  • User.posts: oneToMany(User → Post)
  • Post.author: manyToOne(Post → User、onDelete: Cascade)

暗黙的 Many-to-Many

双方向の配列参照がある場合、暗黙的な Many-to-Many リレーションが自動検出されます。

model Post {
id Int @id
tags Tag[]
}

model Tag {
id Int @id
name String
posts Post[]
}

生成されるクライアントでは、中間テーブル名が _PostToTag(モデル名のアルファベット順)として自動的に解決されます。@relation("PostTags") のようにリレーションに名前を付けた場合は、リレーション名がそのまま中間テーブル名(_PostTags)になります(Prisma と同じ規則です)。

注記

中間テーブル(シート)自体は migrate / db push で自動作成できます(スプレッドシート側に同名のシートを手動で用意しても構いません)。 中間テーブル名を変更したい場合は、@relation でリレーションに名前を付けてください。

enum

Prisma の enum 定義からリテラルユニオン型が自動生成されます。

enum Role {
ADMIN
USER
MODERATOR
}

model User {
id Int @id
role Role
}

生成される型:

"role": "ADMIN" | "USER" | "MODERATOR"

enum の @map

enum メンバーに @map を付けると、コード上の名前とスプレッドシート上の値をマッピングできます。

enum Role {
admin @map("ADMIN")
user @map("USER")
moderator @map("MODERATOR")
}

生成される定数:

const Role = {
admin: "ADMIN",
user: "USER",
moderator: "MODERATOR",
} as const;

型定義には @map の値が使用されます:

"role": "ADMIN" | "USER" | "MODERATOR"

@gassma.addType

Prisma のフィールドコメント(///)に @gassma.addType を記述すると、フィールドの型にユニオン型を追加できます。

model User {
/// @gassma.addType string
id Int @id // 生成型: number | string

/// @gassma.addType string, boolean
score Int // 生成型: number | string | boolean

name String // 生成型: string(コメントなしなら通常通り)
}

@gassma.replaceType

@gassma.addType は基底型とのユニオンですが、@gassma.replaceType は基底型を置換して指定した型のみ生成します。

model User {
/// @gassma.replaceType "admin", "user", "moderator"
role String
}

生成される型:

"role": "admin" | "user" | "moderator"  // string を含まない
注記

優先順位: enum > replaceType > addType。enum がある場合は replaceType / addType は無視されます。

@default

@default() が付いたフィールドは、生成される Create 入力型でオプショナル(?)になります。

model User {
id Int @id @default(autoincrement())
name String
isActive Boolean @default(true)
createdAt DateTime @default(now())
}

生成される型:

"isActive"?: boolean   // @default(true) → オプショナル
"createdAt"?: Date // @default(now()) → オプショナル

生成されるクライアント JS には defaults 設定が自動的に埋め込まれます。

@default()生成される JS
@default(true) / @default(false)true / false
@default(0) (数値)0
@default("USER") (文字列)"USER"
@default(ADMIN) (enum 値)"ADMIN"
@default(active) (enum 値・active @map("ACTIVE"))"ACTIVE"@map 後の値)
@default(now())() => new Date()
@default(uuid())() => Utilities.getUuid()
@default(autoincrement())autoincrement 設定として別途生成

@updatedAt

@updatedAt が付いたフィールドは Create 入力型でオプショナルになり、生成されるクライアント JS に updatedAt 設定が埋め込まれます。

model Post {
id Int @id
title String
updatedAt DateTime @updatedAt
}

@ignore

@ignore が付いたフィールドは型定義から完全に除外され、生成されるクライアント JS に ignore 設定が埋め込まれます。

model User {
id Int @id
name String
secret String @ignore // 型定義に含まれない
}

@map

@map("name") でフィールド名のマッピングを定義できます。生成されるクライアント JS に map 設定が埋め込まれます。

model User {
id Int @id
firstName String @map("名前")
lastName String @map("名字")
}

コード上は firstName / lastName で操作し、スプレッドシート上は「名前」「名字」カラムに対応します。

@@ignore

モデルレベルの @@ignore でシート全体を除外できます。生成されるクライアント JS に ignoreSheets 設定が埋め込まれます。

model Logs {
id Int @id
message String

@@ignore
}

@@map

モデルレベルの @@map("name") でシート名をマッピングできます。

model Users {
id Int @id
name String

@@map("ユーザー一覧")
}

コード上は Users でアクセスし、スプレッドシート上は「ユーザー一覧」シートに対応します。

CLI コマンド

gassma generate

型ファイルとクライアントコードを生成します。

$ npx gassma generate

デフォルトでは ./gassma ディレクトリ内の .prisma ファイルが探索されます。--schema オプションで特定のスキーマファイルまたはディレクトリを指定できます(Prisma の prisma generate --schema に相当)。

$ npx gassma generate --schema gassma/user.prisma
$ npx gassma generate --schema ./schemas

--watch オプションでスキーマファイルの変更を監視し、自動で再生成できます。

$ npx gassma generate --watch

--schema との併用も可能です。

--config オプションで設定ファイルのパスを明示的に指定できます(Prisma の --config に相当)。

$ npx gassma generate --config configs/gassma.config.ts

指定したファイルが存在しない場合は ConfigFileNotFoundError になります。未指定時は従来どおりデフォルトの場所が探索されます(後述の「設定ファイルの探索規則」を参照)。

gassma init

プロジェクトを初期化し、スキーマファイルと設定ファイルを自動生成します。

$ npx gassma init

以下のファイルが生成されます:

  • gassma/schema.prisma — 初期スキーマ
  • gassma.config.ts — 設定ファイル
オプション説明
--output <path>生成先パスをカスタマイズ
--with-modelサンプル User モデルを含むスキーマを生成

既に schema.prisma が存在する場合はエラーで安全に停止します。

gassma validate

スキーマファイルの構文チェック・整合性チェックを行います(Prisma の prisma validate に相当)。

$ npx gassma validate
$ npx gassma validate --schema gassma/test.prisma

--config オプションで設定ファイルのパスを指定することもできます。

チェック項目:

  • 構文エラー(パーサーエラー検出)
  • generator ブロックの存在チェック
  • output フィールドの必須チェック
  • モデルが 1 つ以上定義されていること

成功時は以下のように出力されます:

The schema at /path/to/gassma/test.prisma is valid 🚀

gassma format

.prisma ファイルを Prisma 公式と同じフォーマットで整形します(@prisma/internalsformatSchema を使用)。

$ npx gassma format
オプション説明
--schema <path>特定ファイルのみ整形
--config <path>設定ファイルのパスを指定
--checkフォーマット済みかチェック(CI 用、未整形時は exit 1)

gassma studio

datasource に設定したスプレッドシートを、OS のデフォルトブラウザで開きます。

$ npx gassma studio
オプション説明
--config <path>設定ファイルのパスを指定

URL は以下の順で解決されます。

  1. スキーマ内の datasource ブロックの url
  2. gassma.config.tsdatasource.url

url にフル URL(https://...)を指定している場合はそのまま開き、スプレッドシート ID を指定している場合は https://docs.google.com/spreadsheets/d/<id>/edit を組み立てて開きます。どちらにも URL が設定されていない場合は NoDatasourceUrlError になります。

gassma version

GASsma CLI のバージョンを表示します。

$ npx gassma version

--version / -V フラグでも確認できます。

オプション説明
--jsonバージョン情報を JSON で出力

--json を付けると、バージョン情報を JSON 形式({"gassma":"<version>"})で出力します。

$ npx gassma version --json
{"gassma":"1.2.3"}

生成されるファイル

スキーマファイル名をもとに以下のファイルが生成されます。例えば schema.prisma の場合:

ファイル内容
schema.d.ts型定義(モデル型、クエリ型、共通型)
schemaClient.jsクライアント実装(リレーション定義の自動注入込み)
schemaClient.d.tsクライアントの型定義

出力先は generator ブロックの output で指定したディレクトリです。

生成されたクライアントの使い方

生成されたクライアントファイルから GassmaClient をインポートしてそのまま使えます。リレーション定義は自動注入済みです。

import { GassmaClient } from "./generated/gassma/schemaClient";

const gassma = new GassmaClient();

// 型安全にシートへアクセス
const users = gassma.User.findMany({
where: { age: { gte: 20 } },
select: { name: true, email: true },
});

Prisma と同じパターンでインスタンス化できます。

// Prisma
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();

// GASsma(同じパターン)
import { GassmaClient } from "./generated/gassma/schemaClient";
const gassma = new GassmaClient();

オプション付きの初期化

// スプレッドシートID指定
const gassma = new GassmaClient("SPREAD_SHEET_ID");

// オプションオブジェクト
const gassma = new GassmaClient({
id: "SPREAD_SHEET_ID",
omit: {
User: { password: true },
},
});

設定ファイル(gassma.config.ts)

プロジェクトルートに gassma.config.ts を配置することで、CLI の設定を一元管理できます(Prisma の prisma.config.ts に相当)。TypeScript 以外の拡張子(.js / .mjs / .cjs / .mts / .cts)や .config/ ディレクトリへの配置にも対応しています(後述の「設定ファイルの探索規則」を参照)。

設定インターフェース

設定ファイルの記述方法は 2 つあります。

1. defineConfig ヘルパーを使用(推奨):

import { defineConfig } from "gassma/config";

export default defineConfig({
schema: "gassma/schema.prisma",
datasource: {
url: "https://docs.google.com/spreadsheets/d/XXXXX/edit",
},
});

2. satisfies 演算子を使用:

import type { GassmaConfig } from "gassma";

export default {
schema: "gassma/schema.prisma",
datasource: {
url: "https://docs.google.com/spreadsheets/d/XXXXX/edit",
},
} satisfies GassmaConfig;

GassmaConfig 型は gassma パッケージのルートから import できます。

設定オプション

オプション必須説明
schemastringいいえスキーマファイルまたはディレクトリのパス(デフォルト: ./gassma
datasource.urlstringいいえスプレッドシートの URL または ID

設定ファイルの探索規則

設定ファイルは以下の順序で探索され、最初に見つかったファイル が採用されます。

  1. gassma.config.js
  2. gassma.config.ts
  3. gassma.config.mjs
  4. gassma.config.cjs
  5. gassma.config.mts
  6. gassma.config.cts
  7. .config/gassma.js
  8. .config/gassma.ts
  9. .config/gassma.mjs
  10. .config/gassma.cjs
  11. .config/gassma.mts
  12. .config/gassma.cts

プロジェクトルート直下の gassma.config.* が全拡張子ぶん先に探索され、その後 .config/ ディレクトリ内の gassma.* が探索されます。.js.ts より先に採用される点も含め、Prisma の設定ファイル探索と同じ順序です。

--config オプション

generate--watch 含む)/ validate / format / studio の各コマンドでは、--config オプションで設定ファイルのパスを明示的に指定できます(Prisma の --config に相当)。

$ npx gassma generate --config configs/gassma.config.ts
  • 相対パスは実行時のカレントディレクトリ基準で解決されます。
  • 指定したファイルが存在しない場合は ConfigFileNotFoundError になります。
  • 未指定時は上記の探索規則に従ってデフォルトの場所が探索されます。

ロード時の挙動

gassma generate 実行時、設定ファイルのロードに成功すると以下のように表示されます。

⚙️ Loaded config from gassma.config.ts
  • 設定ファイルに構文エラー・実行時エラーがある場合や、既知のキー(schema / datasource.url)の型が不正な場合は GassmaConfigLoadError になります。
  • 未知のキーが含まれている場合は警告が表示され、そのキーは無視されます(エラーにはなりません)。
Warning: Unknown property `outut` in /path/to/gassma.config.ts. Known properties are: schema, datasource. It will be ignored.

env() ヘルパー

env() 関数を使うと、環境変数からスプレッドシート URL を取得できます(Prisma の env() に相当)。

import "dotenv/config";
import { defineConfig, env } from "gassma/config";

export default defineConfig({
schema: "gassma",
datasource: {
url: env("SPREADSHEET_URL"),
},
});

satisfies パターンでも使用できます。

import "dotenv/config";
import type { GassmaConfig } from "gassma";
import { env } from "gassma/config";

export default {
schema: "gassma",
datasource: {
url: env("SPREADSHEET_URL"),
},
} satisfies GassmaConfig;

型付きの env()

型引数に環境変数のインターフェースを渡すと、env() に指定できる名前がそのキーに限定され、補完も効くようになります。

import "dotenv/config";
import { defineConfig, env } from "gassma/config";

interface Env {
SPREADSHEET_URL: string;
}

export default defineConfig({
schema: "gassma",
datasource: {
url: env<Env>("SPREADSHEET_URL"),
},
});

指定できるのは値が string(または string | undefined)型のキーのみです。存在しないキーを指定するとコンパイルエラーになります。

注記

env() は環境変数が未設定または空文字の場合に GassmaConfigEnvError をスローします。オプショナルな環境変数には process.env を直接使用してください。

datasource.url

datasource.url にスプレッドシートの URL または ID を指定すると、生成されるクライアント JS に id が自動埋め込みされます。これにより new GassmaClient() だけで対象スプレッドシートに接続できます。

フル URL とスプレッドシート ID の両方に対応しています。

// フル URL
datasource: {
url: "https://docs.google.com/spreadsheets/d/XXXXX/edit",
}

// ID 直接指定
datasource: {
url: "XXXXX",
}

スキーマ内の datasource ブロック

スキーマファイル内に datasource ブロックを記述することでも URL を指定できます。

datasource db {
provider = "google-spreadsheet"
url = "https://docs.google.com/spreadsheets/d/XXXXX/edit"
}

URL 解決の優先順位

  1. スキーマ内の datasource ブロック(最優先)
  2. gassma.config.tsdatasource.url

スキーマ解決の優先順位

  1. --schema オプション(最優先)
  2. gassma.config.tsschema 設定
  3. デフォルト ./gassma ディレクトリ

相対パスの解決基準はそれぞれ異なります。--schema オプションは実行時のカレントディレクトリ基準、設定ファイルの schema設定ファイルのある場所基準 で解決されます(Prisma と同じ)。

gassma init を実行すると gassma.config.ts も自動生成されます。

マルチファイルスキーマ

同じディレクトリ(およびサブディレクトリ)内に複数の .prisma ファイルを配置すると、自動的に 1 つのスキーマとして統合 されます。Prisma の Multi-file schema と同等の機能です。

gassma/
├── schema.prisma ← generator ブロックをここに記述
├── models/
│ ├── user.prisma ← User, Profile モデル
│ └── post.prisma ← Post, Comment モデル

generator ブロックはいずれか 1 ファイルに記述すれば、全ファイルで共有されます。すべてのモデルが 1 つのクライアント出力にまとめられます。

複数スキーマ(複数スプレッドシート)

異なるスプレッドシートを扱う場合は、スキーマを別々のディレクトリに分けて個別に生成します。型名にはスキーマ名のプレフィックスが付与されるため、同名モデルがあっても衝突しません。

schemas/
├── user/
│ └── schema.prisma → userClient.js, user.d.ts
└── order/
└── schema.prisma → orderClient.js, order.d.ts
import { GassmaClient as UserClient } from "./generated/user/schemaClient";
import { GassmaClient as OrderClient } from "./generated/order/schemaClient";

const userGassma = new UserClient();
const orderGassma = new OrderClient();

生成される型の概要

生成される .d.ts には以下の型が含まれます。

  • モデル型: 各フィールドの型定義(GassmaUserUse 等)
  • クエリ型: FindDataCreateDataUpdateDataDeleteDataUpsertData
  • Select / Omit 型: フィールド選択・除外の型
  • フィルタ型: WhereUseFilterConditionsFieldRef 対応含む)
  • OrderBy 型: ソート条件(リレーションソート、_count ソート、nulls 制御含む)
  • Include 型: リレーション取得の型(_count 含む)
  • Nested Write 型: リレーション先の作成・接続・更新・削除操作
  • 数値操作型: NumberOperation(increment / decrement / multiply / divide)
  • 共通型: FieldRefGassmaClientOptions、エラークラス群
  • 設定型: DefaultsConfigUpdatedAtConfigIgnoreConfigAutoincrementConfigMapConfig
  • コントローラー型: 全メソッドの引数・戻り値型