Backend Essentials
Learn the basics of working with your Convex backend, including how to modify the database schema.
Your VibeFast backend is powered entirely by Convex, which provides a real-time database and serverless functions in one seamless package. All your backend code lives in the convex/
directory.
Queries, Mutations, and Actions
Convex functions are organized into three types:
Type | Purpose | Can Modify Data? |
---|---|---|
Queries | Read data from the database. They are fast and automatically reactive. | No |
Mutations | Write, update, or delete data. They are transactional and atomic. | Yes |
Actions | Run server-side code with side effects, like calling third-party APIs. | Yes |
How-To: Add a New Field to a Table
Let's walk through a common task: adding a new isFavorite
field to the recordings
table to allow users to favorite voice notes.
Step 1: Modify the Schema
The single source of truth for your database is convex/schema.ts
. Open this file and find the recordings
table definition.
Add the new field to the table definition. We'll make it an optional boolean.
// ...
recordings: defineTable({
userId: v.id('users'),
name: v.optional(v.string()),
fileUri: v.string(),
duration: v.number(),
createdAt: v.number(),
status: v.union(v.literal('draft'), v.literal('completed')),
metering: v.optional(v.array(v.number())),
isFavorite: v.optional(v.boolean()), // <-- ADD THIS LINE
}).index('by_userId', ['userId']),
// ...
Once you save this file, the npx convex dev
process in your terminal will automatically push the schema change to your backend.
Step 2: Create a Mutation to Update the Field
Now, we need a way to change the value of isFavorite
. Open convex/recordingFunctions.ts
and add a new mutation.
// ... other imports
import { mutation } from './_generated/server';
// ... other functions
export const toggleFavoriteStatus = mutation({
args: {
recordingId: v.id('recordings'),
isFavorite: v.boolean(),
},
handler: async (ctx, { recordingId, isFavorite }) => {
const userId = await getAuthUserId(ctx);
if (!userId) {
throw new Error('Not authenticated');
}
// Optional: Verify ownership
const recording = await ctx.db.get(recordingId);
if (recording?.userId !== userId) {
throw new Error('Not authorized');
}
await ctx.db.patch(recordingId, { isFavorite });
},
});
Step 3: Use the New Mutation in the Frontend
Finally, you can call this new mutation from any component in your frontend using the useMutation
hook.
import { useMutation } from 'convex/react';
import { api } from 'convex/_generated/api';
// ... inside your component
const toggleFavorite = useMutation(api.recordingFunctions.toggleFavoriteStatus);
const handleFavoritePress = () => {
toggleFavorite({
recordingId: recording._id,
isFavorite: !recording.isFavorite,
});
};