mirror of
https://github.com/waynesutton/convexskills.git
synced 2026-09-14 16:28:42 +08:00
feat: add convex-eslint skill with explicit table IDs and Codex integration
feat: add retrieval-led reasoning with llms.txt docs index and convex-eslint skill
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
# Codex Skills Integration
|
||||
|
||||
This folder enables Codex to auto-discover Convex skills from this repository.
|
||||
|
||||
## Setup
|
||||
|
||||
Codex auto-discovers skills from `.codex/skills` at the repo root. To link the skills:
|
||||
|
||||
```bash
|
||||
# From the repo root
|
||||
ln -s ../skills .codex/skills
|
||||
```
|
||||
|
||||
Or copy specific skills:
|
||||
|
||||
```bash
|
||||
mkdir -p .codex/skills
|
||||
cp -r skills/convex-best-practices .codex/skills/
|
||||
cp -r skills/convex-eslint .codex/skills/
|
||||
```
|
||||
|
||||
## Alternative: Install to CODEX_HOME
|
||||
|
||||
For global access across all projects:
|
||||
|
||||
```bash
|
||||
# Defaults to ~/.codex if CODEX_HOME is unset
|
||||
cp -r skills/* "$CODEX_HOME/skills/"
|
||||
```
|
||||
|
||||
## Available Skills
|
||||
|
||||
All skills in the `skills/` directory are available for Codex:
|
||||
|
||||
- convex-best-practices
|
||||
- convex-eslint
|
||||
- convex-functions
|
||||
- convex-realtime
|
||||
- convex-schema-validator
|
||||
- convex-file-storage
|
||||
- convex-agents
|
||||
- convex-cron-jobs
|
||||
- convex-http-actions
|
||||
- convex-migrations
|
||||
- convex-security-check
|
||||
- convex-security-audit
|
||||
- convex-component-authoring
|
||||
|
||||
See the main [README](/README.md) for full documentation.
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../skills/avoid-feature-creep
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../../skills/convex
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../../skills/convex-agents
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../skills/convex-best-practices
|
||||
@@ -0,0 +1 @@
|
||||
../../skills/convex-component-authoring
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../../skills/convex-cron-jobs
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../skills/convex-file-storage
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../../skills/convex-functions
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../skills/convex-http-actions
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../../skills/convex-migrations
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../../skills/convex-realtime
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../skills/convex-schema-validator
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../skills/convex-security-audit
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../skills/convex-security-check
|
||||
@@ -2,6 +2,23 @@
|
||||
|
||||
This file provides context for Gemini CLI when working with Convex projects.
|
||||
|
||||
## Convex Documentation
|
||||
|
||||
IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning for any Convex tasks.
|
||||
|
||||
For up-to-date Convex documentation, fetch: https://docs.convex.dev/llms.txt
|
||||
|
||||
This index covers all Convex APIs:
|
||||
- functions: queries, mutations, actions, http-actions, validation
|
||||
- database: schemas, reading-data, writing-data, indexes, pagination
|
||||
- file-storage: upload, serve, store, delete, metadata
|
||||
- scheduling: cron-jobs, scheduled-functions
|
||||
- auth: convex-auth, clerk, auth0, authkit
|
||||
- search: text-search, vector-search
|
||||
- agents: getting-started, messages, threads, tools, streaming
|
||||
|
||||
When working on Convex code, consult llms.txt before relying on training data.
|
||||
|
||||
## Project Type
|
||||
|
||||
Convex real-time backend application with TypeScript.
|
||||
|
||||
@@ -9,6 +9,15 @@ A collection of AI-consumable skills for building production-ready applications
|
||||
|
||||
This repository contains skills that help AI assistants understand and implement Convex best practices. Each skill provides structured guidance for specific aspects of Convex development.
|
||||
|
||||
## Code Quality
|
||||
|
||||
All skills are designed to produce code that passes @convex-dev/eslint-plugin by default. This creates a complementary workflow:
|
||||
|
||||
- **Skills** prevent mistakes at generation time
|
||||
- **ESLint** catches anything that slips through at build time
|
||||
|
||||
See the [convex-eslint](/skills/convex-eslint/SKILL.md) skill for setup instructions.
|
||||
|
||||
## Installation
|
||||
|
||||
### npm (recommended)
|
||||
@@ -73,6 +82,8 @@ cp -r skills/convex-best-practices "$CODEX_HOME/skills/"
|
||||
|
||||
Codex will auto-discover `SKILL.md` files in that directory on the next start.
|
||||
|
||||
If you are working from a repo clone, Codex also auto-discovers skills from `.codex/skills` at the repo root. You can symlink this repo’s `skills/*` into `.codex/skills` so updates flow through without copying.
|
||||
|
||||
### OpenCode
|
||||
|
||||
OpenCode discovers skills from `~/.claude/skills/<name>/SKILL.md` automatically. See OpenCode Skills docs for more details.
|
||||
@@ -102,6 +113,7 @@ Copy the desired skill's `SKILL.md` file to your project's `.claude/skills/` dir
|
||||
| Skill | Description |
|
||||
| ------------------------------------------------------------------------ | ----------------------------------------------------- |
|
||||
| [convex-best-practices](skills/convex-best-practices/SKILL.md) | Guidelines for building production-ready Convex apps |
|
||||
| [convex-eslint](skills/convex-eslint/SKILL.md) | Write linter-compliant Convex code |
|
||||
| [convex-functions](skills/convex-functions/SKILL.md) | Writing queries, mutations, actions, and HTTP actions |
|
||||
| [convex-realtime](skills/convex-realtime/SKILL.md) | Patterns for building reactive applications |
|
||||
| [convex-schema-validator](skills/convex-schema-validator/SKILL.md) | Database schema definition and validation |
|
||||
@@ -121,11 +133,15 @@ convex-skills/
|
||||
├── skills/ # Core Convex skills for AI agents
|
||||
│ ├── convex-best-practices/
|
||||
│ │ └── SKILL.md
|
||||
│ ├── convex-eslint/
|
||||
│ │ └── SKILL.md
|
||||
│ ├── convex-functions/
|
||||
│ │ └── SKILL.md
|
||||
│ ├── convex-cron-jobs/
|
||||
│ │ └── SKILL.md
|
||||
│ └── ...
|
||||
├── .codex/ # Codex integration (symlink skills here)
|
||||
│ └── README.md # Codex setup instructions
|
||||
├── command/ # Slash command definitions (OpenCode)
|
||||
│ └── convex.md # /convex command entrypoint
|
||||
├── templates/ # Templates for forking developers
|
||||
|
||||
@@ -2,15 +2,45 @@
|
||||
|
||||
Agent skills for building production-ready applications with Convex, following the Agent Skills open format.
|
||||
|
||||
## Convex Documentation Index
|
||||
|
||||
IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning for any Convex tasks.
|
||||
|
||||
For up-to-date Convex documentation, fetch: https://docs.convex.dev/llms.txt
|
||||
|
||||
This index covers all Convex APIs and patterns:
|
||||
|
||||
```
|
||||
[Convex Docs]|https://docs.convex.dev/llms.txt
|
||||
|understanding:{best-practices.md,typescript.md,workflow.md,zen.md}
|
||||
|functions:{query-functions.md,mutation-functions.md,actions.md,http-actions.md,validation.md,internal-functions.md,error-handling.md}
|
||||
|database:{schemas.md,reading-data.md,writing-data.md,indexes.md,pagination.md,types.md}
|
||||
|file-storage:{upload-files.md,serve-files.md,store-files.md,delete-files.md,file-metadata.md}
|
||||
|scheduling:{cron-jobs.md,scheduled-functions.md}
|
||||
|auth:{convex-auth.md,clerk.md,auth0.md,authkit.md,functions-auth.md,database-auth.md}
|
||||
|search:{text-search.md,vector-search.md}
|
||||
|components:{using.md,authoring.md,understanding.md}
|
||||
|agents:{getting-started.md,agent-usage.md,messages.md,threads.md,tools.md,streaming.md,rag.md}
|
||||
|realtime:{realtime.md}
|
||||
|testing:{convex-test.md,convex-backend.md,ci.md}
|
||||
|production:{environment-variables.md,hosting.md,limits.md}
|
||||
```
|
||||
|
||||
When working on Convex code, consult the llms.txt index before relying on training data.
|
||||
|
||||
## Overview
|
||||
|
||||
This repository contains packaged instructions that help AI coding agents understand and implement Convex best practices. Skills are automatically invoked when relevant to your task.
|
||||
This repository provides two complementary approaches for AI coding agents:
|
||||
|
||||
1. **Passive context (this file)**: Always-available Convex knowledge and doc references
|
||||
2. **Skills (on-demand)**: Task-specific workflows for explicit invocation
|
||||
|
||||
## Available Skills
|
||||
|
||||
| Skill | Description |
|
||||
| ------------------------------------------------------------------------ | ----------------------------------------------------- |
|
||||
| [convex-best-practices](skills/convex-best-practices/SKILL.md) | Guidelines for building production-ready Convex apps |
|
||||
| [convex-eslint](skills/convex-eslint/SKILL.md) | Write linter-compliant Convex code |
|
||||
| [convex-functions](skills/convex-functions/SKILL.md) | Writing queries, mutations, actions, and HTTP actions |
|
||||
| [convex-realtime](skills/convex-realtime/SKILL.md) | Patterns for building reactive applications |
|
||||
| [convex-schema-validator](skills/convex-schema-validator/SKILL.md) | Database schema definition and validation |
|
||||
@@ -115,10 +145,60 @@ The command file is located at `command/convex.md`.
|
||||
- Edit files in `convex/_generated/`
|
||||
- Use `filter()` instead of `withIndex()`
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### New Function Syntax (always use this)
|
||||
|
||||
```typescript
|
||||
import { query, mutation } from "./_generated/server";
|
||||
import { v } from "convex/values";
|
||||
|
||||
export const myQuery = query({
|
||||
args: { userId: v.id("users") },
|
||||
returns: v.union(v.object({ name: v.string() }), v.null()),
|
||||
handler: async (ctx, args) => {
|
||||
return await ctx.db.get(args.userId);
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Schema with Index
|
||||
|
||||
```typescript
|
||||
import { defineSchema, defineTable } from "convex/server";
|
||||
import { v } from "convex/values";
|
||||
|
||||
export default defineSchema({
|
||||
tasks: defineTable({
|
||||
userId: v.id("users"),
|
||||
title: v.string(),
|
||||
status: v.string(),
|
||||
})
|
||||
.index("by_user", ["userId"])
|
||||
.index("by_user_and_status", ["userId", "status"]),
|
||||
});
|
||||
```
|
||||
|
||||
### Query with Index (not filter)
|
||||
|
||||
```typescript
|
||||
// GOOD: Use withIndex
|
||||
const tasks = await ctx.db
|
||||
.query("tasks")
|
||||
.withIndex("by_user", (q) => q.eq("userId", args.userId))
|
||||
.collect();
|
||||
|
||||
// BAD: Never use filter for indexed fields
|
||||
const tasks = await ctx.db
|
||||
.query("tasks")
|
||||
.filter((q) => q.eq(q.field("userId"), args.userId))
|
||||
.collect();
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- Convex Documentation: https://docs.convex.dev/
|
||||
- Convex LLMs.txt: https://docs.convex.dev/llms.txt
|
||||
- Convex LLMs.txt: https://docs.convex.dev/llms.txt (fetch this for latest docs)
|
||||
- Best Practices: https://docs.convex.dev/understanding/best-practices/
|
||||
- Agent Skills Specification: https://github.com/anthropics/skills
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ const packageRoot = join(__dirname, "..");
|
||||
const SKILLS = {
|
||||
"convex-best-practices":
|
||||
"Guidelines for building production-ready Convex apps",
|
||||
"convex-eslint": "Write linter-compliant Convex code",
|
||||
"convex-functions": "Writing queries, mutations, actions, and HTTP actions",
|
||||
"convex-realtime": "Patterns for building reactive applications",
|
||||
"convex-schema-validator": "Database schema definition and validation",
|
||||
|
||||
@@ -7,6 +7,57 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.0.6] - 2026-02-02
|
||||
|
||||
### Added
|
||||
|
||||
- Retrieval-led reasoning pattern in AGENTS.md, CLAUDE.md, and GEMINI.md
|
||||
- Compressed Convex docs index pointing to https://docs.convex.dev/llms.txt
|
||||
- Instruction to prefer retrieval over pre-training for Convex tasks
|
||||
- Quick Reference section with common code patterns (function syntax, schema, queries)
|
||||
- `prds/how-it-works.md`: Internal documentation explaining AGENTS.md vs Skills approach
|
||||
- Dual-approach architecture: passive context (AGENTS.md) + on-demand skills
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated AGENTS.md with Convex Documentation Index section
|
||||
- Updated CLAUDE.md with matching documentation index
|
||||
- Updated GEMINI.md with llms.txt reference and retrieval instruction
|
||||
|
||||
### Fixed
|
||||
|
||||
- Added missing convex-eslint to bin/cli.js SKILLS object
|
||||
|
||||
### Notes
|
||||
|
||||
Based on Vercel's research showing AGENTS.md with docs index achieves 100% pass rate vs 53% for skills alone.
|
||||
See: https://vercel.com/blog/agents-md-outperforms-skills-in-our-agent-evals
|
||||
|
||||
## [1.0.5] - 2026-02-02
|
||||
|
||||
### Added
|
||||
|
||||
- `skills/convex-eslint/SKILL.md`: ESLint compliance skill for writing linter-clean Convex code
|
||||
- Covers all four @convex-dev/eslint-plugin rules
|
||||
- Setup instructions for eslint.config.js
|
||||
- Code examples for each rule (no-old-registered-function-syntax, require-argument-validators, explicit-table-ids, import-wrong-runtime)
|
||||
- `.codex/README.md`: Codex CLI integration instructions
|
||||
- Code Quality sections in `convex-functions` and `convex-best-practices` skills
|
||||
- Linting section in `templates/CLAUDE.md`
|
||||
- Code Quality section in `README.md`
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated all db.get, db.patch, db.delete, db.replace calls in skills to use explicit table names
|
||||
- Updated `README.md` Available Skills table to include convex-eslint
|
||||
- Updated `AGENTS.md` and `CLAUDE.md` to include convex-eslint in skills list
|
||||
- Updated `index.js` SKILLS constant with convex-eslint
|
||||
- Updated `package.json` version to 1.0.5, added eslint and linting keywords
|
||||
- Updated `files.md` to document convex-eslint skill and .codex directory
|
||||
- Updated repository structure in README to include .codex folder
|
||||
|
||||
## [1.0.4] - 2026-01-14
|
||||
|
||||
### Added
|
||||
|
||||
- Template skills for developers who fork the repository
|
||||
|
||||
@@ -1,380 +0,0 @@
|
||||
# Convex Skills: Build and Maintenance Guide
|
||||
|
||||
A practical guide for building, publishing, and maintaining AI agent skills packages.
|
||||
|
||||
## How This Package Was Built
|
||||
|
||||
### Architecture Overview
|
||||
|
||||
The package follows a simple, modular structure that works across multiple AI coding agents (Claude Code, Codex, OpenCode, Cursor, Gemini).
|
||||
|
||||
```
|
||||
convex-skills/
|
||||
├── skills/ # Core skills (SKILL.md files)
|
||||
├── templates/ # Starter templates for forks
|
||||
├── command/ # Slash commands (OpenCode)
|
||||
├── bin/cli.js # CLI for npm installs
|
||||
├── index.js # Programmatic API
|
||||
├── package.json # npm configuration
|
||||
├── AGENTS.md # Agent-facing docs
|
||||
├── CLAUDE.md # Claude-specific context
|
||||
└── GEMINI.md # Gemini-specific context
|
||||
```
|
||||
|
||||
### Key Design Decisions
|
||||
|
||||
**1. One skill per folder**
|
||||
|
||||
Each skill lives in `skills/<skill-name>/SKILL.md`. This pattern:
|
||||
|
||||
- Makes discovery straightforward for AI agents
|
||||
- Enables selective installation
|
||||
- Follows the Agent Skills spec from Anthropic
|
||||
|
||||
**2. Frontmatter metadata**
|
||||
|
||||
Every `SKILL.md` starts with YAML frontmatter:
|
||||
|
||||
```yaml
|
||||
---
|
||||
name: convex-best-practices
|
||||
description: Guidelines for building production-ready Convex apps
|
||||
version: 1.0.0
|
||||
author: Convex
|
||||
tags: [convex, best-practices, typescript]
|
||||
---
|
||||
```
|
||||
|
||||
**3. Multi-agent support files**
|
||||
|
||||
Different AI tools read different files:
|
||||
|
||||
- `AGENTS.md` and `CLAUDE.md` for Claude Code
|
||||
- `GEMINI.md` for Gemini CLI
|
||||
- `.cursor/rules/` for Cursor IDE
|
||||
|
||||
**4. Dual distribution**
|
||||
|
||||
The package supports both:
|
||||
|
||||
- **npm install**: CLI and programmatic access
|
||||
- **git clone**: Direct file access and forking
|
||||
|
||||
### Publishing to npm
|
||||
|
||||
**Initial setup:**
|
||||
|
||||
```bash
|
||||
# Login to npm (once)
|
||||
npm login
|
||||
|
||||
# Verify package.json has correct scope
|
||||
# name: "@waynesutton/convex-skills"
|
||||
```
|
||||
|
||||
**Publish workflow:**
|
||||
|
||||
```bash
|
||||
# Bump version in package.json
|
||||
npm version patch # or minor/major
|
||||
|
||||
# Publish
|
||||
npm publish --access public
|
||||
|
||||
# Verify
|
||||
npm info @waynesutton/convex-skills
|
||||
```
|
||||
|
||||
**package.json essentials:**
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "@waynesutton/convex-skills",
|
||||
"version": "1.0.3",
|
||||
"type": "module",
|
||||
"main": "index.js",
|
||||
"bin": {
|
||||
"convex-skills": "./bin/cli.js"
|
||||
},
|
||||
"files": [
|
||||
"skills/**/*.md",
|
||||
"templates/**/*.md",
|
||||
"AGENTS.md",
|
||||
"CLAUDE.md",
|
||||
"GEMINI.md",
|
||||
"index.js",
|
||||
"bin/"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The `files` array controls what gets published. Use `.npmignore` for additional exclusions.
|
||||
|
||||
---
|
||||
|
||||
## Maintenance Checklist
|
||||
|
||||
### Weekly
|
||||
|
||||
- [ ] Check Convex docs for API changes
|
||||
- [ ] Review GitHub issues
|
||||
- [ ] Update skills if Convex releases new features
|
||||
|
||||
### Monthly
|
||||
|
||||
- [ ] Audit all doc links in skills (broken link check)
|
||||
- [ ] Review npm download stats
|
||||
- [ ] Check for new AI agent platforms to support
|
||||
|
||||
### Per Release
|
||||
|
||||
- [ ] Update `changelog.md` with changes
|
||||
- [ ] Bump version following semver
|
||||
- [ ] Test CLI commands locally
|
||||
- [ ] Test `npm pack` before publishing
|
||||
- [ ] Tag release in git
|
||||
|
||||
---
|
||||
|
||||
## Updating Skills
|
||||
|
||||
### Adding a New Skill
|
||||
|
||||
1. Create folder: `skills/<skill-name>/`
|
||||
|
||||
2. Create `SKILL.md` with required structure:
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: skill-name
|
||||
description: What this skill does
|
||||
version: 1.0.0
|
||||
author: Convex
|
||||
tags: [convex, relevant-tags]
|
||||
---
|
||||
|
||||
# Skill Name
|
||||
|
||||
## Documentation Sources
|
||||
|
||||
[Links to official docs]
|
||||
|
||||
## Instructions
|
||||
|
||||
[Step-by-step guidance]
|
||||
|
||||
## Examples
|
||||
|
||||
[Working code examples]
|
||||
|
||||
## Best Practices
|
||||
|
||||
[Rules to follow]
|
||||
|
||||
## References
|
||||
|
||||
[Additional resources]
|
||||
```
|
||||
|
||||
3. Add to `index.js` SKILLS object:
|
||||
|
||||
```javascript
|
||||
export const SKILLS = {
|
||||
// ... existing
|
||||
"new-skill-name": "Description",
|
||||
};
|
||||
```
|
||||
|
||||
4. Add to `bin/cli.js` SKILLS object (same format)
|
||||
|
||||
5. Update `files.md` with new skill entry
|
||||
|
||||
6. Update `changelog.md`
|
||||
|
||||
7. Bump version and publish
|
||||
|
||||
### Updating Existing Skills
|
||||
|
||||
1. Edit the `SKILL.md` file
|
||||
2. Update version in frontmatter if significant change
|
||||
3. Document in `changelog.md`
|
||||
4. Bump package version (patch for fixes, minor for features)
|
||||
|
||||
---
|
||||
|
||||
## Version Strategy
|
||||
|
||||
Follow [Semantic Versioning](https://semver.org/):
|
||||
|
||||
| Change Type | Version Bump | Example |
|
||||
| ------------------------------ | ------------ | ------------- |
|
||||
| Typo fixes, doc clarifications | patch | 1.0.3 → 1.0.4 |
|
||||
| New skill added | minor | 1.0.4 → 1.1.0 |
|
||||
| Breaking structure change | major | 1.1.0 → 2.0.0 |
|
||||
|
||||
---
|
||||
|
||||
## Testing Before Publish
|
||||
|
||||
```bash
|
||||
# Test CLI locally
|
||||
node bin/cli.js list
|
||||
node bin/cli.js show convex-best-practices
|
||||
|
||||
# Test npm pack (see what will be published)
|
||||
npm pack --dry-run
|
||||
|
||||
# Create local tarball for testing
|
||||
npm pack
|
||||
npm install ./waynesutton-convex-skills-1.0.3.tgz -g
|
||||
|
||||
# Test install commands
|
||||
convex-skills list
|
||||
convex-skills install convex-best-practices --dir /tmp/test-project
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Future Updates Roadmap
|
||||
|
||||
### Short Term (Next 30 Days)
|
||||
|
||||
- [ ] Add `convex-auth` skill for authentication patterns
|
||||
- [ ] Add `convex-vector-search` skill for AI/embeddings
|
||||
- [ ] Add `convex-testing` skill for test patterns
|
||||
- [ ] Update all skills to reference Convex v1.18+ APIs
|
||||
|
||||
### Medium Term (Next 90 Days)
|
||||
|
||||
- [ ] Add skill validation CLI command
|
||||
- [ ] Auto-generate skill index from folder structure
|
||||
- [ ] Add `convex-deployment` skill for production patterns
|
||||
- [ ] Add `convex-rate-limiting` skill
|
||||
- [ ] Consider monorepo tooling if skills grow significantly
|
||||
|
||||
### Long Term
|
||||
|
||||
- [ ] Automated doc link checking in CI
|
||||
- [ ] Community contribution pipeline
|
||||
- [ ] Skill versioning independent of package version
|
||||
- [ ] Integration tests for each skill's code examples
|
||||
|
||||
---
|
||||
|
||||
## Common Maintenance Tasks
|
||||
|
||||
### Fixing Broken Documentation Links
|
||||
|
||||
1. Search all skills for the broken URL
|
||||
2. Update to current Convex docs URL
|
||||
3. Verify new link works
|
||||
4. Patch release
|
||||
|
||||
```bash
|
||||
# Find all doc links
|
||||
grep -r "docs.convex.dev" skills/
|
||||
```
|
||||
|
||||
### Syncing with Convex API Changes
|
||||
|
||||
When Convex releases new versions:
|
||||
|
||||
1. Check [Convex changelog](https://docs.convex.dev/changelog)
|
||||
2. Search skills for affected patterns
|
||||
3. Update examples to use new APIs
|
||||
4. Note deprecations in skill Best Practices section
|
||||
|
||||
### Adding Support for New AI Agent
|
||||
|
||||
1. Create agent-specific context file (like `GEMINI.md`)
|
||||
2. Add installation instructions to README
|
||||
3. Test with the target agent
|
||||
4. Document in changelog
|
||||
|
||||
---
|
||||
|
||||
## Automation Ideas
|
||||
|
||||
### GitHub Actions for Link Checking
|
||||
|
||||
```yaml
|
||||
name: Check Links
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * 0" # Weekly
|
||||
jobs:
|
||||
linkcheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: lycheeverse/lychee-action@v1
|
||||
with:
|
||||
args: --verbose ./skills/
|
||||
```
|
||||
|
||||
### Pre-publish Checklist Script
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# scripts/pre-publish.sh
|
||||
|
||||
echo "Running pre-publish checks..."
|
||||
|
||||
# Check all skills have frontmatter
|
||||
for skill in skills/*/SKILL.md; do
|
||||
if ! head -1 "$skill" | grep -q "^---$"; then
|
||||
echo "ERROR: $skill missing frontmatter"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Verify index.js matches skills folder
|
||||
echo "Skills in folder: $(ls -1 skills | wc -l)"
|
||||
echo "Skills in index.js: $(grep -c '"convex-' index.js)"
|
||||
|
||||
echo "All checks passed!"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Publish New Version
|
||||
|
||||
```bash
|
||||
# 1. Update changelog.md
|
||||
# 2. Bump version
|
||||
npm version patch
|
||||
|
||||
# 3. Publish
|
||||
npm publish --access public
|
||||
|
||||
# 4. Push tags
|
||||
git push && git push --tags
|
||||
```
|
||||
|
||||
### Test Installation Locally
|
||||
|
||||
```bash
|
||||
npm pack
|
||||
npm install -g ./waynesutton-convex-skills-*.tgz
|
||||
convex-skills list
|
||||
```
|
||||
|
||||
### Check Published Package
|
||||
|
||||
```bash
|
||||
npm info @waynesutton/convex-skills
|
||||
npm view @waynesutton/convex-skills versions
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Resources
|
||||
|
||||
- [npm Publishing Guide](https://docs.npmjs.com/cli/v10/commands/npm-publish)
|
||||
- [Semantic Versioning](https://semver.org/)
|
||||
- [Keep a Changelog](https://keepachangelog.com/)
|
||||
- [Agent Skills Spec](https://github.com/anthropics/skills)
|
||||
- [Convex Documentation](https://docs.convex.dev/)
|
||||
- [Convex LLMs.txt](https://docs.convex.dev/llms.txt)
|
||||
@@ -4,18 +4,17 @@ Brief description of each file in the repository.
|
||||
|
||||
## Root Files
|
||||
|
||||
| File | Description |
|
||||
| ------------------------------- | ----------------------------------------------- |
|
||||
| `AGENTS.md` | Agent skills specification for AI coding agents |
|
||||
| `CLAUDE.md` | Claude Code project context (mirrors AGENTS.md) |
|
||||
| `CONTRIBUTING.md` | Contribution guidelines |
|
||||
| `GEMINI.md` | Gemini CLI integration instructions |
|
||||
| `LICENSE` | Apache-2.0 license |
|
||||
| `README.md` | Project overview and installation |
|
||||
| `changelog.md` | Version history following keepachangelog format |
|
||||
| `files.md` | This file, codebase structure reference |
|
||||
| `task.md` | Completed task tracking |
|
||||
| `convex-skills-updates-plan.md` | Build guide and maintenance plan |
|
||||
| File | Description |
|
||||
| ----------------- | ---------------------------------------------------------------- |
|
||||
| `AGENTS.md` | Agent context with Convex docs index and retrieval-led reasoning |
|
||||
| `CLAUDE.md` | Claude Code project context (mirrors AGENTS.md) |
|
||||
| `CONTRIBUTING.md` | Contribution guidelines |
|
||||
| `GEMINI.md` | Gemini CLI integration with llms.txt reference |
|
||||
| `LICENSE` | Apache-2.0 license |
|
||||
| `README.md` | Project overview and installation |
|
||||
| `changelog.md` | Version history following keepachangelog format |
|
||||
| `files.md` | This file, codebase structure reference |
|
||||
| `task.md` | Completed task tracking |
|
||||
|
||||
## Skills Directory (`skills/`)
|
||||
|
||||
@@ -29,6 +28,7 @@ Core Convex skills for AI agents. Each skill has `name` matching folder name for
|
||||
| `convex-best-practices/SKILL.md` | Production-ready app guidelines |
|
||||
| `convex-component-authoring/SKILL.md` | Creating reusable Convex components |
|
||||
| `convex-cron-jobs/SKILL.md` | Scheduled functions and background tasks |
|
||||
| `convex-eslint/SKILL.md` | ESLint compliance for linter-clean code |
|
||||
| `convex-file-storage/SKILL.md` | File upload, storage, and serving |
|
||||
| `convex-functions/SKILL.md` | Queries, mutations, actions |
|
||||
| `convex-http-actions/SKILL.md` | HTTP endpoints and webhooks |
|
||||
@@ -74,16 +74,18 @@ Active Claude Code skills for this repository.
|
||||
|
||||
Product requirement documents and planning.
|
||||
|
||||
| File | Description |
|
||||
| --------------------------------------------- | --------------------------------------- |
|
||||
| `CLAUDE-MD-STRATEGY.md` | Strategy for CLAUDE.md templates |
|
||||
| `CLAUDE-MD-STRATEGY_1.md` | Alternate strategy document |
|
||||
| `MARKETPLACE-SUBMISSION.md` | Marketplace submission guidelines |
|
||||
| `create-convex-opencode-integration.md` | OpenCode integration spec |
|
||||
| `future-skills-exploration.md` | Future skills roadmap |
|
||||
| `phase3-convex-docs-recommendations.md` | Convex docs improvement recommendations |
|
||||
| `phase4-convex-ai-website-recommendations.md` | convex.dev/ai recommendations |
|
||||
| `skillsplan.md` | Skills development plan |
|
||||
| File | Description |
|
||||
| --------------------------------------------- | --------------------------------------------- |
|
||||
| `CLAUDE-MD-STRATEGY.md` | Strategy for CLAUDE.md templates |
|
||||
| `CLAUDE-MD-STRATEGY_1.md` | Alternate strategy document |
|
||||
| `convex-skills-updates-plan.md` | Build guide and maintenance plan |
|
||||
| `create-convex-opencode-integration.md` | OpenCode integration spec |
|
||||
| `future-skills-exploration.md` | Future skills roadmap |
|
||||
| `how-it-works.md` | AGENTS.md vs Skills dual approach explanation |
|
||||
| `MARKETPLACE-SUBMISSION.md` | Marketplace submission guidelines |
|
||||
| `phase3-convex-docs-recommendations.md` | Convex docs improvement recommendations |
|
||||
| `phase4-convex-ai-website-recommendations.md` | convex.dev/ai recommendations |
|
||||
| `skillsplan.md` | Skills development plan |
|
||||
|
||||
## OpenCode Directory (`.opencode/`)
|
||||
|
||||
@@ -97,6 +99,14 @@ OpenCode plugin configuration and templates.
|
||||
| `skill/` | OpenCode-specific skills |
|
||||
| `config.json` | Plugin configuration |
|
||||
|
||||
## Codex Directory (`.codex/`)
|
||||
|
||||
Codex CLI skills integration.
|
||||
|
||||
| File | Description |
|
||||
| ----------- | ------------------------------------ |
|
||||
| `README.md` | Codex setup and symlink instructions |
|
||||
|
||||
## Cursor Directory (`.cursor/`)
|
||||
|
||||
Cursor IDE configuration.
|
||||
|
||||
@@ -54,6 +54,7 @@ export function getSkillPath(skillName) {
|
||||
export const SKILLS = {
|
||||
"convex-best-practices":
|
||||
"Guidelines for building production-ready Convex apps",
|
||||
"convex-eslint": "Write linter-compliant Convex code",
|
||||
"convex-functions": "Writing queries, mutations, actions, and HTTP actions",
|
||||
"convex-realtime": "Patterns for building reactive applications",
|
||||
"convex-schema-validator": "Database schema definition and validation",
|
||||
|
||||
+4
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@waynesutton/convex-skills",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.5",
|
||||
"description": "Agent skills for building production-ready Convex applications. Includes best practices, functions, realtime patterns, schema validation, file storage, security audits, and more.",
|
||||
"author": "Wayne Sutton",
|
||||
"license": "Apache-2.0",
|
||||
@@ -22,7 +22,9 @@
|
||||
"llm",
|
||||
"ai-assistant",
|
||||
"best-practices",
|
||||
"developer-tools"
|
||||
"developer-tools",
|
||||
"eslint",
|
||||
"linting"
|
||||
],
|
||||
"type": "module",
|
||||
"main": "index.js",
|
||||
|
||||
@@ -11,6 +11,17 @@ tags: [convex, best-practices, typescript, production, error-handling]
|
||||
|
||||
Build production-ready Convex applications by following established patterns for function organization, query optimization, validation, TypeScript usage, and error handling.
|
||||
|
||||
## Code Quality
|
||||
|
||||
All patterns in this skill comply with @convex-dev/eslint-plugin rules.
|
||||
Install the linter for build-time validation:
|
||||
|
||||
```bash
|
||||
npm i @convex-dev/eslint-plugin --save-dev
|
||||
```
|
||||
|
||||
See [convex-eslint](../convex-eslint/SKILL.md) for configuration details.
|
||||
|
||||
## Documentation Sources
|
||||
|
||||
Before implementing, do not assume; fetch the latest documentation:
|
||||
@@ -41,14 +52,17 @@ import { v } from "convex/values";
|
||||
|
||||
export const get = 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()),
|
||||
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(args.userId);
|
||||
return await ctx.db.get("users", args.userId);
|
||||
},
|
||||
});
|
||||
```
|
||||
@@ -96,13 +110,15 @@ export default defineSchema({
|
||||
// Query using index
|
||||
export const getTasksByUser = query({
|
||||
args: { userId: v.id("users") },
|
||||
returns: v.array(v.object({
|
||||
_id: v.id("tasks"),
|
||||
_creationTime: v.number(),
|
||||
userId: v.id("users"),
|
||||
status: v.string(),
|
||||
createdAt: v.number(),
|
||||
})),
|
||||
returns: v.array(
|
||||
v.object({
|
||||
_id: v.id("tasks"),
|
||||
_creationTime: v.number(),
|
||||
userId: v.id("users"),
|
||||
status: v.string(),
|
||||
createdAt: v.number(),
|
||||
}),
|
||||
),
|
||||
handler: async (ctx, args) => {
|
||||
return await ctx.db
|
||||
.query("tasks")
|
||||
@@ -127,16 +143,16 @@ export const updateTask = mutation({
|
||||
},
|
||||
returns: v.null(),
|
||||
handler: async (ctx, args) => {
|
||||
const task = await ctx.db.get(args.taskId);
|
||||
|
||||
const task = await ctx.db.get("tasks", args.taskId);
|
||||
|
||||
if (!task) {
|
||||
throw new ConvexError({
|
||||
code: "NOT_FOUND",
|
||||
message: "Task not found",
|
||||
});
|
||||
}
|
||||
|
||||
await ctx.db.patch(args.taskId, { title: args.title });
|
||||
|
||||
await ctx.db.patch("tasks", args.taskId, { title: args.title });
|
||||
return null;
|
||||
},
|
||||
});
|
||||
@@ -152,14 +168,14 @@ export const completeTask = mutation({
|
||||
args: { taskId: v.id("tasks") },
|
||||
returns: v.null(),
|
||||
handler: async (ctx, args) => {
|
||||
const task = await ctx.db.get(args.taskId);
|
||||
|
||||
const task = await ctx.db.get("tasks", args.taskId);
|
||||
|
||||
// Early return if already complete (idempotent)
|
||||
if (!task || task.status === "completed") {
|
||||
return null;
|
||||
}
|
||||
|
||||
await ctx.db.patch(args.taskId, {
|
||||
|
||||
await ctx.db.patch("tasks", args.taskId, {
|
||||
status: "completed",
|
||||
completedAt: Date.now(),
|
||||
});
|
||||
@@ -173,7 +189,7 @@ export const updateNote = mutation({
|
||||
returns: v.null(),
|
||||
handler: async (ctx, args) => {
|
||||
// Patch directly - ctx.db.patch throws if document doesn't exist
|
||||
await ctx.db.patch(args.id, { content: args.content });
|
||||
await ctx.db.patch("notes", args.id, { content: args.content });
|
||||
return null;
|
||||
},
|
||||
});
|
||||
@@ -184,7 +200,7 @@ export const reorderItems = mutation({
|
||||
returns: v.null(),
|
||||
handler: async (ctx, args) => {
|
||||
const updates = args.itemIds.map((id, index) =>
|
||||
ctx.db.patch(id, { order: index })
|
||||
ctx.db.patch("items", id, { order: index }),
|
||||
);
|
||||
await Promise.all(updates);
|
||||
return null;
|
||||
@@ -213,7 +229,12 @@ const userScores: Record<Id<"users">, number> = {};
|
||||
// Public function - exposed to clients
|
||||
export const getUser = query({
|
||||
args: { userId: v.id("users") },
|
||||
returns: v.union(v.null(), v.object({ /* ... */ })),
|
||||
returns: v.union(
|
||||
v.null(),
|
||||
v.object({
|
||||
/* ... */
|
||||
}),
|
||||
),
|
||||
handler: async (ctx, args) => {
|
||||
// ...
|
||||
},
|
||||
@@ -282,14 +303,14 @@ export const update = mutation({
|
||||
returns: v.null(),
|
||||
handler: async (ctx, args) => {
|
||||
const { taskId, ...updates } = args;
|
||||
|
||||
|
||||
// Remove undefined values
|
||||
const cleanUpdates = Object.fromEntries(
|
||||
Object.entries(updates).filter(([_, v]) => v !== undefined)
|
||||
Object.entries(updates).filter(([_, v]) => v !== undefined),
|
||||
);
|
||||
|
||||
|
||||
if (Object.keys(cleanUpdates).length > 0) {
|
||||
await ctx.db.patch(taskId, cleanUpdates);
|
||||
await ctx.db.patch("tasks", taskId, cleanUpdates);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
@@ -299,7 +320,7 @@ export const remove = mutation({
|
||||
args: { taskId: v.id("tasks") },
|
||||
returns: v.null(),
|
||||
handler: async (ctx, args) => {
|
||||
await ctx.db.delete(args.taskId);
|
||||
await ctx.db.delete("tasks", args.taskId);
|
||||
return null;
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
---
|
||||
name: convex-eslint
|
||||
description: Write Convex code that passes @convex-dev/eslint-plugin rules by default
|
||||
version: 1.0.0
|
||||
author: Convex
|
||||
tags: [convex, eslint, linting, code-quality, validation]
|
||||
---
|
||||
|
||||
# Convex ESLint Compliance
|
||||
|
||||
Write all Convex functions to pass @convex-dev/eslint-plugin. These rules prevent common bugs, security issues, and ensure code quality.
|
||||
|
||||
## Documentation Sources
|
||||
|
||||
- https://docs.convex.dev/eslint
|
||||
- https://www.npmjs.com/package/@convex-dev/eslint-plugin
|
||||
|
||||
## Setup
|
||||
|
||||
Install the plugin:
|
||||
|
||||
```bash
|
||||
npm i @convex-dev/eslint-plugin --save-dev
|
||||
```
|
||||
|
||||
Configure `eslint.config.js`:
|
||||
|
||||
```javascript
|
||||
import { defineConfig } from "eslint/config";
|
||||
import convexPlugin from "@convex-dev/eslint-plugin";
|
||||
|
||||
export default defineConfig([...convexPlugin.configs.recommended]);
|
||||
```
|
||||
|
||||
## Rules
|
||||
|
||||
### 1. no-old-registered-function-syntax
|
||||
|
||||
Always use object syntax with a `handler` property.
|
||||
|
||||
```typescript
|
||||
// Correct
|
||||
export const list = query({
|
||||
args: {},
|
||||
handler: async (ctx) => {
|
||||
return await ctx.db.query("messages").collect();
|
||||
},
|
||||
});
|
||||
|
||||
// Wrong - bare function syntax
|
||||
export const list = query(async (ctx) => {
|
||||
return await ctx.db.query("messages").collect();
|
||||
});
|
||||
```
|
||||
|
||||
### 2. require-argument-validators
|
||||
|
||||
Always include `args` object, even when empty.
|
||||
|
||||
```typescript
|
||||
// Correct - with arguments
|
||||
export const get = query({
|
||||
args: { id: v.id("messages") },
|
||||
handler: async (ctx, { id }) => {
|
||||
return await ctx.db.get("messages", id);
|
||||
},
|
||||
});
|
||||
|
||||
// Correct - no arguments
|
||||
export const listAll = query({
|
||||
args: {},
|
||||
handler: async (ctx) => {
|
||||
return await ctx.db.query("messages").collect();
|
||||
},
|
||||
});
|
||||
|
||||
// Wrong - missing args
|
||||
export const get = query({
|
||||
handler: async (ctx, { id }: { id: Id<"messages"> }) => {
|
||||
return await ctx.db.get("messages", id);
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### 3. explicit-table-ids
|
||||
|
||||
Use explicit table names in all database operations (Convex 1.31.0+).
|
||||
|
||||
```typescript
|
||||
// Correct
|
||||
const message = await ctx.db.get("messages", messageId);
|
||||
await ctx.db.patch("messages", messageId, { text: "updated" });
|
||||
await ctx.db.replace("messages", messageId, {
|
||||
text: "replaced",
|
||||
author: "Alice",
|
||||
});
|
||||
await ctx.db.delete("messages", messageId);
|
||||
|
||||
// Wrong - implicit table from ID type
|
||||
const message = await ctx.db.get(messageId);
|
||||
await ctx.db.patch(messageId, { text: "updated" });
|
||||
await ctx.db.replace(messageId, { text: "replaced", author: "Alice" });
|
||||
await ctx.db.delete(messageId);
|
||||
```
|
||||
|
||||
Migration codemod available:
|
||||
|
||||
```bash
|
||||
npx @convex-dev/codemod@latest explicit-ids
|
||||
```
|
||||
|
||||
### 4. import-wrong-runtime
|
||||
|
||||
Never import Node.js runtime files into Convex runtime files.
|
||||
|
||||
```typescript
|
||||
// convex/queries.ts (no "use node" directive)
|
||||
|
||||
// Correct - importing from Convex runtime file
|
||||
import { helper } from "./utils"; // utils.ts has no "use node"
|
||||
|
||||
// Wrong - importing from Node runtime file
|
||||
import { nodeHelper } from "./nodeUtils"; // nodeUtils.ts has "use node"
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. Run ESLint before committing: `npx eslint convex/`
|
||||
2. Use auto-fix for quick migrations: `npx eslint convex/ --fix`
|
||||
3. Add to CI pipeline to catch violations early
|
||||
4. Configure your editor for real-time feedback
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Rule | What it enforces |
|
||||
| ----------------------------------- | --------------------------------- |
|
||||
| `no-old-registered-function-syntax` | Object syntax with `handler` |
|
||||
| `require-argument-validators` | `args: {}` on all functions |
|
||||
| `explicit-table-ids` | Table name in db operations |
|
||||
| `import-wrong-runtime` | No Node imports in Convex runtime |
|
||||
|
||||
## References
|
||||
|
||||
- [ESLint Plugin Docs](https://docs.convex.dev/eslint)
|
||||
- [Explicit IDs Announcement](https://news.convex.dev/db-table-name/)
|
||||
@@ -11,6 +11,16 @@ tags: [convex, functions, queries, mutations, actions, http]
|
||||
|
||||
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 [convex-eslint](../convex-eslint/SKILL.md) for linting setup.
|
||||
|
||||
## Documentation Sources
|
||||
|
||||
Before implementing, do not assume; fetch the latest documentation:
|
||||
@@ -26,12 +36,12 @@ Before implementing, do not assume; fetch the latest documentation:
|
||||
|
||||
### 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 |
|
||||
| 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
|
||||
|
||||
@@ -50,22 +60,24 @@ export const getUser = query({
|
||||
name: v.string(),
|
||||
email: v.string(),
|
||||
}),
|
||||
v.null()
|
||||
v.null(),
|
||||
),
|
||||
handler: async (ctx, args) => {
|
||||
return await ctx.db.get(args.userId);
|
||||
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(),
|
||||
})),
|
||||
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")
|
||||
@@ -93,7 +105,7 @@ export const createTask = mutation({
|
||||
returns: v.id("tasks"),
|
||||
handler: async (ctx, args) => {
|
||||
// Validate user exists
|
||||
const user = await ctx.db.get(args.userId);
|
||||
const user = await ctx.db.get("users", args.userId);
|
||||
if (!user) {
|
||||
throw new ConvexError("User not found");
|
||||
}
|
||||
@@ -111,7 +123,7 @@ export const deleteTask = mutation({
|
||||
args: { taskId: v.id("tasks") },
|
||||
returns: v.null(),
|
||||
handler: async (ctx, args) => {
|
||||
await ctx.db.delete(args.taskId);
|
||||
await ctx.db.delete("tasks", args.taskId);
|
||||
return null;
|
||||
},
|
||||
});
|
||||
@@ -154,7 +166,7 @@ export const processOrder = action({
|
||||
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");
|
||||
}
|
||||
@@ -218,8 +230,8 @@ http.route({
|
||||
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">
|
||||
const user = await ctx.runQuery(api.users.get, {
|
||||
userId: userId as Id<"users">,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
@@ -238,7 +250,11 @@ export default http;
|
||||
Use internal functions for sensitive operations:
|
||||
|
||||
```typescript
|
||||
import { internalMutation, internalQuery, internalAction } from "./_generated/server";
|
||||
import {
|
||||
internalMutation,
|
||||
internalQuery,
|
||||
internalAction,
|
||||
} from "./_generated/server";
|
||||
import { v } from "convex/values";
|
||||
|
||||
// Only callable from other Convex functions
|
||||
@@ -249,10 +265,10 @@ export const _updateUserCredits = internalMutation({
|
||||
},
|
||||
returns: v.null(),
|
||||
handler: async (ctx, args) => {
|
||||
const user = await ctx.db.get(args.userId);
|
||||
const user = await ctx.db.get("users", args.userId);
|
||||
if (!user) return null;
|
||||
|
||||
await ctx.db.patch(args.userId, {
|
||||
await ctx.db.patch("users", args.userId, {
|
||||
credits: (user.credits || 0) + args.amount,
|
||||
});
|
||||
return null;
|
||||
@@ -298,7 +314,7 @@ export const scheduleReminder = mutation({
|
||||
return await ctx.scheduler.runAfter(
|
||||
args.delayMs,
|
||||
internal.notifications.sendReminder,
|
||||
{ userId: args.userId, message: args.message }
|
||||
{ userId: args.userId, message: args.message },
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
+44
-16
@@ -59,7 +59,11 @@ convex/
|
||||
// QUERY: Read-only, reactive, cached. Use for fetching data.
|
||||
export const list = query({
|
||||
args: { limit: v.optional(v.number()) },
|
||||
returns: v.array(v.object({ /* ... */ })),
|
||||
returns: v.array(
|
||||
v.object({
|
||||
/* ... */
|
||||
}),
|
||||
),
|
||||
handler: async (ctx, args) => {
|
||||
return await ctx.db.query("items").take(args.limit ?? 10);
|
||||
},
|
||||
@@ -79,8 +83,12 @@ export const processExternal = action({
|
||||
args: { id: v.id("items") },
|
||||
handler: async (ctx, args) => {
|
||||
const data = await ctx.runQuery(internal.items.get, { id: args.id });
|
||||
const result = await fetch("https://api.example.com", { /* ... */ });
|
||||
await ctx.runMutation(internal.items.saveResult, { /* ... */ });
|
||||
const result = await fetch("https://api.example.com", {
|
||||
/* ... */
|
||||
});
|
||||
await ctx.runMutation(internal.items.saveResult, {
|
||||
/* ... */
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
@@ -151,7 +159,9 @@ const posts = await ctx.db
|
||||
|
||||
```typescript
|
||||
export const myMutation = mutation({
|
||||
args: { /* ... */ },
|
||||
args: {
|
||||
/* ... */
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
const identity = await ctx.auth.getUserIdentity();
|
||||
if (!identity) throw new Error("Unauthenticated");
|
||||
@@ -189,6 +199,17 @@ export const getUrl = query({
|
||||
});
|
||||
```
|
||||
|
||||
## Linting
|
||||
|
||||
This project uses @convex-dev/eslint-plugin for code quality. All Convex functions must:
|
||||
|
||||
1. **Use object syntax** with `handler` property (not bare functions)
|
||||
2. **Include argument validators** - always have `args: {}` even when empty
|
||||
3. **Use explicit table names** - `db.get("table", id)` not `db.get(id)`
|
||||
4. **Respect runtime boundaries** - don't import "use node" files into Convex runtime
|
||||
|
||||
Run `npx eslint convex/` to check compliance.
|
||||
|
||||
## IMPORTANT Rules
|
||||
|
||||
1. **ALWAYS validate arguments** on public functions with `v` validators
|
||||
@@ -239,24 +260,31 @@ HTTP webhook/API? → httpAction()
|
||||
### Validator Cheat Sheet
|
||||
|
||||
```typescript
|
||||
v.string() // string
|
||||
v.number() // number (float64)
|
||||
v.boolean() // boolean
|
||||
v.id("table") // Id<"table">
|
||||
v.null() // null
|
||||
v.optional(v.string()) // string | undefined (field can be missing)
|
||||
v.nullable(v.string()) // string | null (field must exist)
|
||||
v.array(v.string()) // string[]
|
||||
v.object({ k: v.string() }) // { k: string }
|
||||
v.union(v.literal("a"), v.literal("b")) // "a" | "b"
|
||||
v.record(v.string(), v.number()) // { [key: string]: number }
|
||||
v.string(); // string
|
||||
v.number(); // number (float64)
|
||||
v.boolean(); // boolean
|
||||
v.id("table"); // Id<"table">
|
||||
v.null(); // null
|
||||
v.optional(v.string()); // string | undefined (field can be missing)
|
||||
v.nullable(v.string()); // string | null (field must exist)
|
||||
v.array(v.string()); // string[]
|
||||
v.object({ k: v.string() }); // { k: string }
|
||||
v.union(v.literal("a"), v.literal("b")); // "a" | "b"
|
||||
v.record(v.string(), v.number()); // { [key: string]: number }
|
||||
```
|
||||
|
||||
### Import Patterns
|
||||
|
||||
```typescript
|
||||
// Function constructors
|
||||
import { query, mutation, action, internalQuery, internalMutation, internalAction } from "./_generated/server";
|
||||
import {
|
||||
query,
|
||||
mutation,
|
||||
action,
|
||||
internalQuery,
|
||||
internalMutation,
|
||||
internalAction,
|
||||
} from "./_generated/server";
|
||||
|
||||
// API references
|
||||
import { api, internal } from "./_generated/api";
|
||||
|
||||
Reference in New Issue
Block a user