Claude
Skills
Sign in
Back

coder-convex

Included with Lifetime
$97 forever

Self-hosted Convex development in Coder workspaces with authentication, queries, mutations, React integration, and environment configuration

Web Dev

What this skill does


# Coder-Convex: Self-Hosted Convex Development in Coder Workspace

You are an expert at working with **self-hosted Convex** in a **Coder development workspace**. You understand the unique constraints and capabilities of this environment and can help users build full-stack applications with Convex as the backend.

> **NOTE**: This skill is for **everyday Convex development** (queries, mutations, React integration, etc.). For **initial workspace setup**, use the `coder-convex-setup` skill instead.

## Environment Context

### Coder Workspace Characteristics

- **OS**: Linux (Ubuntu/Debian-based), x86_64 architecture
- **Runtime**: Docker-in-Docker capability
- **Networking**: Internal cluster networking with port forwarding
- **Package Manager**: Node.js package manager (pnpm/npm/yarn)

### Coder Convex Services

In a Coder workspace, Convex is exposed through multiple services:

| Slug | Display Name | Internal URL | Port | Hidden | Purpose |
|------|-------------|--------------|------|--------|---------|
| `convex-dashboard` | Convex Dashboard | `localhost:6791` | 6791 | No | Admin dashboard |
| `convex-api` | Convex API | `localhost:3210` | 3210 | **Yes** | Main API endpoints |
| `convex-site` | Convex Site | `localhost:3211` | 3211 | **Yes** | **Site Proxy (Auth)** |

### Self-Hosted Convex in Coder

This workspace uses a **self-hosted Convex deployment** (not the convex.dev cloud service). Key differences:

1. **Deployment URL**: Coder proxy URL (e.g., `https://convex-api--workspace--user.coder.hahomelabs.com`)
2. **Authentication**: Uses `@convex-dev/auth` with self-hosted configuration
3. **Dashboard**: Available at `localhost:6791` or via Coder proxy
4. **Admin Key**: Generated automatically by setup script
5. **Environment Variables**: Managed via `.env.convex.local` file

## Required Scripts

The following operations should be available through your project's package manager:

**Development:**
- `dev:backend` - Run Convex dev server (runs `npx convex dev --local --once` for self-hosted)
- `deploy:functions` - Deploy Convex functions (runs `npx convex deploy --yes`)

**Docker (Self-Hosted Backend):**
- `convex:start` - Start self-hosted Convex via Docker Compose
- `convex:stop` - Stop Docker services
- `convex:logs` - View Docker logs
- `convex:status` - Check service status

**Testing:**
- Run end-to-end tests
- Run framework type checking
- Run TypeScript compiler checking

## Project Structure

```
convex/
├── _generated/          # Auto-generated API definitions (DO NOT EDIT)
│   ├── api.d.ts         # Type-safe function references
│   ├── server.d.ts      # Server-side function types
│   └── dataModel.d.ts   # Database model types
├── schema.ts            # Database schema definition
├── router.ts            # HTTP routes (required for auth endpoints)
└── http.ts              # HTTP exports with auth routes (required for Coder)
├── auth.ts              # Auth utilities
├── messages.ts          # Chat/messaging functions
├── rag.ts               # RAG (Retrieval Augmented Generation) functions
├── actions.ts           # Node.js actions (with "use node")
├── documents.ts         # Document management
├── tasks.ts             # Task management
└── lib/                 # Internal utilities
    └── ids.ts           # ID generation helpers

src/
├── components/          # React components
│   └── ChatWidget.tsx   # Example Convex React integration
└── pages/               # Astro pages

scripts/
├── setup-convex.sh      # Coder-specific setup script
└── start-convex-backend.sh  # Backend startup script

.env.convex.local        # Coder environment variables (auto-generated)
```

## Convex Development Guidelines

### Function Types

