Convex Backend Overview
Relevant source files
The following files were used as context for generating this wiki page:
The TREAD Talks project uses Convex as its primary backend, providing a hosted database and server-side functions. Convex handles data storage, real-time synchronization, and execution of business logic through queries, mutations, and actions.
Sources: README.md:5, AGENTS.md:214-219
Core Backend Architecture
The backend follows a feature-based organization. Database schemas and backend functions reside in the convex/ directory, while entity-specific logic is grouped under convex/model/.
Directory Structure
convex/- Main entry points and configuration.convex/model/{domain}/- Logic for specific domains liketalks,users, ortopics.convex/lib/- Shared utilities for the backend runtime.
Sources: AGENTS.md:65-72, AGENTS.md:129-133
Function Types
Convex provides three primary types of server-side functions:
| Function Type | Description |
|---|---|
| Query | Deterministic, read-only functions used to fetch data. They are automatically reactive. |
| Mutation | Deterministic functions that write to the database. They run as ACID transactions. |
| Action | Non-deterministic functions that can call external APIs or perform side effects. |
Sources: convex/_generated/server.d.ts:31-75, convex/_generated/ai/guidelines.md:329-335
Data Modeling and Relationships
The project defines its schema in convex/schema.ts, utilizing modular schema fragments from different feature domains. Relationships are primarily managed through explicit join tables and foreign keys.
Entity Relationships
The following ER diagram illustrates the core relationships between entities like Talks, Topics, and Speakers.
Visual representation of how talks, topics, and speakers are linked via join tables and IDs.
Sources: convex/schema.ts:13-64
Data Models Summary
| Table | Description | Key Fields |
|---|---|---|
talks | Stores information about Christ-centered talks. | title, slug, speakerId, status |
topics | Categories for talks. | title, slug, description |
talksOnTopics | Join table for many-to-many relationship between talks and topics. | talkId, topicId |
userFavoriteTalks | Tracks user bookmarks for specific talks. | talkId, userId |
Sources: convex/schema.ts:13-64, convex/model/talks/queries.ts:251-270
Query and Mutation Patterns
The backend implements specific naming and execution conventions to ensure consistency and performance.
Execution Flow
A typical data request starts at the frontend, calls a Convex function via the generated API, and interacts with the database.
Sequence of a standard read query in the TreadTalks ecosystem.
Sources: convex/_generated/server.d.ts:31-38, convex/model/talks/queries.ts:79-84
Naming Conventions
get*: Fetches a single document or null.list*: Returns a filtered or public array of documents, often enriched with related data.create*/update*: Mutations used for data entry and modification.
Sources: AGENTS.md:139-147
Implementation Example: Adding a Talk to a Topic
Mutations validate authorization and resource existence before modifying the database.
// convex/model/topics/mutations.ts:14-53
export const addTalkToTopic = mutation({
args: { talkId: v.id('talks'), topicId: v.id('topics') },
handler: async (ctx, args) => {
await requireAuth(ctx);
const talk = await ctx.db.get('talks', args.talkId);
if (!talk) throwNotFound('Talk not found');
const existing = await ctx.db
.query('talksOnTopics')
.withIndex('by_talkId_and_topicId', (q) =>
q.eq('talkId', args.talkId).eq('topicId', args.topicId)
)
.unique();
if (existing) throwValidationError('Already associated');
return await ctx.db.insert('talksOnTopics', { ...args });
},
});
Sources: convex/model/topics/mutations.ts:14-53
Integration with Components
TreadTalks leverages pre-built Convex components to handle complex infrastructure concerns without hand-rolling logic.
| Component | Purpose | Source File |
|---|---|---|
betterAuth | Handles authentication flows and user identity. | convex/_generated/api.d.ts:182 |
rateLimiter | Implements per-key quotas and throttling. | convex/_generated/api.d.ts:183 |
resend | Integrates with the Resend service for transactional emails. | convex/_generated/api.d.ts:184 |
Sources: convex/_generated/api.d.ts:181-185, package.json:52-54
Performance and Scaling Guidelines
The project strictly adheres to Convex's scaling guidelines to prevent performance degradation as the dataset grows.
- Index-Driven Queries: Every query must use
.withIndex()to avoid full table scans. - Bounded Collections: Queries should use
.take(n)or.paginate()instead of.collect()when results are potentially unbounded. - No Wall-Clock Reads:
Date.now()is forbidden inside queries to ensure cacheability; it must be passed as an argument from the client. - Denormalized Counters: Since Convex lacks a native
countoperator, the project uses denormalized counters or the@convex-dev/aggregatecomponent for row counts.
Sources: convex/_generated/ai/guidelines.md:383-400, convex/_generated/ai/guidelines.md:405-412
Summary
The Convex backend for TreadTalks provides a type-safe, ACID-compliant foundation for faith-based content management. By organizing logic into domain models and utilizing specialized join tables, the system maintains high performance while supporting complex relationships between talks, speakers, and topics.