- Convex Function Creator
- Generate secure, type-safe Convex functions following all best practices.
- When to Use
- Creating new query functions (read data)
- Creating new mutation functions (write data)
- Creating new action functions (external APIs, long-running)
- Adding API endpoints to your Convex backend
- Function Types
- Queries (Read-Only)
- Can only read from database
- Cannot modify data or call external APIs
- Cached and reactive
- Run in transactions
- import
- {
- query
- }
- from
- "./_generated/server"
- ;
- import
- {
- v
- }
- from
- "convex/values"
- ;
- export
- const
- getTask
- =
- query
- (
- {
- args
- :
- {
- taskId
- :
- v
- .
- id
- (
- "tasks"
- )
- }
- ,
- returns
- :
- v
- .
- union
- (
- v
- .
- object
- (
- {
- _id
- :
- v
- .
- id
- (
- "tasks"
- )
- ,
- text
- :
- v
- .
- string
- (
- )
- ,
- completed
- :
- v
- .
- boolean
- (
- )
- ,
- }
- )
- ,
- v
- .
- null
- (
- )
- )
- ,
- handler
- :
- async
- (
- ctx
- ,
- args
- )
- =>
- {
- return
- await
- ctx
- .
- db
- .
- get
- (
- args
- .
- taskId
- )
- ;
- }
- ,
- }
- )
- ;
- Mutations (Transactional Writes)
- Can read and write to database
- Cannot call external APIs
- Run in ACID transactions
- Automatic retries on conflicts
- import
- {
- mutation
- }
- from
- "./_generated/server"
- ;
- import
- {
- v
- }
- from
- "convex/values"
- ;
- export
- const
- createTask
- =
- mutation
- (
- {
- args
- :
- {
- text
- :
- v
- .
- string
- (
- )
- ,
- priority
- :
- v
- .
- optional
- (
- v
- .
- union
- (
- v
- .
- literal
- (
- "low"
- )
- ,
- v
- .
- literal
- (
- "medium"
- )
- ,
- v
- .
- literal
- (
- "high"
- )
- )
- )
- ,
- }
- ,
- returns
- :
- v
- .
- id
- (
- "tasks"
- )
- ,
- handler
- :
- async
- (
- ctx
- ,
- args
- )
- =>
- {
- const
- identity
- =
- await
- ctx
- .
- auth
- .
- getUserIdentity
- (
- )
- ;
- if
- (
- !
- identity
- )
- throw
- new
- Error
- (
- "Not authenticated"
- )
- ;
- return
- await
- ctx
- .
- db
- .
- insert
- (
- "tasks"
- ,
- {
- text
- :
- args
- .
- text
- ,
- priority
- :
- args
- .
- priority
- ??
- "medium"
- ,
- completed
- :
- false
- ,
- createdAt
- :
- Date
- .
- now
- (
- )
- ,
- }
- )
- ;
- }
- ,
- }
- )
- ;
- Actions (External + Non-Transactional)
- Can call external APIs (fetch, AI, etc.)
- Can call mutations via
- ctx.runMutation
- Cannot directly access database
- No automatic retries
- Use
- "use node"
- directive when needing Node.js APIs
- Important:
- If your action needs Node.js-specific APIs (crypto, third-party SDKs, etc.), add
- "use node"
- at the top of the file. Files with
- "use node"
- can ONLY contain actions, not queries or mutations.
- "use node"
- ;
- // Required for Node.js APIs like OpenAI SDK
- import
- {
- action
- }
- from
- "./_generated/server"
- ;
- import
- {
- api
- }
- from
- "./_generated/api"
- ;
- import
- {
- v
- }
- from
- "convex/values"
- ;
- import
- OpenAI
- from
- "openai"
- ;
- const
- openai
- =
- new
- OpenAI
- (
- {
- apiKey
- :
- process
- .
- env
- .
- OPENAI_API_KEY
- }
- )
- ;
- export
- const
- generateTaskSuggestion
- =
- action
- (
- {
- args
- :
- {
- prompt
- :
- v
- .
- string
- (
- )
- }
- ,
- returns
- :
- v
- .
- string
- (
- )
- ,
- handler
- :
- async
- (
- ctx
- ,
- args
- )
- =>
- {
- const
- identity
- =
- await
- ctx
- .
- auth
- .
- getUserIdentity
- (
- )
- ;
- if
- (
- !
- identity
- )
- throw
- new
- Error
- (
- "Not authenticated"
- )
- ;
- // Call OpenAI (requires "use node")
- const
- completion
- =
- await
- openai
- .
- chat
- .
- completions
- .
- create
- (
- {
- model
- :
- "gpt-4"
- ,
- messages
- :
- [
- {
- role
- :
- "user"
- ,
- content
- :
- args
- .
- prompt
- }
- ]
- ,
- }
- )
- ;
- const
- suggestion
- =
- completion
- .
- choices
- [
- 0
- ]
- .
- message
- .
- content
- ;
- // Write to database via mutation
- await
- ctx
- .
- runMutation
- (
- api
- .
- tasks
- .
- createTask
- ,
- {
- text
- :
- suggestion
- ,
- }
- )
- ;
- return
- suggestion
- ;
- }
- ,
- }
- )
- ;
- Note:
- If you only need basic fetch (no Node.js APIs), you can omit
- "use node"
- . But for third-party SDKs, crypto, or other Node.js features, you must use it.
- Required Components
- 1. Argument Validation
- Always
- define
- args
- with validators:
- args
- :
- {
- id
- :
- v
- .
- id
- (
- "tasks"
- )
- ,
- text
- :
- v
- .
- string
- (
- )
- ,
- count
- :
- v
- .
- number
- (
- )
- ,
- enabled
- :
- v
- .
- boolean
- (
- )
- ,
- tags
- :
- v
- .
- array
- (
- v
- .
- string
- (
- )
- )
- ,
- metadata
- :
- v
- .
- optional
- (
- v
- .
- object
- (
- {
- key
- :
- v
- .
- string
- (
- )
- ,
- }
- )
- )
- ,
- }
- 2. Return Type Validation
- Always
- define
- returns
- :
- returns
- :
- v
- .
- object
- (
- {
- _id
- :
- v
- .
- id
- (
- "tasks"
- )
- ,
- text
- :
- v
- .
- string
- (
- )
- ,
- }
- )
- // Or for arrays
- returns
- :
- v
- .
- array
- (
- v
- .
- object
- (
- {
- / ... /
- }
- )
- )
- // Or for nullable
- returns
- :
- v
- .
- union
- (
- v
- .
- object
- (
- {
- / ... /
- }
- )
- ,
- v
- .
- null
- (
- )
- )
- 3. Authentication Check
- Always
- verify auth in public functions:
- const
- identity
- =
- await
- ctx
- .
- auth
- .
- getUserIdentity
- (
- )
- ;
- if
- (
- !
- identity
- )
- {
- throw
- new
- Error
- (
- "Not authenticated"
- )
- ;
- }
- 4. Authorization Check
- Always
- verify ownership/permissions:
- const
- task
- =
- await
- ctx
- .
- db
- .
- get
- (
- args
- .
- taskId
- )
- ;
- if
- (
- !
- task
- )
- {
- throw
- new
- Error
- (
- "Task not found"
- )
- ;
- }
- if
- (
- task
- .
- userId
- !==
- user
- .
- _id
- )
- {
- throw
- new
- Error
- (
- "Unauthorized"
- )
- ;
- }
- Complete Examples
- Secure Query with Auth
- export
- const
- getMyTasks
- =
- query
- (
- {
- args
- :
- {
- status
- :
- v
- .
- optional
- (
- v
- .
- union
- (
- v
- .
- literal
- (
- "active"
- )
- ,
- v
- .
- literal
- (
- "completed"
- )
- )
- )
- ,
- }
- ,
- returns
- :
- v
- .
- array
- (
- v
- .
- object
- (
- {
- _id
- :
- v
- .
- id
- (
- "tasks"
- )
- ,
- text
- :
- v
- .
- string
- (
- )
- ,
- completed
- :
- v
- .
- boolean
- (
- )
- ,
- }
- )
- )
- ,
- handler
- :
- async
- (
- ctx
- ,
- args
- )
- =>
- {
- const
- identity
- =
- await
- ctx
- .
- auth
- .
- getUserIdentity
- (
- )
- ;
- if
- (
- !
- identity
- )
- throw
- new
- Error
- (
- "Not authenticated"
- )
- ;
- const
- user
- =
- await
- ctx
- .
- db
- .
- query
- (
- "users"
- )
- .
- withIndex
- (
- "by_token"
- ,
- q
- =>
- q
- .
- eq
- (
- "tokenIdentifier"
- ,
- identity
- .
- tokenIdentifier
- )
- )
- .
- unique
- (
- )
- ;
- if
- (
- !
- user
- )
- throw
- new
- Error
- (
- "User not found"
- )
- ;
- let
- query
- =
- ctx
- .
- db
- .
- query
- (
- "tasks"
- )
- .
- withIndex
- (
- "by_user"
- ,
- q
- =>
- q
- .
- eq
- (
- "userId"
- ,
- user
- .
- _id
- )
- )
- ;
- const
- tasks
- =
- await
- query
- .
- collect
- (
- )
- ;
- if
- (
- args
- .
- status
- )
- {
- return
- tasks
- .
- filter
- (
- t
- =>
- args
- .
- status
- ===
- "completed"
- ?
- t
- .
- completed
- :
- !
- t
- .
- completed
- )
- ;
- }
- return
- tasks
- ;
- }
- ,
- }
- )
- ;
- Secure Mutation with Validation
- export
- const
- updateTask
- =
- mutation
- (
- {
- args
- :
- {
- taskId
- :
- v
- .
- id
- (
- "tasks"
- )
- ,
- text
- :
- v
- .
- optional
- (
- v
- .
- string
- (
- )
- )
- ,
- completed
- :
- v
- .
- optional
- (
- v
- .
- boolean
- (
- )
- )
- ,
- }
- ,
- returns
- :
- v
- .
- id
- (
- "tasks"
- )
- ,
- handler
- :
- async
- (
- ctx
- ,
- args
- )
- =>
- {
- // 1. Authentication
- const
- identity
- =
- await
- ctx
- .
- auth
- .
- getUserIdentity
- (
- )
- ;
- if
- (
- !
- identity
- )
- throw
- new
- Error
- (
- "Not authenticated"
- )
- ;
- // 2. Get user
- const
- user
- =
- await
- ctx
- .
- db
- .
- query
- (
- "users"
- )
- .
- withIndex
- (
- "by_token"
- ,
- q
- =>
- q
- .
- eq
- (
- "tokenIdentifier"
- ,
- identity
- .
- tokenIdentifier
- )
- )
- .
- unique
- (
- )
- ;
- if
- (
- !
- user
- )
- throw
- new
- Error
- (
- "User not found"
- )
- ;
- // 3. Get resource
- const
- task
- =
- await
- ctx
- .
- db
- .
- get
- (
- args
- .
- taskId
- )
- ;
- if
- (
- !
- task
- )
- throw
- new
- Error
- (
- "Task not found"
- )
- ;
- // 4. Authorization
- if
- (
- task
- .
- userId
- !==
- user
- .
- _id
- )
- {
- throw
- new
- Error
- (
- "Unauthorized"
- )
- ;
- }
- // 5. Update
- const
- updates
- :
- Partial
- <
- any
- >
- =
- {
- }
- ;
- if
- (
- args
- .
- text
- !==
- undefined
- )
- updates
- .
- text
- =
- args
- .
- text
- ;
- if
- (
- args
- .
- completed
- !==
- undefined
- )
- updates
- .
- completed
- =
- args
- .
- completed
- ;
- await
- ctx
- .
- db
- .
- patch
- (
- args
- .
- taskId
- ,
- updates
- )
- ;
- return
- args
- .
- taskId
- ;
- }
- ,
- }
- )
- ;
- Action Calling External API
- Create separate file for actions that need Node.js:
- // convex/taskActions.ts
- "use node"
- ;
- // Required for SendGrid SDK
- import
- {
- action
- }
- from
- "./_generated/server"
- ;
- import
- {
- api
- }
- from
- "./_generated/api"
- ;
- import
- {
- v
- }
- from
- "convex/values"
- ;
- import
- sendgrid
- from
- "@sendgrid/mail"
- ;
- sendgrid
- .
- setApiKey
- (
- process
- .
- env
- .
- SENDGRID_API_KEY
- )
- ;
- export
- const
- sendTaskReminder
- =
- action
- (
- {
- args
- :
- {
- taskId
- :
- v
- .
- id
- (
- "tasks"
- )
- }
- ,
- returns
- :
- v
- .
- boolean
- (
- )
- ,
- handler
- :
- async
- (
- ctx
- ,
- args
- )
- =>
- {
- // 1. Auth
- const
- identity
- =
- await
- ctx
- .
- auth
- .
- getUserIdentity
- (
- )
- ;
- if
- (
- !
- identity
- )
- throw
- new
- Error
- (
- "Not authenticated"
- )
- ;
- // 2. Get data via query
- const
- task
- =
- await
- ctx
- .
- runQuery
- (
- api
- .
- tasks
- .
- getTask
- ,
- {
- taskId
- :
- args
- .
- taskId
- ,
- }
- )
- ;
- if
- (
- !
- task
- )
- throw
- new
- Error
- (
- "Task not found"
- )
- ;
- // 3. Call external service (using Node.js SDK)
- await
- sendgrid
- .
- send
- (
- {
- to
- :
- identity
- .
- ,
- from
- :
- "noreply@example.com"
- ,
- subject
- :
- "Task Reminder"
- ,
- text
- :
- `
- Don't forget:
- ${
- task
- .
- text
- }
- `
- ,
- }
- )
- ;
- // 4. Update via mutation
- await
- ctx
- .
- runMutation
- (
- api
- .
- tasks
- .
- markReminderSent
- ,
- {
- taskId
- :
- args
- .
- taskId
- ,
- }
- )
- ;
- return
- true
- ;
- }
- ,
- }
- )
- ;
- Note:
- Keep queries and mutations in
- convex/tasks.ts
- (without "use node"), and actions that need Node.js in
- convex/taskActions.ts
- (with "use node").
- Internal Functions
- For backend-only functions (called by scheduler, other functions):
- import
- {
- internalMutation
- }
- from
- "./_generated/server"
- ;
- export
- const
- processExpiredTasks
- =
- internalMutation
- (
- {
- args
- :
- {
- }
- ,
- handler
- :
- async
- (
- ctx
- )
- =>
- {
- // No auth needed - only callable from backend
- const
- now
- =
- Date
- .
- now
- (
- )
- ;
- const
- expired
- =
- await
- ctx
- .
- db
- .
- query
- (
- "tasks"
- )
- .
- withIndex
- (
- "by_due_date"
- ,
- q
- =>
- q
- .
- lt
- (
- "dueDate"
- ,
- now
- )
- )
- .
- collect
- (
- )
- ;
- for
- (
- const
- task
- of
- expired
- )
- {
- await
- ctx
- .
- db
- .
- patch
- (
- task
- .
- _id
- ,
- {
- status
- :
- "expired"
- }
- )
- ;
- }
- }
- ,
- }
- )
- ;
- Checklist
- args
- defined with validators
- returns
- defined with validator
- Authentication check (
- ctx.auth.getUserIdentity()
- )
- Authorization check (ownership/permissions)
- All promises awaited
- Indexed queries (no
- .filter()
- on queries)
- Error handling with descriptive messages
- Scheduled functions use
- internal.*
- not
- api.*
- If using Node.js APIs:
- "use node"
- at top of file
- If file has
- "use node"
- Only actions (no queries/mutations) Actions in separate file from queries/mutations when using "use node"
function-creator
安装
npx skills add https://github.com/get-convex/agent-skills --skill function-creator