#Directory structure

19 messages · Page 1 of 1 (latest)

amber osprey
#

What is the standard, most used, Directory structure for a convex project ?

viscid turretBOT
#

Hi @amber osprey. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

There is no single "standard" directory structure for Convex projects, but there are several common patterns and best practices that have emerged in the community. The default and most widely used approach is to have a top-level convex/ directory, which contains your backend API functions, schema, and configuration files. Here’s what you’ll typically see:

convex/
  schema.ts
  convex.config.ts
  [table or feature folders]/
    queries.ts
    mutations.ts
    actions.ts
    helpers.ts
  ...

Key points from the knowledge sources:

#
#

Example structure for a larger project:

convex/
  schema.ts
  convex.config.ts
  db/
    users.ts
    trades.ts
  features/
    userManagement/
      queries.ts
      mutations.ts
      helpers.ts
    trading/
      queries.ts
      mutations.ts
      helpers.ts
  model/
    sharedHelpers.ts

Note:

  • Customizing the root folder or function folder is not currently supported; all API functions must be under the convex/ directory Different functions and root folders?.
  • There is ongoing community discussion about supporting grouping folders (like Next.js’s (feature) folders) that are ignored by codegen, but this is not currently a feature.

In summary, the most common and recommended structure is to keep everything under convex/, organize by table or feature, and use helpers for shared logic. Adjust the structure as your project grows to keep things maintainable.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@amber osprey Kapa.ai is still learning and improving, please let me know how I did by reacting below.

amber osprey
#

@viscid turret Where should "auth" utilitaries be?

viscid turretBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

The knowledge sources recommend placing authentication utilities and related logic within the convex/ directory, typically in a dedicated file such as convex/auth.ts. This keeps your authentication code colocated with other server-side logic and makes it easy to import into your queries, mutations, and actions. For example, when using Convex Auth or Better Auth, the main configuration and utility functions are placed in convex/auth.ts or a similar file within the convex/ directory. This approach is also suggested for helper functions that handle authentication checks or user retrieval, as seen in several examples:

In summary:
Authentication utilities should be placed in the convex/ directory, typically in auth.ts or a dedicated helpers file, to keep them accessible and maintainable within your backend codebase.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@amber osprey Kapa.ai is still learning and improving, please let me know how I did by reacting below.

amber osprey
#

@viscid turret Why are models, in your given example, seperated from features?

viscid turretBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

Models are separated from features in Convex projects to promote a clear separation of concerns and improve maintainability. According to Convex best practices, most business logic should be implemented as plain TypeScript helper functions—often placed in a directory like convex/model—while the public API (queries, mutations, actions) should be thin wrappers that call into these helpers. This structure makes it easier to refactor, test, and reuse core logic across different features or API endpoints.

By keeping models (shared business logic and data access) separate from features (application-specific logic or API endpoints), you:

  • Avoid code duplication by centralizing shared logic.
  • Make it easier to update or refactor core logic without affecting feature-specific code.
  • Enable feature code to remain focused on orchestrating business logic, validation, and authorization, rather than low-level data manipulation.

This approach is explicitly recommended in the Convex documentation:

Most logic should be written as plain TypeScript functions, with the query, mutation, and action wrapper functions being a thin wrapper around one or more helper function. Concretely, most of your code should live in a directory like convex/model, and your public API ... should have very short functions that mostly just call into convex/model Use helper functions to write shared code.

#

This separation is also reflected in community discussions and example codebases, where a "db layer" (models) is kept distinct from the "api layer" (features) to ensure clarity and maintainability as the codebase grows Folder/File Naming/Structure Ideas.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@amber osprey Kapa.ai is still learning and improving, please let me know how I did by reacting below.

amber osprey
#

@viscid turret Why not using queries / insert etc in functions inself but use models ?

viscid turretBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

The main reason for not putting queries, inserts, and other database operations directly in your Convex API functions (queries, mutations, actions) and instead using "models" (helper functions) is to promote code reuse, maintainability, and clarity.

Convex best practices recommend that most of your business logic should be written as plain TypeScript helper functions—often placed in a directory like convex/model—while your API functions should be thin wrappers that call these helpers. This approach has several benefits:

  • Code reuse: Shared logic (like loading a user, checking permissions, or combining multiple database operations) can be reused across multiple API endpoints without duplication.
  • Easier refactoring: When logic is centralized in helpers/models, you can update it in one place and have those changes reflected everywhere it's used.
  • Cleaner API functions: Your API functions become short and focused, making it easier to see at a glance what each endpoint does.
  • Testing and composability: Helper functions can be tested and composed more easily than large, monolithic API functions.

From the documentation:

Most logic should be written as plain TypeScript functions, with the query, mutation, and action wrapper functions being a thin wrapper around one or more helper function. Concretely, most of your code should live in a directory like convex/model, and your public API ... should have very short functions that mostly just call into convex/model Use helper functions to write shared code.

#

This pattern is also shown in detailed examples, where the API layer is just a thin wrapper, and the actual logic (including database access) is in the model/helper functions Example: Use helper functions to write shared code.

In summary:
Using models (helper functions) for database operations instead of putting them directly in API functions leads to more maintainable, reusable, and testable code, which is the recommended best practice in Convex.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: