원클릭으로
convex-conventions
This repo's own conventions for writing Convex functions in
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
This repo's own conventions for writing Convex functions in
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Turn a product idea into a structured build plan and parallel execution strategy for this MF2 scaffold. Use when the user describes a product, app, or startup idea to build on this scaffold, asks where to start, how to plan or parallelize a build, or what the scaffold already provides, even if they do not mention mf2 by name. Probes once for the requirements that change the plan (B2B vs B2C, monetization, realtime collaboration, AI features, target surfaces, content and marketing, notifications), maps every feature to the specific capability the template already ships so nothing gets rebuilt, writes BUILD-PLAN.md at the project root, and splits the work into parallel workers with dependency edges and quality gates.
Add Arcjet security protection to any code path — HTTP route handlers, API endpoints, AI agent tool calls, MCP servers, background jobs, and queue workers. Covers rate limiting, bot detection, email validation, prompt injection detection, sensitive information blocking, and abuse prevention. Works across Next.js, Express, Fastify, SvelteKit, Remix, Bun, Deno, NestJS, FastAPI, Flask, and non-HTTP contexts. Use this skill when the user wants to add security, rate limiting, bot protection, or abuse prevention to any part of their application — whether they say "protect my API," "rate limit tool calls," "block bots," "secure my endpoint," "add security to my MCP server," or "prevent abuse" without mentioning Arcjet specifically.
Deploy Expo apps to production with EAS — build and submit to the iOS App Store, Google Play Store, and TestFlight, configure eas.json build and submit profiles, manage app versions and build numbers, publish App Store metadata and ASO, and deploy web bundles and API routes via EAS Hosting. Use whenever the user is preparing a production build, running eas build or eas submit, shipping to TestFlight, releasing or rolling out to the app stores, bumping version or build numbers, or setting up store listing metadata for an Expo app.
Build Expo app for development
Set up Tailwind CSS v4 in Expo with react-native-css and NativeWind v5 for universal styling
Use when implementing or debugging ANY network request, API call, or data fetching. Covers fetch API, React Query, SWR, error handling, caching, offline support, and Expo Router data loaders (`useLoaderData`).
| name | convex-conventions |
| description | This repo's own conventions for writing Convex functions in |
Backend code lives in packages/backend/convex/. Schema at convex/schema.ts
(composed from per-module tables.ts files), functions at
convex/<module>/. These conventions are offline: everything here is
verifiable against the source in this repo.
A folder module's index.ts adds an .index segment to every function
reference: convex/surveys/index.ts gives api.surveys.index.create, NOT
api.surveys.create (this repo's convex/email/index.ts is referenced as
internal.email.index.send). Name files after their role (queries.ts,
mutations.ts) or expect the .index segment.
An action that calls back into its own deployment via ctx.runQuery,
ctx.runMutation, or ctx.runAction on api.* or internal.* creates a
type cycle: the generated api type needs the action's return type, which
needs the runQuery result type, which needs api. TypeScript gives up and
infers any for the WHOLE api object. Every consumer's useQuery(...)
then returns any, silently disabling type safety across the repo. The
errors surface far from the cause (TS7022/TS7006 in unrelated frontend
files).
Break the cycle at both ends, always:
export const summarize = action({
args: { threadId: v.id("threads") },
returns: v.object({ summary: v.string() }),
// 1. Explicit return type on the handler
handler: async (ctx, args): Promise<{ summary: string }> => {
// 2. Explicit type on every runQuery/runMutation/runAction result
const messages: Array<{ role: string; content: string }> =
await ctx.runQuery(internal.chat.api.messages.getHistory, {
threadId: args.threadId,
});
// ...
},
});
returns: validators do not break the cycle; only TypeScript annotations
do. Calls to components.* (Stripe, Resend, ...) are safe: component types
do not reference this app's api. See convex/chat/api/title.ts and
convex/auth/users.ts for the shipped pattern.
export const setTitle = mutation({
args: { threadId: v.id("threads"), title: v.string() },
returns: v.null(),
handler: async (ctx, args) => {
// ...
return null;
},
});
Handlers that return nothing declare returns: v.null() and explicitly
return null.
query / mutation / action: exposed to clients, must validate auth.internalQuery / internalMutation / internalAction: callable only
from other Convex functions (crons, schedulers, webhooks, other actions).Anything invoked by ctx.scheduler, ctx.runQuery-from-actions, crons, or
HTTP handlers should be internal unless a client genuinely calls it.
mustGetCurrentUser(ctx): returns Doc<"users"> or throws. Use at the
top of any authenticated query/mutation.getCurrentUser(ctx): same but returns null instead of throwing.getOrgContext(ctx): returns { user, orgId } where orgId comes from
the Clerk JWT org_id claim. Use for org-scoped data.These helpers take a QueryCtx and therefore work in queries and mutations
only, which leads to:
ActionCtx has no db. Passing an action's ctx to a QueryCtx helper
(mustGetCurrentUser, getOrgContext, anything that touches ctx.db) is
both a type error and a latent runtime crash. Inside an action, either:
convex/stripe/index.ts scopes by identity.subject):const identity = await ctx.auth.getUserIdentity();
if (!identity) {
throw new Error("Not authenticated");
}
const orgId =
typeof identity.org_id === "string" ? identity.org_id : undefined;
ctx.runQuery an internal query that does the db work (then apply the
circular-inference rule above).This repo's Convex version takes the table name as the first argument:
await ctx.db.get("users", id);
await ctx.db.patch("users", id, { clerkUser });
await ctx.db.delete("messages", messageId);
await ctx.db.insert("messages", { threadId, role, content });
Reads use indexes, not filters:
ctx.db
.query("messages")
.withIndex("by_thread", (q) => q.eq("threadId", args.threadId))
.order("asc")
.collect();
Define indexes on the table (convex/<module>/tables.ts) for every access
pattern you query by.
With no Convex URL configured, ConvexClientProvider renders children
without a Convex context and every useQuery/useMutation/useAction
throws at runtime. Wrap Convex-consuming subtrees in ConvexGate from
@repo/convex/provider (native: provider.native), or branch on
useConvexConfigured().
convex/_generated/ is committed, so typecheck and tests work with no
deployment at all. To run the backend or regenerate types after schema
changes, run plain bunx convex dev in packages/backend:
CONVEX_AGENT_MODE=anonymous to
force this path regardless of login state.npx convex deployment create local or
npx convex deployment select local; those require a logged-in account
and fail with "Cannot create a deployment in anonymous mode".bunx convex codegen only works once a deployment exists; bunx convex dev --once both creates one and regenerates _generated/. After that
one-time setup, npx convex codegen auto-starts the local backend itself._generated/api.d.ts
instead: one import line plus one fullApi entry per new module.Commit _generated/ changes together with the schema change that caused
them.
Use convex-test (in-memory, no running backend needed). Bootstrap with
the shipped module glob, seed with t.run, and act as a user with
t.withIdentity:
import { convexTest } from "convex-test";
import schema from "../schema";
import { modules } from "../test.setup";
const t = convexTest(schema, modules);
const asUser = t.withIdentity({ subject, org_id, org_role: "org:member" });
Test files go in packages/backend/tests/ or next to the module under
test; bun run test --filter=@repo/backend runs them.
With network access, npx convex ai-files install installs Convex's
managed AI guidelines (docs.convex.dev/ai),
a useful complement to this file. This skill does not depend on it.