Skip to main content

convex-functions

Writing queries, mutations, actions, and HTTP actions with proper argument validation, error handling, internal functions, and runtime considerations

설치로 이동

소스 정보

저장소
waynesutton/convexskills
최근 소스 활동
2026년 2월 6일 01:35
감지된 SKILL.md 언어
영어
스타
404
포크
32

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

파일 탐색기
4 개 파일

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
convex-functions
displayName
Convex Functions
description
Writing queries, mutations, actions, and HTTP actions with proper argument validation, error handling, internal functions, and runtime considerations
version
1.0.0
author
Convex
tags
["convex","functions","queries","mutations","actions","http"]
# Convex Functions Master Convex functions including queries, mutations, actions, and HTTP endpoints with proper validation, error handling, and runtime considerations. ## Code Quality All examples in this skill comply with @convex-dev/eslint-plugin rules: - Object syntax with `handler` property - Argument validators on all functions - Explicit table names in database operations See the Code Quality section in [convex-best-practices](../convex-best-practices/SKILL.md) for linting setup. ## Documentation Sources Before implementing, do not assume; fetch the latest documentation: - Primary: https://docs.convex.dev/functions - Query Functions: https://docs.convex.dev/functions/query-functions - Mutation Functions: https://docs.convex.dev/functions/mutation-functions - Actions: https://docs.convex.dev/functions/actions - HTTP Actions: https://docs.convex.dev/functions/http-actions - For broader context: https://docs.convex.dev/llms.txt ## Instructions ### Function Types Overview | Type | Database Access | External APIs | Caching | Use Case | | ----------- | ------------------------ | ------------- | ------------- | --------------------- | | Query | Read-only | No | Yes, reactive | Fetching data | | Mutation | Read/Write | No | No | Modifying data | | Action | Via runQuery/runMutation | Yes | No | External integrations | | HTTP Action | Via runQuery/runMutation | Yes | No | Webhooks, APIs | ### Queries Queries are reactive, cached, and read-only: ```typescript import { query } from "./_generated/server"; import { v } from "convex/values"; export const getUser = query({ args: { userId: v.id("users") }, returns: v.union( v.object({ _id: v.id("users"), _creationTime: v.number(), name: v.string(), email: v.string(), }), v.null(), ), handler: async (ctx, args) => { return await ctx.db.get("users", args.userId); }, }); // Query with index export const listUserTasks = query({ args: { userId: v.id("users") }, returns: v.array( v.object({ _id: v.id("tasks"), _creationTime: v.number(), title: v.string(), completed: v.boolean(), }), ), handler: async (ctx, args) => { return await ctx.db .query("tasks") .withIndex("by_user", (q) => q.eq("userId", args.userId)) .order("desc") .collect(); }, }); ``` ### Mutations Mutations modify the database and are transactional: ```typescript import { mutation } from "./_generated/server"; import { v } from "convex/values"; import { ConvexError } from "convex/values"; export const createTask = mutation({ args: { title: v.string(), userId: v.id("users"), }, returns: v.id("tasks"), handler: async (ctx, args) => { // Validate user exists const user = await ctx.db.get("users", args.userId); if (!user) { throw new ConvexError("User not found"); } return await ctx.db.insert("tasks", { title: args.title, userId: args.userId, completed: false, createdAt: Date.now(), }); }, }); export const deleteTask = mutation({ args: { taskId: v.id("tasks") }, returns: v.null(), handler: async (ctx, args) => { await ctx.db.delete("tasks", args.taskId); return null; }, }); ``` ### Actions Actions can call external APIs but have no direct database access: ```typescript "use node"; import { action } from "./_generated/server"; import { v } from "convex/values"; import { api, internal } from "./_generated/api"; export const sendEmail = action({ args: { to: v.string(), subject: v.string(), body: v.string(), }, returns: v.object({ success: v.boolean() }), handler: async (ctx, args) => { // Call external API const response = await fetch("https://api.email.com/send", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(args), }); return { success: response.ok }; }, }); // Action calling queries and mutations export const processOrder = action({ args: { orderId: v.id("orders") }, returns: v.null(), handler: async (ctx, args) => { // Read data via query const order = await ctx.runQuery(api.orders.get, { orderId: args.orderId }); if (!order) { throw new Error("Order not found"); } // Call external payment API const paymentResult = await processPayment(order); // Update database via mutation await ctx.runMutation(internal.orders.updateStatus, { orderId: args.orderId, status: paymentResult.success ? "paid" : "failed", }); return null; }, }); ``` ### HTTP Actions HTTP actions handle webhooks and external requests: ```typescript // convex/http.ts import { httpRouter } from "convex/server"; import { httpAction } from "./_generated/server"; import { api, internal } from "./_generated/api"; const http = httpRouter(); // Webhook endpoint http.route({ path: "/webhooks/stripe", method: "POST", handler: httpAction(async (ctx, request) => { const signature = request.headers.get("stripe-signature"); const body = await request.text(); // Verify webhook signature if (!verifyStripeSignature(body, signature)) { return new Response("Invalid signature", { status: 401 }); } const event = JSON.parse(body); // Process webhook await ctx.runMutation(internal.payments.handleWebhook, { eventType: event.type, data: event.data, }); return new Response("OK", { status: 200 }); }), }); // API endpoint http.route({ path: "/api/users/:userId", method: "GET", handler: httpAction(async (ctx, request) => { const url = new URL(request.url); const userId = url.pathname.split("/").pop(); const user = await ctx.runQuery(api.users.get, { userId: userId as Id<"users">, }); if (!user) { return new Response("Not found", { status: 404 }); } return Response.json(user); }), }); export default http; ``` ### Internal Functions Use internal functions for sensitive operations: ```typescript import { internalMutation, internalQuery, internalAction, } from "./_generated/server"; import { v } from "convex/values"; // Only callable from other Convex functions export const _updateUserCredits = internalMutation({ args: { userId: v.id("users"), amount: v.number(), }, returns: v.null(), handler: async (ctx, args) => { const user = await ctx.db.get("users", args.userId); if (!user) return null; await ctx.db.patch("users", args.userId, { credits: (user.credits || 0) + args.amount, }); return null; }, }); // Call internal function from action export const purchaseCredits = action({ args: { userId: v.id("users"), amount: v.number() }, returns: v.null(), handler: async (ctx, args) => { // Process payment externally await processPayment(args.amount); // Update credits via internal mutation await ctx.runMutation(internal.users._updateUserCredits, { userId: args.userId, amount: args.amount, }); return null; }, }); ``` ### Scheduling Functions Schedule functions to run later: ```typescript import { mutation, internalMutation } from "./_generated/server"; import { v } from "convex/values"; import { internal } from "./_generated/api"; export const scheduleReminder = mutation({ args: { userId: v.id("users"), message: v.string(), delayMs: v.number(), }, returns: v.id("_scheduled_functions"), handler: async (ctx, args) => { return await ctx.scheduler.runAfter( args.delayMs, internal.notifications.sendReminder, { userId: args.userId, message: args.message }, ); }, }); export const sendReminder = internalMutation({ args: { userId: v.id("users"), message: v.string(), }, returns: v.null(), handler: async (ctx, args) => { await ctx.db.insert("notifications", { userId: args.userId, message: args.message, sentAt: Date.now(), }); return null; }, }); ``` ## Examples ### Complete Function File ```typescript // convex/messages.ts import { query, mutation, internalMutation } from "./_generated/server"; import { v } from "convex/values"; import { ConvexError } from "convex/values"; import { internal } from "./_generated/api"; const messageValidator = v.object({ _id: v.id("messages"), _creationTime: v.number(), channelId: v.id("channels"), authorId: v.id("users"), content: v.string(), editedAt: v.optional(v.number()), }); // Public query export const list = query({ args: { channelId: v.id("channels"), limit: v.optional(v.number()), }, returns: v.array(messageValidator), handler: async (ctx, args) => { const limit = args.limit ?? 50; return await ctx.db .query("messages") .withIndex("by_channel", (q) => q.eq("channelId", args.channelId)) .order("desc") .take(limit); }, }); // Public mutation export const send = mutation({ args: { channelId: v.id("channels"), authorId: v.id("users"), content: v.string(), }, returns: v.id("messages"), handler: async (ctx, args) => { if (args.content.trim().length === 0) { throw new ConvexError("Message cannot be empty"); } const messageId = await ctx.db.insert("messages", { channelId: args.channelId, authorId: args.authorId, content: args.content.trim(), }); // Schedule notification await ctx.scheduler.runAfter(0, internal.messages.notifySubscribers, { channelId: args.channelId, messageId, }); return messageId; }, }); // Internal mutation
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기