> ## Documentation Index
> Fetch the complete documentation index at: https://vlyai-1c28d863.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Convex overview

> The reactive backend that powers every vly app. Schema, queries, mutations, actions, scheduled functions, file storage, vector search, full-text search.

[Convex](https://www.convex.dev) is the backend platform every vly app runs on. This deep-dive explains the core concepts, the function model, and the patterns vly uses.

If you've used Firebase, Supabase, or Postgres, Convex will feel familiar but more cohesive — schema, functions, auth, file storage, and real-time sync are one system, not stitched together.

## The mental model

```mermaid theme={null}
graph LR
    Client[React client]
    Schema[Schema<br/>defineSchema + defineTable]
    Q[Queries<br/>read]
    M[Mutations<br/>write]
    A[Actions<br/>side-effects]
    SCH[Scheduled<br/>functions]
    DB[(Document DB)]
    EX[External APIs<br/>Stripe / OpenAI / etc]

    Client -->|useQuery| Q
    Client -->|useMutation| M
    Client -->|useAction| A
    Q --> DB
    M --> DB
    A --> EX
    A -->|run mutation| M
    A -->|run query| Q
    SCH -->|run mutation| M
    SCH -->|run action| A

    style Client fill:#EDE9FE,stroke:#6D28D9
    style DB fill:#CFFAFE,stroke:#06B6D4
    style EX fill:#FFEDD5,stroke:#FF6600
```

Five concepts, in order of how often you'll touch them:

<Steps>
  <Step title="Schema — what your data looks like" icon="database">
    Tables, fields, types, indexes. TypeScript-first. See [Schema](/convex/schema).
  </Step>

  <Step title="Queries — read data, reactively" icon="search">
    Pure functions that read the DB. Clients subscribe; updates are automatic. See [Queries](/convex/queries).
  </Step>

  <Step title="Mutations — write data, transactionally" icon="pencil">
    Write functions with ACID guarantees. Cannot call third-party APIs. See [Mutations](/convex/mutations).
  </Step>

  <Step title="Actions — side-effects and third-party calls" icon="zap">
    For anything that touches the outside world: API calls, email, file processing. See [Actions](/convex/actions).
  </Step>

  <Step title="Scheduled functions — cron and one-shot" icon="clock">
    Run a mutation or action at a specific time, or on a recurring schedule. See [Scheduled functions](/convex/scheduled-functions).
  </Step>
</Steps>

## Why reactive queries change everything

Convex queries are **subscriptions**. When a client calls a query via `useQuery`, the result auto-updates whenever any data the query depends on changes. No polling. No `refetch`. No manual cache invalidation.

```tsx theme={null}
const tasks = useQuery(api.tasks.list, { teamId });
// tasks updates automatically when:
// - any new task is added to this team
// - any task in this team is edited or deleted
// - the team changes
```

This is why vly apps feel "live" by default. Building a chat, a multiplayer game, a dashboard, or a collaborative editor takes 90% less plumbing because the sync layer is automatic.

## The function types in detail

| Type | Reads | Writes | External APIs | Use for |
| - | - | - | - | - |
| Query | ✓ | ✗ | ✗ | Anything the UI displays |
| Mutation | ✓ | ✓ | ✗ | Saving / updating / deleting data |
| Action | ✓ (via runQuery) | ✓ (via runMutation) | ✓ | Calling Stripe / Resend / OpenAI / etc. |
| Scheduled | (calls mutation or action) | — | — | Cron, retries, cleanups, reminders |

The split between mutations and actions is intentional: mutations are transactional and cannot call the network (so they always commit cleanly). Actions can call the network but aren't transactional (because external APIs aren't either).

## A complete worked example

Schema:

```typescript convex/schema.ts theme={null}
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";

export default defineSchema({
  tasks: defineTable({
    title: v.string(),
    completed: v.boolean(),
    ownerId: v.id("users"),
    dueAt: v.optional(v.number()),
  })
    .index("by_owner", ["ownerId"])
    .index("by_owner_and_completed", ["ownerId", "completed"]),

  users: defineTable({
    email: v.string(),
    name: v.string(),
    avatarUrl: v.optional(v.string()),
    createdAt: v.number(),
  }).index("by_email", ["email"]),
});
```

A query — listing tasks for an owner:

```typescript convex/tasks.ts theme={null}
import { query } from "./_generated/server";
import { v } from "convex/values";

export const listForOwner = query({
  args: { ownerId: v.id("users"), onlyOpen: v.optional(v.boolean()) },
  handler: async (ctx, { ownerId, onlyOpen }) => {
    const q = ctx.db
      .query("tasks")
      .withIndex(
        onlyOpen ? "by_owner_and_completed" : "by_owner",
        (q) => onlyOpen ? q.eq("ownerId", ownerId).eq("completed", false)
                        : q.eq("ownerId", ownerId),
      );
    return await q.collect();
  },
});
```

Call it from React:

```tsx theme={null}
const tasks = useQuery(api.tasks.listForOwner, { ownerId, onlyOpen: true });
```

A mutation — creating and toggling tasks:

