データベーススキーマドキュメント
ReCoronのデータベース設計と各テーブルの詳細説明です。
📋 目次
概要
ReCoronはPrisma ORMを使用してPostgreSQLデータベースと連携しています。スキーマは以下の4つの主要カテゴリに分類されています:
- Enums - 基本的な型定義
- 認証関連 - Better Authによるユーザー管理
- コア機能 - ジョブとAPIキー管理
- 使用量追跡 - 課金と悪用防止
Enums(列挙型)
Plan(プラン)
ユーザーの料金プランを定義します。
| 値 | 説明 | ジョブ数 | 月次実行回数 | 最小実行間隔 |
|---|---|---|---|---|
FREE | 無料プラン | 5個 | 500回 | 60分 |
HOBBY | 趣味プラン | 20個 | 10,000回 | 30分 |
PRO | プロプラン | 100個 | 50,000回 | 15分 |
Method(HTTPメソッド)
HTTPリクエストのメソッドを定義します。
GET- リソースの取得POST- リソースの作成PUT- リソースの完全更新DELETE- リソースの削除PATCH- リソースの部分更新
Type(実行タイプ)
ジョブの実行方法を定義します。
| 値 | 説明 |
|---|---|
AUTO | 自動実行(スケジュールに基づく) |
MANUAL | 手動実行(API経由) |
認証関連モデル
User(ユーザー)
システムのユーザー情報を管理します。Better Authと連携しています。
フィールド
| フィールド | 型 | 必須 | デフォルト | 説明 |
|---|---|---|---|---|
id | String | ✓ | - | ユーザーID(主キー) |
name | String | ✓ | - | ユーザー名 |
email | String | ✓ | - | メールアドレス(一意) |
emailVerified | Boolean | ✓ | false | メール認証済みフラグ |
image | String? | - | null | プロフィール画像URL |
plan | Plan | ✓ | FREE | 料金プラン |
isAdmin | Boolean | ✓ | false | 管理者権限フラグ |
createdAt | DateTime | ✓ | now() | 作成日時 |
updatedAt | DateTime | ✓ | now() | 更新日時 |
リレーション
sessions- ユーザーのセッション(1対多)accounts- OAuth連携アカウント(1対多)jobs- 作成したジョブ(1対多)logs- 実行ログ(1対多)apiKeys- APIキー(1対多)monthlyUsages- 月次使用量(1対多)dailyUsages- 日次使用量(1対多)resourceHistories- リソース履歴(1対多)WebhookJobs- Webhookジョブ(1対多)
インデックス
email- 一意制約
Session(セッション)
ユーザーの認証セッションを管理します。
フィールド
| フィールド | 型 | 必須 | デフォルト | 説明 |
|---|---|---|---|---|
id | String | ✓ | - | セッションID(主キー) |
token | String | ✓ | - | セッショントークン(一意) |
expiresAt | DateTime | ✓ | - | 有効期限 |
ipAddress | String? | - | null | アクセス元IPアドレス |
userAgent | String? | - | null | ユーザーエージェント |
userId | String | ✓ | - | ユーザーID(外部キー) |
createdAt | DateTime | ✓ | now() | 作成日時 |
updatedAt | DateTime | ✓ | now() | 更新日時 |
リレーション
user- ユーザー(多対1、カスケード削除)
インデックス
token- 一意制約
Account(アカウント)
OAuth連携情報やパスワード認証を管理します。
フィールド
| フィールド | 型 | 必須 | デフォルト | 説明 |
|---|---|---|---|---|
id | String | ✓ | - | アカウントID(主キー) |
accountId | String | ✓ | - | プロバイダー側のアカウントID |
providerId | String | ✓ | - | プロバイダーID(github, google等) |
userId | String | ✓ | - | ユーザーID(外部キー) |
accessToken | String? | - | null | アクセストークン |
refreshToken | String? | - | null | リフレッシュトークン |
idToken | String? | - | null | IDトークン |
accessTokenExpiresAt | DateTime? | - | null | アクセストークン有効期限 |
refreshTokenExpiresAt | DateTime? | - | null | リフレッシュトークン有効期限 |
scope | String? | - | null | スコープ |
password | String? | - | null | パスワードハッシュ |
createdAt | DateTime | ✓ | now() | 作成日時 |
updatedAt | DateTime | ✓ | now() | 更新日時 |
リレーション
user- ユーザー(多対1、カスケード削除)
Verification(検証)
メール認証などの検証トークンを管理します。
フィールド
| フィールド | 型 | 必須 | デフォルト | 説明 |
|---|---|---|---|---|
id | String | ✓ | - | 検証ID(主キー) |
identifier | String | ✓ | - | 識別子(メールアドレス等) |
value | String | ✓ | - | 検証トークン |
expiresAt | DateTime | ✓ | - | 有効期限 |
createdAt | DateTime | ✓ | now() | 作成日時 |
updatedAt | DateTime | ✓ | now() | 更新日時 |
コア機能モデル
Job(Cronジョブ)
スケジュール実行されるHTTPリクエストを管理します。
フィールド
| フィールド | 型 | 必須 | デフォルト | 説明 |
|---|---|---|---|---|
id | String | ✓ | cuid() | ジョブID(主キー) |
userId | String | ✓ | - | ユーザーID(外部キー) |
name | String | ✓ | - | ジョブ名 |
schedule | String | ✓ | - | Cron式(例: "0 9 * * *") |
timezone | String | ✓ | "Asia/Tokyo" | タイムゾーン |
url | String | ✓ | - | リクエスト先URL |
method | Method | ✓ | GET | HTTPメソッド |
headers | Json? | - | null | カスタムヘッダー |
body | String? | - | null | リクエストボディ |
enabled | Boolean | ✓ | true | 有効/無効フラグ |
lastRunAt | DateTime? | - | null | 最終実行日時 |
nextRunAt | DateTime? | - | null | 次回実行予定日時 |
count | Int | ✓ | 0 | 実行回数 |
createdAt | DateTime | ✓ | now() | 作成日時 |
updatedAt | DateTime | ✓ | now() | 更新日時 |
リレーション
user- ユーザー(多対1、カスケード削除)runningLogs- 実行ログ(1対多)WebhookJobs- Webhookジョブ(1対1、オプション)
インデックス
userId- パフォーマンス向上(enabled, nextRunAt)- 実行対象ジョブの検索用
使用例
// ジョブ作成
const job = await prisma.job.create({
data: {
userId: "user_123",
name: "Daily Report",
schedule: "0 9 * * *",
timezone: "Asia/Tokyo",
url: "https://api.example.com/report",
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ report: "daily" }),
},
});
RunningLog(実行ログ)
ジョブの実行結果を記録します。
フィールド
| フィールド | 型 | 必須 | デフォルト | 説明 |
|---|---|---|---|---|
id | String | ✓ | cuid() | ログID(主キー) |
jobId | String? | - | null | ジョブID(外部キー、オプション) |
userId | String | ✓ | - | ユーザーID(外部キー) |
type | Type | ✓ | AUTO | 実行タイプ |
startedAt | DateTime | ✓ | - | 実行開始時刻 |
finishedAt | DateTime | ✓ | now() | 実行完了時刻 |
url | String | ✓ | - | リクエストURL |
status | Int | ✓ | - | HTTPステータスコード |
responseBody | String? | - | null | レスポンスボディ |
responseHeaders | Json? | - | null | レスポンスヘッダー |
durationMs | Int | ✓ | - | 実行時間(ミリ秒) |
successful | Boolean | ✓ | true | 成功/失敗フラグ |
createdAt | DateTime | ✓ | now() | 作成日時 |
リレーション
job- ジョブ(多対1、NULL設定)user- ユーザー(多対1、カスケード削除)
インデックス
jobId- ジョブごとのログ検索用createdAt- 日時順のログ取得用
注意事項
jobIdは削除されたジョブの場合NULLになります- ユーザーが削除されるとログも削除されます
WebhookJobs(Webhookジョブ)
外部サービスからのHTTPリクエストを受け付けるジョブです。
フィールド
| フィールド | 型 | 必須 | デフォルト | 説明 |
|---|---|---|---|---|
id | String | ✓ | cuid() | WebhookジョブID(主キー) |
userId | String | ✓ | - | ユーザーID(外部キー) |
jobId | String | ✓ | - | ジョブID(外部キー、一意) |
endpoint | String | ✓ | - | Webhookエンドポイント |
headers | Json? | - | null | カスタムヘッダー |
createdAt | DateTime | ✓ | now() | 作成日時 |
updatedAt | DateTime | ✓ | now() | 更新日時 |
リレーション
user- ユーザー(多対1、カスケード削除)job- ジョブ(1対1、カスケード削除)
インデックス
userId- パフォーマンス向上jobId- 一意制約
APIKey(APIキー)
プログラマティックアクセス用の認証キーを管理します。
フィールド
| フィールド | 型 | 必須 | デフォルト | 説明 |
|---|---|---|---|---|
id | String | ✓ | cuid() | APIキーID(主キー) |
userId | String | ✓ | - | ユーザーID(外部キー) |
name | String | ✓ | - | APIキー名(識別用) |
keyHash | String | ✓ | - | トークンのハッシュ値(一意) |
lastUsed | DateTime? | - | null | 最終使用日時 |
expiresAt | DateTime? | - | null | 有効期限 |
count | Int | ✓ | 0 | 使用回数 |
scopes | String[] | ✓ | [] | アクセススコープ |
enabled | Boolean | ✓ | true | 有効/無効フラグ |
createdAt | DateTime | ✓ | now() | 作成日時 |
updatedAt | DateTime | ✓ | now() | 更新日時 |
リレーション
user- ユーザー(多対1、カスケード削除)logs- APIログ(1対多)
インデックス
userId- パフォーマンス向上keyHash- トークン検証用(一意制約)
スコープ
APIキーには以下のスコープを設定できます:
| スコープ | 説明 |
|---|---|
read:jobs | ジョブの読み取り |
write:jobs | ジョブの作成・更新・削除 |
read:keys | APIキーの読み取り |
write:keys | APIキーの作成・削除 |
セキュリティ
- トークンはハッシュ化されて保存されます
- 実際のトークンは作成時のみ表示されます
- 有効期限を設定できます
APILog(APIログ)
APIキーを使用したリクエストの履歴を記録します。
フィールド
| フィールド | 型 | 必須 | デフォルト | 説明 |
|---|---|---|---|---|
id | String | ✓ | cuid() | ログID(主キー) |
apiKeyId | String | ✓ | - | APIキーID(外部キー) |
url | String | ✓ | - | リクエストURL |
method | Method | ✓ | - | HTTPメソッド |
requestHeaders | Json? | - | null | リクエストヘッダー |
requestBody | String? | - | null | リクエストボディ |
createdAt | DateTime | ✓ | now() | 作成日時 |
リレーション
apiKey- APIキー(多対1、カスケード削除)
インデックス
apiKeyId- APIキーごとのログ検索用
使用量追跡モデル
MonthlyUsage(月次使用量)
ユーザーの月ごとの使用量と課金情報を追跡します。
フィールド
| フィールド | 型 | 必須 | デフォルト | 説明 |
|---|---|---|---|---|
id | String | ✓ | cuid() | 使用量ID(主キー) |
userId | String | ✓ | - | ユーザーID(外部キー) |
year | Int | ✓ | - | 年 |
month | Int | ✓ | - | 月(1-12) |
totalExecutions | Int | ✓ | 0 | 総ジョブ実行回数 |
totalApiCalls | Int | ✓ | 0 | 総API呼び出し回数 |
billedAmount | Decimal | ✓ | 0 | 課金額 |
paid | Boolean | ✓ | false | 支払い済みフラグ |
createdAt | DateTime | ✓ | now() | 作成日時 |
updatedAt | DateTime | ✓ | now() | 更新日時 |
リレーション
user- ユーザー(多対1、カスケード削除)
インデックス
(userId, year, month)- 一意制約userId- パフォーマンス向上(year, month)- 期間検索用
特徴
- 削除されたリソースの使用量も含む
- 悪用防止(頻繁な削除・再作成)に使用
- 課金計算の基礎データ
DailyUsage(日次使用量)
より細かい粒度での使用量を追跡します。
フィールド
| フィールド | 型 | 必須 | デフォルト | 説明 |
|---|---|---|---|---|
id | String | ✓ | cuid() | 使用量ID(主キー) |
userId | String | ✓ | - | ユーザーID(外部キー) |
date | Date | ✓ | - | 日付 |
executions | Int | ✓ | 0 | 実行回数 |
apiCalls | Int | ✓ | 0 | API呼び出し回数 |
peakJobCount | Int | ✓ | 0 | 最大ジョブ数 |
peakApiKeyCount | Int | ✓ | 0 | 最大APIキー数 |
createdAt | DateTime | ✓ | now() | 作成日時 |
updatedAt | DateTime | ✓ | now() | 更新日時 |
リレーション
user- ユーザー(多対1、カスケード削除)
インデックス
(userId, date)- 一意制約userId- パフォーマンス向上date- 日付検索用
用途
- 日次レポート生成
- ピーク時の把握
- より詳細な使用パターン分析
ResourceHistory(リソース履歴)
ジョブやAPIキーの作成・削除を追跡します。
フィールド
| フィールド | 型 | 必須 | デフォルト | 説明 |
|---|---|---|---|---|
id | String | ✓ | cuid() | 履歴ID(主キー) |
userId | String | ✓ | - | ユーザーID(外部キー) |
resourceType | String | ✓ | - | リソースタイプ("JOB" or "API_KEY") |
resourceId | String | ✓ | - | リソースID |
action | String | ✓ | - | アクション("CREATED" or "DELETED") |
createdAt | DateTime | ✓ | now() | 作成日時 |
リレーション
user- ユーザー(多対1、カスケード削除)
インデックス
(userId, resourceType)- パフォーマンス向上createdAt- 時系列検索用
悪用防止
このテーブルは以下の悪用を防ぐために使用されます:
// 例: 今日の作成・削除回数をチェック
const todayHistory = await prisma.resourceHistory.findMany({
where: {
userId: "user_123",
resourceType: "JOB",
createdAt: {
gte: new Date(new Date().setHours(0, 0, 0, 0)),
},
},
});
const createdCount = todayHistory.filter(h => h.action === "CREATED").length;
const deletedCount = todayHistory.filter(h => h.action === "DELETED").length;
// 今日5回以上作成削除している場合は制限
if (createdCount + deletedCount >= 10) {
throw new Error("今日の作成・削除回数の上限に達しました");
}
リレーションシップ図
User
├── Session (1:多)
├── Account (1:多)
├── Job (1:多)
│ ├── RunningLog (1:多)
│ └── WebhookJobs (1:1)
├── APIKey (1:多)
│ └── APILog (1:多)
├── MonthlyUsage (1:多)
├── DailyUsage (1:多)
└── ResourceHistory (1:多)
データベース操作のベストプラクティス
1. トランザクション
複数のテーブルを更新する場合はトランザクションを使用:
await prisma.$transaction([
prisma.job.create({ /* ... */ }),
prisma.resourceHistory.create({
data: {
userId: "user_123",
resourceType: "JOB",
resourceId: jobId,
action: "CREATED",
},
}),
prisma.dailyUsage.update({ /* ... */ }),
]);
2. インデックスの活用
頻繁に検索されるフィールドにはインデックスが設定されています:
// ✅ インデックスを活用(高速)
await prisma.job.findMany({
where: {
userId: "user_123",
enabled: true,
},
});
// ❌ インデックスを活用していない(低速)
await prisma.job.findMany({
where: {
name: { contains: "report" },
},
});
3. カスケード削除
ユーザーを削除すると関連データも自動削除されます:
// ユーザー削除時に自動削除されるもの:
// - Session
// - Account
// - Job(→ RunningLog, WebhookJobsも削除)
// - APIKey(→ APILogも削除)
// - MonthlyUsage
// - DailyUsage
// - ResourceHistory
await prisma.user.delete({
where: { id: "user_123" },
});
マイグレーション
マイグレーション作成
npx prisma migrate dev --name add_new_feature
マイグレーション適用
# 開発環境
npx prisma migrate dev
# 本番環境
npx prisma migrate deploy
Prisma Clientの再生成
スキーマ変更後は必ず実行:
npx prisma generate