Backend
Mutations
Write data to your Convex database with mutations.
Mutations write data to your database. They are transactional and run on the server.
Creating Documents
Use ctx.db.insert() to create new documents:
convex/tasks.ts
import { v } from 'convex/values'
import { mutation } from './_generated/server'
export const add = mutation({
args: { title: v.string(), userId: v.string() },
handler: async (ctx, { title, userId }) => {
return await ctx.db.insert('tasks', {
title,
userId,
isCompleted: false,
createdAt: Date.now(),
})
},
})
This mutation returns the new document's ID.
Updating Documents
Use ctx.db.patch() to update specific fields:
convex/tasks.ts
export const toggle = mutation({
args: { id: v.id('tasks') },
handler: async (ctx, { id }) => {
const task = await ctx.db.get(id)
if (task) {
await ctx.db.patch(id, { isCompleted: !task.isCompleted })
}
},
})
Use ctx.db.replace() to replace the entire document:
convex/tasks.ts
export const update = mutation({
args: { id: v.id('tasks'), title: v.string(), isCompleted: v.boolean() },
handler: async (ctx, { id, title, isCompleted }) => {
await ctx.db.replace(id, { title, isCompleted, createdAt: Date.now() })
},
})
Deleting Documents
Use ctx.db.delete() to remove documents:
convex/tasks.ts
export const remove = mutation({
args: { id: v.id('tasks') },
handler: async (ctx, { id }) => {
await ctx.db.delete(id)
},
})
Using in Components
Call mutations from Vue components with useConvexMutation:
app/components/AddTask.vue
<script setup lang="ts">
import { api } from '#convex/api'
const { mutate: addTask, isLoading } = useConvexMutation(api.tasks.add)
const title = ref('')
async function handleSubmit() {
await addTask({ title: title.value, userId: 'user_123' })
title.value = ''
}
</script>
<template>
<form @submit.prevent="handleSubmit">
<input v-model="title" :disabled="isLoading" placeholder="New task...">
<button :disabled="isLoading">
Add
</button>
</form>
</template>
See useConvexMutation for full documentation.
Transactions
Mutations are transactional. All database operations within a mutation either succeed together or fail together.
All operations in a mutation are atomic. If any operation fails, all changes roll back automatically.
You can read and write multiple documents atomically:
convex/tasks.ts
export const completeAll = mutation({
args: { userId: v.string() },
handler: async (ctx, { userId }) => {
const tasks = await ctx.db
.query('tasks')
.withIndex('by_user', q => q.eq('userId', userId))
.collect()
for (const task of tasks) {
await ctx.db.patch(task._id, { isCompleted: true })
}
},
})