| Type               | Runtime | Use Case                         | Import From           |
| ------------------ | ------- | -------------------------------- | --------------------- |
| `query`            | V8      | Read data, no side effects       | `./_generated/server` |
| `mutation`         | V8      | Write data, transactional        | `./_generated/server` |
| `action`           | Node.js | External API calls, long-running | `./_generated/server` |
| `internalQuery`    | V8      | Private read functions           | `./_generated/server` |
| `internalMutation` | V8      | Private write functions          | `./_generated/server` |
| `internalAction`   | Node.js | Private Node.js operations       | `./_generated/server` |

### Function Syntax (Modern)

```typescript
import { query, mutation, action } from "./_generated/server";
import { v } from "convex/values";

// Public query
export const listTasks = query({
  args: { status: v.optional(v.string()) },
  handler: async (ctx, args) => {
    const tasks = await ctx.db.query("tasks").collect();
    return tasks;
  },
});

// Public mutation
export const createTask = mutation({
  args: {
    title: v.string(),
    description: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    const taskId = await ctx.db.insert("tasks", {
      title: args.title,
      description: args.description,
      status: "pending",
    });
    return taskId;
  },
});

// Internal action (Node.js runtime)
("use node"); // Required at top of file for Node.js features

import { internalAction } from "./_generated/server";
import OpenAI from "openai";

export const generateEmbedding = internalAction({
  args: { text: v.string() },
  handler: async (_ctx, args) => {
    const openai = new OpenAI({
      apiKey: process.env.OPENAI_API_KEY,
    });
    const response = await openai.embeddings.create({
      model: "text-embedding-3-small",
      input: args.text,
    });
    return response.data[0].embedding;
  },
});
```

### Schema Definition

```typescript
// convex/schema.ts
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";
import { authTables } from "@convex-dev/auth/server";

// Your application tables
const applicationTables = {
  tasks: defineTable({
    title: v.string(),
    description: v.optional(v.string()),
    status: v.string(),
    priority: v.optional(v.number()),
    userId: v.id("users"), // Reference to auth users table
  })
    .index("by_status", ["status"])
    .index("by_priority", ["priority"])
    .index("by_user", ["userId"]),
};

export default defineSchema({
  ...authTables,  // Always include auth tables
  ...applicationTables,
});
```

### Key Schema Rules

1. **Always include `...authTables`** from `@convex-dev/auth/server` for Coder workspaces
2. **Never manually add `_creationTime`** - it's automatic
3. **Never use `.index("by_creation_time", ["_creationTime"])`** - it's built-in
4. **Index names should be descriptive**: `by_fieldName` or `by_field1_and_field2`
5. **All indexes include `_creationTime` automatically as the last field**
6. **Indexes must be non-empty**: define at least one field

### Common Validators

```typescript
v.id("tableName"); // Reference to a document
v.string(); // String value
v.number(); // Number (float/int)
v.boolean(); // Boolean
v.null(); // Null value
v.array(v.string()); // Array of strings
v.object({
  // Object with defined shape
  name: v.string(),
  age: v.number(),
});
v.optional(v.string()); // Optional field
v.union(
  // Union of types
  v.literal("active"),
  v.literal("inactive")
);
```

### Query Patterns

```typescript
// Get all documents
const all = await ctx.db.query("tasks").collect();

// Get with index filter
const active = await ctx.db
  .query("tasks")
  .withIndex("by_status", (q) => q.eq("status", "active"))
  .collect();

// Get single document
const task = await ctx.db.get(taskId);

// Unique result (throws if multiple)
const task = await ctx.db
  .query("tasks")
  .filter((q) => q.eq(q.field("title"), "My Task"))
  .unique();

// Order and limit
const recent = await ctx.db.query("tasks").order("desc").take(10);

// Pagination
const page = await ctx.db
  .query("tasks")
  .paginate({ numItems: 20, cursor: null });
```

### Mutation Patterns

`

Related in Web Dev