Convex Schema Generation
Relevant source files
The following files were used as context for generating this wiki page:- packages/adapters/convex/src/generate-schema/index.ts
- packages/adapters/convex/src/generate-schema/generate-schema-builder.ts
- packages/adapters/convex/src/generate-schema/get-convex-schema.ts
- packages/adapters/convex/src/generate-schema/generate-imports.ts
- packages/adapters/convex/test/schema.test.ts
- packages/adapters/convex/test/test_convex1/schema.ts
- packages/plugins/feedback/src/index.ts
The Convex Schema Generation is a utility within the better-auth-kit Convex adapter responsible for automatically creating and updating the convex/schema.ts file. It introspects the BetterAuthOptions, including schemas from core authentication and any installed plugins, and merges them with an existing user-defined Convex schema. This process ensures that the database schema required by better-auth-kit is correctly defined in the Convex project, while preserving any custom tables or fields the developer has added.
This automation simplifies setup and maintenance by removing the need for developers to manually define and update tables for users, sessions, accounts, and other plugin-specific data models. The generator is non-destructive, meaning it reads the existing schema, adds or updates the necessary better-auth-kit tables, and writes the result back, keeping custom user code intact.
Core Process
The schema generation is orchestrated by the generateSchema function. It follows a three-step process to produce the final schema.ts file content.
Sources: packages/adapters/convex/src/generate-schema/index.ts:10-21
// packages/adapters/convex/src/generate-schema/index.ts:10-21
export const generateSchema = async (
BAOptions: BetterAuthOptions,
options: { convex_dir_path: string } = {
convex_dir_path: "./convex",
},
): Promise<string> => {
const { convex_dir_path } = options;
const existing_schema_code: string = await getConvexSchema(convex_dir_path);
// Step 1. Ensure that the imports are present
let code: string = generateImportStage(existing_schema_code);
// Step 2. Add scham code to the defineSchema export
code = await generateSchemaBuilderStage({ code, BAOptions });
return code;
};
The following diagram illustrates the high-level flow of the schema generation process.
This diagram shows the sequence of function calls within generateSchema to produce the final schema code.
Sources: packages/adapters/convex/src/generate-schema/index.ts:10-21
Key Functions
| Function | Description | Source File |
|---|---|---|
generateSchema | The main entry point. Orchestrates reading, building, and formatting the schema. | generate-schema/index.ts |
getConvexSchema | Reads the content of an existing convex/schema.ts file. Returns an empty string if not found. | generate-schema/get-convex-schema.ts |
generateImportStage | Checks for and adds the required convex/server and convex/values imports to the schema file content if they are missing. | generate-schema/generate-imports.ts |
generateSchemaBuilderStage | The core logic. Parses the existing schema, converts better-auth schemas to Convex format, merges them, and reconstructs the final defineSchema block. | generate-schema/generate-schema-builder.ts |
parse_existing_schema | Uses regular expressions to parse a schema string, extracting existing table definitions into a structured object, preserving user-defined tables and fields. | generate-schema/generate-schema-builder.ts:32-68 |
convert_plugins_to_convex_schema | Iterates through better-auth tables (from core and plugins), converts their fields to Convex v syntax, and combines them with the parsed existing schema tables. | generate-schema/generate-schema-builder.ts:70-136 |
Schema Parsing and Merging
The heart of the generator is its ability to merge better-auth schemas with a pre-existing schema.ts file. This is handled within the generateSchemaBuilderStage function.
Sources: packages/adapters/convex/src/generate-schema/generate-schema-builder.ts
Parsing Existing Schema
The process begins by parsing the existing schema file content using the parse_existing_schema function. This function uses several regular expressions to identify the defineSchema({...}) block and each tableName: defineTable({...}) definition within it. It deconstructs the schema into a structured Table object, which contains each table's name, fields, and field properties (type, optionality). This allows the system to preserve any tables and fields that are not managed by better-auth-kit.
Sources: packages/adapters/convex/src/generate-schema/generate-schema-builder.ts:32-68, packages/adapters/convex/test/test_convex1/schema.ts
Merging and Conversion
The convert_plugins_to_convex_schema function takes the BetterAuthOptions and the parsed tables. It retrieves all required tables using getAuthTables and converts each one into a Convex defineTable string. It intelligently handles field type conversion, optional fields, and relationships.
Finally, it combines the newly generated better-auth table strings with the string representations of any pre-existing, user-defined tables that were parsed in the first step. This ensures that custom tables, like the messages table in the test files, are preserved in the final output.
The diagram below shows the data flow for this merging process.
This diagram illustrates how BetterAuth options and an existing schema file are processed and merged to produce the final output.
Sources: packages/adapters/convex/src/generate-schema/generate-schema-builder.ts:20-26
Field Type Conversion
A critical part of the generation is mapping better-auth schema field types to their corresponding Convex validator types from convex/values. The system handles primitive types, arrays, and relationships (references).
| Better-Auth Type | Convex v Type | Notes |
|---|---|---|
string | v.string() | Standard string type. |
boolean | v.boolean() | Standard boolean type. |
number | v.number() | Standard number type. |
date | v.string() | Dates are stored as ISO strings. |
string[] | v.array() | The generator currently uses a generic v.array(). |
number[] | v.array() | The generator currently uses a generic v.array(). |
string (with references) | v.id("referenced_model") | If a field has a references property, it's converted to a Convex ID type for relationships. |
string (field name is id) | v.id("table_name") | A field named id is automatically treated as the table's ID reference. |
required: false | v.optional(...) | Any non-required field is wrapped in v.optional(). IDs are an exception and are always required. |
Sources: packages/adapters/convex/src/generate-schema/generate-schema-builder.ts:93-112, packages/adapters/convex/test/schema.test.ts:153-195
Example: Reference Generation
The generator correctly identifies fields that should be ID references.
// packages/adapters/convex/test/schema.test.ts:202-219
// Input schema from a plugin
{
id: "admin",
schema: {
admin: {
fields: {
id: { type: "string", required: true },
reference_required: {
type: "string",
required: true,
references: {
field: "something2",
model: "something2_else",
},
},
},
},
},
}
// Generated output in schema.ts
`admin: defineTable({
id: v.id("admin"),
reference_required: v.id("something2_else"),
}),`
This test case demonstrates how a field with a references property is correctly converted to a v.id() call pointing to the referenced model.
Sources: packages/adapters/convex/test/schema.test.ts:197-238
Default Auth Schema
The better-auth-kit core defines a default set of tables required for authentication. The schema generator ensures these tables are present in the final schema.ts.
The following ER diagram shows the relationships between the default authentication tables.
This diagram represents the default tables (user, session, account, verification) and their relationships as defined for the Convex adapter.
Sources: packages/adapters/convex/test/schema.test.ts:10-53
Conclusion
The Convex Schema Generation feature is a powerful tool that bridges the gap between the abstract schema definition of better-auth-kit and the concrete implementation required by a Convex backend. By automatically generating and merging schema definitions, it reduces boilerplate, prevents configuration drift, and allows developers to focus on building their application's features while seamlessly integrating a robust authentication system. Its non-destructive approach respects existing user code, making it a safe and efficient utility for any project using better-auth-kit with Convex.