components-guide

安装量: 546
排名: #2001

安装

npx skills add https://github.com/get-convex/agent-skills --skill components-guide

Convex Components Guide Use components to encapsulate features and build maintainable, reusable backends. What Are Convex Components? Components are self-contained mini-backends that bundle: Their own database schema Their own functions (queries, mutations, actions) Their own data (isolated tables) Clear API boundaries Think of them as: npm packages for your backend, or microservices without the deployment complexity. Why Use Components? Traditional Approach (Monolithic) convex/ users.ts (500 lines) files.ts (600 lines - upload, storage, permissions, rate limiting) payments.ts (400 lines - Stripe, webhooks, billing) notifications.ts (300 lines) analytics.ts (200 lines) Total: One big codebase, everything mixed together Component Approach (Encapsulated) convex/ components/ storage/ (File uploads - reusable) billing/ (Payments - reusable) notifications/ (Alerts - reusable) analytics/ (Tracking - reusable) convex.config.ts (Wire components together) domain/ (Your actual business logic) users.ts (50 lines - uses components) projects.ts (75 lines - uses components) Total: Clean, focused, reusable Quick Start 1. Install a Component

Official components from npm

npm install @convex-dev/ratelimiter 2. Configure in convex.config.ts import { defineApp } from "convex/server" ; import ratelimiter from "@convex-dev/ratelimiter/convex.config" ; export default defineApp ( { components : { ratelimiter , } , } ) ; 3. Use in Your Code import { components } from "./_generated/api" ; export const createPost = mutation ( { handler : async ( ctx , args ) => { // Use the component await components . ratelimiter . check ( ctx , { key : user: ${ ctx . user . _id } , limit : 10 , period : 60000 , // 10 requests per minute } ) ; return await ctx . db . insert ( "posts" , args ) ; } , } ) ; Sibling Components Pattern Multiple components working together at the same level: // convex.config.ts export default defineApp ( { components : { // Sibling components - each handles one concern auth : authComponent , storage : storageComponent , payments : paymentsComponent , emails : emailComponent , analytics : analyticsComponent , } , } ) ; Example: Complete Feature Using Siblings // convex/subscriptions.ts import { components } from "./_generated/api" ; export const subscribe = mutation ( { args : { plan : v . string ( ) } , handler : async ( ctx , args ) => { // 1. Verify authentication (auth component) const user = await components . auth . getCurrentUser ( ctx ) ; // 2. Create payment (payments component) const subscription = await components . payments . createSubscription ( ctx , { userId : user . _id , plan : args . plan , amount : getPlanAmount ( args . plan ) , } ) ; // 3. Track conversion (analytics component) await components . analytics . track ( ctx , { event : "subscription_created" , userId : user . _id , plan : args . plan , } ) ; // 4. Send confirmation (emails component) await components . emails . send ( ctx , { to : user . email , template : "subscription_welcome" , data : { plan : args . plan } , } ) ; // 5. Store subscription in main app await ctx . db . insert ( "subscriptions" , { userId : user . _id , paymentId : subscription . id , plan : args . plan , status : "active" , } ) ; return subscription ; } , } ) ; Official Components Browse Component Directory : Authentication @convex-dev/better-auth - Better Auth integration Storage @convex-dev/r2 - Cloudflare R2 file storage @convex-dev/storage - File upload/download Payments @convex-dev/polar - Polar billing & subscriptions AI @convex-dev/agent - AI agent workflows @convex-dev/embeddings - Vector storage & search Backend Utilities @convex-dev/ratelimiter - Rate limiting @convex-dev/aggregate - Data aggregations @convex-dev/action-cache - Cache action results @convex-dev/sharded-counter - Distributed counters @convex-dev/migrations - Schema migrations @convex-dev/workflow - Workflow orchestration Creating Your Own Component When to Create a Component Good reasons: Feature is self-contained You'll reuse it across projects Want to share with team/community Complex feature with its own data model Third-party integration wrapper Not good reasons: One-off business logic Tightly coupled to main app Simple utility functions Structure mkdir -p convex/components/notifications // convex/components/notifications/convex.config.ts import { defineComponent } from "convex/server" ; export default defineComponent ( "notifications" ) ; // convex/components/notifications/schema.ts import { defineSchema , defineTable } from "convex/server" ; import { v } from "convex/values" ; export default defineSchema ( { notifications : defineTable ( { userId : v . id ( "users" ) , message : v . string ( ) , read : v . boolean ( ) , createdAt : v . number ( ) , } ) . index ( "by_user" , [ "userId" ] ) . index ( "by_user_and_read" , [ "userId" , "read" ] ) , } ) ; // convex/components/notifications/send.ts import { mutation } from "./_generated/server" ; import { v } from "convex/values" ; export const send = mutation ( { args : { userId : v . id ( "users" ) , message : v . string ( ) , } , handler : async ( ctx , args ) => { await ctx . db . insert ( "notifications" , { userId : args . userId , message : args . message , read : false , createdAt : Date . now ( ) , } ) ; } , } ) ; export const markRead = mutation ( { args : { notificationId : v . id ( "notifications" ) } , handler : async ( ctx , args ) => { await ctx . db . patch ( args . notificationId , { read : true } ) ; } , } ) ; // convex/components/notifications/read.ts import { query } from "./_generated/server" ; import { v } from "convex/values" ; export const list = query ( { args : { userId : v . id ( "users" ) } , handler : async ( ctx , args ) => { return await ctx . db . query ( "notifications" ) . withIndex ( "by_user" , q => q . eq ( "userId" , args . userId ) ) . order ( "desc" ) . collect ( ) ; } , } ) ; export const unreadCount = query ( { args : { userId : v . id ( "users" ) } , handler : async ( ctx , args ) => { const unread = await ctx . db . query ( "notifications" ) . withIndex ( "by_user_and_read" , q => q . eq ( "userId" , args . userId ) . eq ( "read" , false ) ) . collect ( ) ; return unread . length ; } , } ) ; Component Communication Patterns Parent to Component (Good) // Main app calls component await components . storage . upload ( ctx , file ) ; await components . analytics . track ( ctx , event ) ; Parent to Multiple Siblings (Good) // Main app orchestrates multiple components await components . auth . verify ( ctx ) ; const file = await components . storage . upload ( ctx , data ) ; await components . notifications . send ( ctx , message ) ; Component Receives Parent Data (Good) // Pass IDs from parent's tables to component await components . audit . log ( ctx , { userId : user . _id , // From parent's users table action : "delete" , resourceId : task . _id , // From parent's tasks table } ) ; // Component stores these as strings/IDs // but doesn't access parent tables directly Component to Parent Tables (Bad) // Inside component code - DON'T DO THIS const user = await ctx . db . get ( userId ) ; // Error! Can't access parent tables Sibling to Sibling (Bad) Components can't call each other directly. If you need this, they should be in the main app or refactor the design. Best Practices 1. Single Responsibility Each component does ONE thing well: Storage component handles files Auth component handles authentication Don't create "utils" component with everything 2. Clear API Surface // Export only what's needed export { upload , download , delete } from "./storage" ; // Keep internals private // (Don't export helper functions) 3. Minimal Coupling // Good: Pass data as arguments await components . audit . log ( ctx , { userId : user . _id , action : "delete" } ) ; // Bad: Component accesses parent tables // (Not even possible, but shows the principle) 4. Version Your Components { "name" : "@yourteam/notifications-component" , "version" : "1.0.0" } 5. Document Your Components Include README with: What the component does How to install How to use API reference Examples Checklist Browse Component Directory for existing solutions Install components via npm: npm install @convex-dev/component-name Configure in convex.config.ts Use sibling components for feature encapsulation Create your own components for reusable features Keep components focused (single responsibility) Test components in isolation Document component APIs Version your components properly

返回排行榜