convex-queries

convex-queries

Best practices for Convex database queries, indexes, and filtering. Use when writing or reviewing database queries in Convex, working with `.filter()`, `.collect()`, `.withIndex()`, defining indexes in schema.ts, or optimizing query performance.

0étoiles
0forks
Mis à jour 1/19/2026
SKILL.md
readonlyread-only
name
convex-queries
description

Best practices for Convex database queries, indexes, and filtering. Use when writing or reviewing database queries in Convex, working with `.filter()`, `.collect()`, `.withIndex()`, defining indexes in schema.ts, or optimizing query performance.

Convex Queries

Query Pattern with Index

export const listUserTasks = query({
  args: { userId: v.id("users") },
  returns: v.array(v.object({
    _id: v.id("tasks"),
    _creationTime: v.number(),
    title: v.string(),
    completed: v.boolean(),
  })),
  handler: async (ctx, args) => {
    return await ctx.db
      .query("tasks")
      .withIndex("by_user", (q) => q.eq("userId", args.userId))
      .order("desc")
      .collect();
  },
});

Avoid .filter() on Database Queries

Use .withIndex() instead - .filter() has same performance as filtering in code:

// Bad - using .filter()
const tomsMessages = await ctx.db
  .query("messages")
  .filter((q) => q.eq(q.field("author"), "Tom"))
  .collect();

// Good - use an index
const tomsMessages = await ctx.db
  .query("messages")
  .withIndex("by_author", (q) => q.eq("author", "Tom"))
  .collect();

// Good - filter in code (if index not needed)
const allMessages = await ctx.db.query("messages").collect();
const tomsMessages = allMessages.filter((m) => m.author === "Tom");

Finding .filter() usage: Search with regex \.filter\(\(?q

Exception: Paginated queries benefit from .filter().

Only Use .collect() with Small Result Sets

For 1000+ documents, use indexes, pagination, or limits:

// Bad - potentially unbounded
const allMovies = await ctx.db.query("movies").collect();

// Good - use .take() with "99+" display
const movies = await ctx.db
  .query("movies")
  .withIndex("by_user", (q) => q.eq("userId", userId))
  .take(100);
const count = movies.length === 100 ? "99+" : movies.length.toString();

// Good - paginated
const movies = await ctx.db
  .query("movies")
  .withIndex("by_user", (q) => q.eq("userId", userId))
  .order("desc")
  .paginate(paginationOptions);

Index Configuration

// convex/schema.ts
export default defineSchema({
  messages: defineTable({
    channelId: v.id("channels"),
    authorId: v.id("users"),
    content: v.string(),
    sentAt: v.number(),
  })
    .index("by_channel", ["channelId"])
    .index("by_channel_and_author", ["channelId", "authorId"])
    .index("by_channel_and_time", ["channelId", "sentAt"]),
});

Check for Redundant Indexes

by_foo and by_foo_and_bar are usually redundant - keep only by_foo_and_bar:

// Bad - redundant
.index("by_team", ["team"])
.index("by_team_and_user", ["team", "user"])

// Good - single combined index works for both
const allTeamMembers = await ctx.db
  .query("teamMembers")
  .withIndex("by_team_and_user", (q) => q.eq("team", teamId))  // Omit user
  .collect();

const specificMember = await ctx.db
  .query("teamMembers")
  .withIndex("by_team_and_user", (q) => q.eq("team", teamId).eq("user", userId))
  .unique();

Exception: by_foo is really foo + _creationTime. Keep separate if you need that sort order.

Don't Use Date.now() in Queries

Queries don't re-run when Date.now() changes:

// Bad - stale results, cache thrashing
const posts = await ctx.db
  .query("posts")
  .withIndex("by_released_at", (q) => q.lte("releasedAt", Date.now()))
  .take(100);

// Good - boolean field updated by scheduled function
const posts = await ctx.db
  .query("posts")
  .withIndex("by_is_released", (q) => q.eq("isReleased", true))
  .take(100);

Write Conflict Avoidance (OCC)

Make mutations idempotent:

// Good - idempotent, early return if already done
export const completeTask = mutation({
  args: { taskId: v.id("tasks") },
  returns: v.null(),
  handler: async (ctx, args) => {
    const task = await ctx.db.get("tasks", args.taskId);
    if (!task || task.status === "completed") return null;  // Idempotent
    await ctx.db.patch("tasks", args.taskId, { status: "completed" });
    return null;
  },
});

// Good - patch directly without reading when possible
export const updateNote = mutation({
  args: { id: v.id("notes"), content: v.string() },
  returns: v.null(),
  handler: async (ctx, args) => {
    await ctx.db.patch("notes", args.id, { content: args.content });
    return null;
  },
});

// Good - parallel updates with Promise.all
export const reorderItems = mutation({
  args: { itemIds: v.array(v.id("items")) },
  returns: v.null(),
  handler: async (ctx, args) => {
    await Promise.all(
      args.itemIds.map((id, index) => ctx.db.patch("items", id, { order: index }))
    );
    return null;
  },
});

References

You Might Also Like

Related Skills

zig-system-calls

zig-system-calls

87Kdev-database

Guides using bun.sys for system calls and file I/O in Zig. Use when implementing file operations instead of std.fs or std.posix.

oven-sh avataroven-sh
Obtenir
bun-file-io

bun-file-io

86Kdev-database

Use this when you are working on file operations like reading, writing, scanning, or deleting files. It summarizes the preferred file APIs and patterns used in this repo. It also notes when to use filesystem helpers for directories.

anomalyco avataranomalyco
Obtenir
vector-index-tuning

vector-index-tuning

26Kdev-database

Optimize vector index performance for latency, recall, and memory. Use when tuning HNSW parameters, selecting quantization strategies, or scaling vector search infrastructure.

wshobson avatarwshobson
Obtenir

Implement efficient similarity search with vector databases. Use when building semantic search, implementing nearest neighbor queries, or optimizing retrieval performance.

wshobson avatarwshobson
Obtenir

Master dbt (data build tool) for analytics engineering with model organization, testing, documentation, and incremental strategies. Use when building data transformations, creating data models, or implementing analytics engineering best practices.

wshobson avatarwshobson
Obtenir
event-store-design

event-store-design

26Kdev-database

Design and implement event stores for event-sourced systems. Use when building event sourcing infrastructure, choosing event store technologies, or implementing event persistence patterns.

wshobson avatarwshobson
Obtenir