一键导入
source-command-convex-quickstart
Scaffold a new feature module in this Convex project - creates schema, functions, and frontend integration
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Scaffold a new feature module in this Convex project - creates schema, functions, and frontend integration
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Set up Convex authentication with proper user management, identity mapping, and access control patterns. Use when implementing auth flows.
Guide to using Convex components for feature encapsulation. Learn about sibling components, creating your own, and when to use components vs monolithic code.
Discover and use convex-helpers utilities for relationships, filtering, sessions, custom functions, and more. Use when you need pre-built Convex patterns.
Create Convex queries, mutations, and actions with proper validation, authentication, and error handling. Use when implementing new API endpoints.
Plan and execute Convex schema migrations safely, including adding fields, creating tables, and data transformations. Use when schema changes affect existing data.
Design and generate Convex database schemas with proper validation, indexes, and relationships. Use when creating schema.ts or modifying table definitions.
| name | source-command-convex-quickstart |
| description | Scaffold a new feature module in this Convex project - creates schema, functions, and frontend integration |
Use this skill when the user asks to run the migrated source command convex-quickstart.
This project already has Convex set up. Use this guide to add a new feature module with proper schema, functions, and frontend integration.
Add your table(s) to convex/schema.ts:
// convex/schema.ts
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";
export default defineSchema({
// ... existing tables
// Add your new table
yourFeature: defineTable({
userId: v.id("users"), // Foreign key
title: v.string(),
status: v.union(
v.literal("draft"),
v.literal("active"),
v.literal("archived"),
),
metadata: v.optional(
v.object({
tags: v.array(v.string()),
}),
),
createdAt: v.number(),
})
.index("by_userId", ["userId"])
.index("by_status", ["status"])
.index("by_userId_and_status", ["userId", "status"]),
});
Index naming: Include all fields (e.g., by_userId_and_status).
Create convex/yourFeature.ts:
import { v } from "convex/values";
import { query, mutation } from "./_generated/server";
import { authComponent } from "./auth";
// List items for authenticated user
export const list = query({
args: {},
returns: v.array(
v.object({
_id: v.id("yourFeature"),
title: v.string(),
status: v.union(
v.literal("draft"),
v.literal("active"),
v.literal("archived"),
),
createdAt: v.number(),
}),
),
handler: async (ctx) => {
const user = await authComponent.getAuthUser(ctx);
if (!user) throw new Error("Not authenticated");
return await ctx.db
.query("yourFeature")
.withIndex("by_userId", (q) => q.eq("userId", user._id))
.collect();
},
});
// Create a new item
export const create = mutation({
args: {
title: v.string(),
},
returns: v.id("yourFeature"),
handler: async (ctx, args) => {
const user = await authComponent.getAuthUser(ctx);
if (!user) throw new Error("Not authenticated");
return await ctx.db.insert("yourFeature", {
userId: user._id,
title: args.title,
status: "draft",
createdAt: Date.now(),
});
},
});
// Update an item (with ownership check)
export const update = mutation({
args: {
id: v.id("yourFeature"),
title: v.optional(v.string()),
status: v.optional(
v.union(v.literal("draft"), v.literal("active"), v.literal("archived")),
),
},
returns: v.null(),
handler: async (ctx, args) => {
const user = await authComponent.getAuthUser(ctx);
if (!user) throw new Error("Not authenticated");
const item = await ctx.db.get(args.id);
if (!item) throw new Error("Not found");
if (item.userId !== user._id) throw new Error("Unauthorized");
const updates: Record<string, unknown> = {};
if (args.title !== undefined) updates.title = args.title;
if (args.status !== undefined) updates.status = args.status;
await ctx.db.patch(args.id, updates);
return null;
},
});
// Delete an item (with ownership check)
export const remove = mutation({
args: { id: v.id("yourFeature") },
returns: v.null(),
handler: async (ctx, args) => {
const user = await authComponent.getAuthUser(ctx);
if (!user) throw new Error("Not authenticated");
const item = await ctx.db.get(args.id);
if (!item) throw new Error("Not found");
if (item.userId !== user._id) throw new Error("Unauthorized");
await ctx.db.delete(args.id);
return null;
},
});
"use client";
import { useQuery, useMutation } from "convex/react";
import { api } from "@/convex/_generated/api";
export function YourFeatureList() {
const items = useQuery(api.yourFeature.list);
const createItem = useMutation(api.yourFeature.create);
const deleteItem = useMutation(api.yourFeature.remove);
if (items === undefined) return <div>Loading...</div>;
return (
<div>
<button onClick={() => createItem({ title: "New Item" })}>
Add Item
</button>
{items.map((item) => (
<div key={item._id}>
<span>{item.title}</span>
<button onClick={() => deleteItem({ id: item._id })}>
Delete
</button>
</div>
))}
</div>
);
}
Create convex/yourFeature.test.ts:
import { convexTest } from "convex-test";
import { describe, it, expect } from "vitest";
import { api } from "./_generated/api";
import schema from "./schema";
import { modules } from "./test.setup";
describe("yourFeature", () => {
it("should create and list items", async () => {
const t = convexTest(schema, modules);
const asUser = t.withIdentity({ subject: "user1", name: "Test User" });
const id = await asUser.mutation(api.yourFeature.create, {
title: "Test Item",
});
const items = await asUser.query(api.yourFeature.list, {});
expect(items).toHaveLength(1);
expect(items[0].title).toBe("Test Item");
});
});
npx convex codegen # Generate types for new schema
pnpm run test:once # Run tests
convex/schema.ts with proper indexesargs and returns validatorsauthComponent.getAuthUser(ctx)internal.* for any scheduled functionsconvex/yourFeature.test.tsnpx convex codegen after schema changes