VibeFast
Backend & Deployment

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:

TypePurposeCan Modify Data?
QueriesRead data from the database. They are fast and automatically reactive.No
MutationsWrite, update, or delete data. They are transactional and atomic.Yes
ActionsRun 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.

convex/schema.ts
// ...
  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.

convex/recordingFunctions.ts
// ... 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.

src/features/voice-notes/components/recording-list-item.tsx
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,
  });
};