```typescript convex/tasks.ts theme={null}
import { mutation } from "./_generated/server";
import { v } from "convex/values";

export const create = mutation({
  args: {
    title: v.string(),
    ownerId: v.id("users"),
    dueAt: v.optional(v.number()),
  },
  handler: async (ctx, args) => {
    return await ctx.db.insert("tasks", {
      ...args,
      completed: false,
    });
  },
});

export const toggle = mutation({
  args: { id: v.id("tasks") },
  handler: async (ctx, { id }) => {
    const task = await ctx.db.get(id);
    if (!task) throw new Error("Task not found");
    await ctx.db.patch(id, { completed: !task.completed });
  },
});
```

An action — calling Resend to send a welcome email:

```typescript convex/notifications.ts theme={null}
import { action } from "./_generated/server";
import { v } from "convex/values";

export const sendWelcomeEmail = action({
  args: { userId: v.id("users") },
  handler: async (ctx, { userId }) => {
    const user = await ctx.runQuery(api.users.get, { id: userId });
    if (!user) return;

    await fetch("https://api.resend.com/emails", {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${process.env.RESEND_API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        from: "hello@vly.ai",
        to: user.email,
        subject: "Welcome to vly",
        html: `<p>Hi ${user.name}, welcome aboard.</p>`,
      }),
    });
  },
});
```

Actions can call third-party APIs and use `process.env`. Queries and mutations cannot.

A React component — putting it all together:

```tsx components/Tasks.tsx theme={null}
import { useQuery, useMutation } from "convex/react";
import { api } from "../convex/_generated/api";

export function Tasks({ ownerId }: { ownerId: Id<"users"> }) {
  const tasks  = useQuery(api.tasks.listForOwner, { ownerId, onlyOpen: true });
  const create = useMutation(api.tasks.create);
  const toggle = useMutation(api.tasks.toggle);

  if (tasks === undefined) return <div>Loading…</div>;

  return (
    <div>
      <button onClick={() => create({ title: "New task", ownerId })}>
        Add task
      </button>
      <ul>
        {tasks.map((t) => (
          <li key={t._id}>
            <input
              type="checkbox"
              checked={t.completed}
              onChange={() => toggle({ id: t._id })}
            />
            {t.title}
          </li>
        ))}
      </ul>
    </div>
  );
}
```

Five files. Reactive sync is free — when one user toggles a task, every other user viewing this list sees the change.

## Beyond CRUD

Convex isn't just a database. The platform includes:

<CardGroup cols={2}>
  <Card title="File storage" icon="hard-drive" href="/convex/file-storage">
    Upload, store, and serve files. Image transformations included.
  </Card>

  <Card title="Vector search" icon="brain" href="/convex/vector-search">
    Semantic search via embeddings. Built on top of mutations and queries.
  </Card>

  <Card title="Full-text search" icon="search" href="/convex/full-text-search">
    BM25-style search across text fields. No external service.
  </Card>

  <Card title="Scheduled functions" icon="clock" href="/convex/scheduled-functions">
    Cron-style or delayed one-shot execution.
  </Card>

  <Card title="Auth integration" icon="lock" href="/convex/auth-integration">
    `ctx.auth.getUserIdentity()` is available everywhere.
  </Card>

  <Card title="Migrations" icon="arrow-right-left" href="/convex/migrations">
    Schema evolution patterns and how vly handles them automatically.
  </Card>
</CardGroup>

## When to use which function type

A simple decision tree:

```mermaid theme={null}
flowchart TD
    A[I need to do something]
    A --> B{Touches the database?}
    B -->|No| C[Use a regular function or hook]
    B -->|Yes| D{Calls an external API?}
    D -->|Yes| E[Action]
    D -->|No| F{Reads or writes?}
    F -->|Read| G[Query]
    F -->|Write| H[Mutation]
    F -->|Both| I[Mutation<br/>or split into both]
    G --> J{On a schedule?}
    E --> J
    H --> J
    J -->|Yes| K[Wrap with cron / scheduled]
    J -->|No| L[Done]
    K --> L

    style E fill:#FFEDD5,stroke:#FF6600
    style G fill:#CFFAFE,stroke:#06B6D4
    style H fill:#EDE9FE,stroke:#6D28D9
```

vly picks the right type for you when you describe a feature. You only need to know the model when hand-editing.

## What vly handles automatically

When you describe a feature in vly, the agent:

* Picks the right function type
* Adds the right schema indexes
* Wires up `useQuery` / `useMutation` in components
* Adds auth checks
* Generates the migration script if a schema field changes

You can always override by hand in [Code mode](/building/code-mode).

## Limits to know

| Limit | Default |
| - | - |
| Document size | 1 MB |
| Query / mutation runtime | 10s |
| Action runtime | 10 min |
| File upload size | 100 MB |
| Concurrent subscribers per query | \~10,000 (soft) |

For higher limits, contact support.

## Next

<CardGroup cols={3}>
  <Card title="Schema" icon="database" href="/convex/schema">
    Defining tables, fields, indexes, validators.
  </Card>

  <Card title="Queries" icon="search" href="/convex/queries">
    Reactive reads, with the patterns vly uses.
  </Card>

  <Card title="Mutations" icon="pencil" href="/convex/mutations">
    Transactional writes.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.