mcpField
C
convex

convex

github.com/mindrally開発288662 回インストール3 週間前に更新
リポジトリ

convex について

Convex と TypeScript でバックエンドを構築する開発者向けのコーディングガイドです。クエリ、ミューテーション、アクション、リアルタイム同期に加え、インデックス、HTTP ルート、定期ジョブ、ファイルアップロードを扱います。

主な機能

  1. 01クエリ、ミューテーション、アクションの作成
  2. 02インデックスと引数検証の利用
  3. 03HTTP ルートと定期ジョブの設定
  4. 04ファイルアップロードの処理
  5. 05リアルタイムサブスクリプションの利用

活用シーン

  1. 01Convex のデータ検索と書き込みの実装
  2. 02外部 API 呼び出しと副作用の構築
  3. 03Webhook と定期ジョブの設定
  4. 04クライアント向けファイルアップロードとリアルタイム更新

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/mindrally/skills --skill convex

インストール後、Claude Code で/convexを入力して読み込まれたか確認します。

詳細

Convex 開発ガイドライン

Convex バックエンド開発、TypeScript、リアルタイムデータ同期のパターンに精通しています。

一般的な開発仕様

コードスタイルと構成
  • 関数宣言、イテレーター、モジュールを使用して簡潔な TypeScript を記述します
  • 補助動詞を使った説明的な変数名を使用します(例:isLoading、hasError)
  • エクスポートするコンポーネント、サブコンポーネント、ヘルパー、静的型でコードを構成します
  • 名前付きエクスポートを使用するディレクトリには、ダッシュケースを使用します
  • type より interface を優先し、enum は避けてユニオン型を使用します
  • 宣言的な JSX パターンを用いた関数コンポーネントを使用します
エラー処理
  • ガード節を使って関数内で早期にエラーを処理します
  • デバッグに適した方法でエラーをログに記録します
  • ユーザーに分かりやすいエラーメッセージを提供します
  • フォームのバリデーションには Zod を使用します
  • React コンポーネントに適切なエラーバウンダリーを実装します
UI フレームワークの統合
  • コンポーネントのプリミティブには Shadcn UI と Radix UI を使用します
  • レスポンシブでモバイルファーストなデザインにし、Tailwind CSS でスタイリングします
  • useClient、useEffect、useState の使用を最小限に抑えます
  • 該当する場合は React Server Components を活用します
  • コード分割には Suspense をローディング状態に使用し、動的ローディングを活用します

Convex 固有のパターン

クエリ

query コンストラクターを使用してクエリを構成します。

import { query } from "./_generated/server";
import { v } from "convex/values";

export const getItems = query({
  args: {
    status: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    // ctx provides: db, storage, auth
    const identity = await ctx.auth.getUserIdentity();

    if (args.status) {
      return await ctx.db
        .query("items")
        .withIndex("by_status", (q) => q.eq("status", args.status))
        .collect();
    }

    return await ctx.db.query("items").collect();
  },
});

重要:パフォーマンス向上のため、フィルターより Convex のインデックスを優先してください。schema.ts で .index() メソッドを使用してインデックスを定義し、.withIndex() でクエリを実行します。

ミューテーション

データベースへの書き込み用にミューテーションを構成します。

import { mutation } from "./_generated/server";
import { v } from "convex/values";

export const createItem = mutation({
  args: {
    title: v.string(),
    description: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    const identity = await ctx.auth.getUserIdentity();
    if (!identity) {
      throw new Error("Not authenticated");
    }

    return await ctx.db.insert("items", {
      title: args.title,
      description: args.description,
      userId: identity.subject,
      createdAt: Date.now(),
    });
  },
});
アクション

外部 API の呼び出しや副作用にはアクションを使用します。

import { action } from "./_generated/server";
import { v } from "convex/values";

export const sendEmail = action({
  args: {
    to: v.string(),
    subject: v.string(),
    body: v.string(),
  },
  handler: async (ctx, args) => {
    // Actions can call external APIs
    const response = await fetch("https://api.email-service.com/send", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(args),
    });

    return response.ok;
  },
});
インデックスを使用したスキーマ定義
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";

export default defineSchema({
  items: defineTable({
    title: v.string(),
    description: v.optional(v.string()),
    status: v.string(),
    userId: v.string(),
    createdAt: v.number(),
  })
    .index("by_status", ["status"])
    .index("by_user", ["userId"])
    .index("by_user_and_status", ["userId", "status"]),
});
HTTP ルーター

Webhook と外部連携のための HTTP ルートを定義します。

import { httpRouter } from "convex/server";
import { httpAction } from "./_generated/server";

const http = httpRouter();

http.route({
  path: "/webhook",
  method: "POST",
  handler: httpAction(async (ctx, request) => {
    const body = await request.json();
    // Process webhook
    return new Response(JSON.stringify({ success: true }), {
      status: 200,
      headers: { "Content-Type": "application/json" },
    });
  }),
});

export default http;
スケジュール済みジョブ

定期的なタスクには cron ジョブを実装します。

import { cronJobs } from "convex/server";
import { internal } from "./_generated/api";

const crons = cronJobs();

// Run every hour
crons.interval(
  "cleanup-old-items",
  { hours: 1 },
  internal.tasks.cleanupOldItems
);

// Run at specific time (daily at midnight UTC)
crons.monthly(
  "monthly-report",
  { day: 1, hourUTC: 0, minuteUTC: 0 },
  internal.reports.generateMonthlyReport
);

export default crons;
ファイル処理

ファイルアップロードの 3 段階のプロセス:

// 1. Generate upload URL (mutation)
export const generateUploadUrl = mutation(async (ctx) => {
  return await ctx.storage.generateUploadUrl();
});

// 2. Client POSTs file to the URL
// const uploadUrl = await generateUploadUrl();
// const response = await fetch(uploadUrl, { method: "POST", body: file });
// const { storageId } = await response.json();

// 3. Save storage ID to database (mutation)
export const saveFile = mutation({
  args: {
    storageId: v.id("_storage"),
    filename: v.string(),
  },
  handler: async (ctx, args) => {
    return await ctx.db.insert("files", {
      storageId: args.storageId,
      filename: args.filename,
    });
  },
});

ベストプラクティス

  1. データをフィルタリングまたはソートするクエリでは、必ずインデックスを使用します
  2. Convex バリデーター(v.string()、v.number() など)を使用して、引数を検証します
  3. 認証が必要なハンドラーでは、早い段階で認証を確認します
  4. クライアントに公開すべきでない操作には、内部関数を使用します
  5. リアルタイムサブスクリプションを活用します。Convex クエリはデータが変更されると自動的に更新されます
  6. ミューテーションは小さく保ち、単一の操作に集中させます
  7. 副作用にはアクションを使用します。クエリやミューテーションから外部 API を呼び出してはいけません
  8. エラーを適切に処理し、ユーザー向けの適切なエラーメッセージを表示します

パフォーマンスに関する考慮事項

  • 可能な限り .filter() の代わりに .withIndex() を使用します
  • 大きな結果セットは .paginate() を使用してページネーションします
  • 結果が 1 件のみの想定では、.collect() の代わりに .first() を使用します
  • 頻繁にアクセスされるデータについては、非正規化を検討します
  • Convex 組み込みのキャッシュを使用し、独自のキャッシュ実装は避けます

コメント