Creating a New Adapter
Relevant source files
The following files were used as context for generating this wiki page:- packages/adapters/convex/src/index.ts
- packages/adapters/convex/src/handler/index.ts
- packages/adapters/convex/src/transform/index.ts
- packages/adapters/convex/test/adapter.test.ts
- packages/cli/src/commands/db/database-explorer-plugin.ts
- packages/adapters/convex/src/generate-schema/generate-schema-builder.ts
- packages/plugins/app-invite/src/index.ts
An adapter in Better Auth Kit is a crucial component that acts as a bridge between the core better-auth logic and a specific database technology. Its primary responsibility is to translate generic data manipulation requests from the authentication library into the specific query language and format of the target database. This allows better-auth to be database-agnostic, supporting various backends like Convex, SQL databases, or NoSQL databases by simply plugging in the appropriate adapter.
Creating a new adapter involves implementing a standard Adapter interface, which defines a set of methods for common database operations (CRUD). This page will use the existing Convex adapter as a reference to guide developers through the process of building a new adapter, covering the required interface, data transformation, and interaction with the database.
Sources: packages/adapters/convex/src/index.ts:2-4, README.md:14-15
Core Adapter Architecture
An adapter is essentially a factory function that takes a database client and optional configuration, and returns an AdapterInstance. This instance is another function that receives the BetterAuthOptions and finally returns the Adapter object containing the implementation of all required database methods.
This multi-layered factory pattern allows for flexible configuration, giving the adapter access to both the database client and the user's better-auth configuration.
Sources: packages/adapters/convex/src/index.ts:8-11
Adapter Interface
The core of any adapter is the Adapter object. It must implement a specific set of methods to handle data persistence. The following diagram and table detail the required methods.
This diagram shows the structure of the adapter factory and the resulting Adapter object with its methods.
Required Methods
A new adapter must implement the following methods. These methods are called by the better-auth core and its plugins to interact with the database.
| Method | Description |
|---|---|
id | A unique string identifier for the adapter (e.g., "convex"). |
create | Inserts a new record into a specified model. |
findOne | Retrieves a single record from a model that matches the where clause. |
findMany | Retrieves multiple records from a model, with support for where clauses, sorting, limits, and offsets. |
update | Updates a single record matching a where clause with new data. |
updateMany | Updates multiple records matching a where clause with new data. |
delete | Deletes a single record matching a where clause. |
deleteMany | Deletes multiple records matching a where clause. |
count | Returns the number of records in a model, optionally filtered by a where clause. |
Sources: packages/adapters/convex/src/index.ts:28-254, packages/cli/src/commands/db/database-explorer-plugin.ts:28-163
Data Transformation
A critical part of an adapter is the data transformation layer. Databases often have their own conventions for field names (e.g., _id vs id, _creationTime vs createdAt) and data types (e.g., Date objects vs ISO strings). The adapter must handle this translation seamlessly.
The Convex adapter implements a createTransform utility for this purpose.
Sources: packages/adapters/convex/src/index.ts:4, packages/adapters/convex/src/transform/index.ts
Key Transformation Functions
transformInput(data, model, action): This function is called before writing data to the database (createorupdate). It convertsbetter-authdata into a format the database understands. For example, it convertsDateobjects to ISO strings and handles default values.
Sources: packages/adapters/convex/src/transform/index.ts:121-152transformOutput(data, model): This function is called after reading data from the database. It converts database-specific fields and types back to thebetter-authstandard. For example, it renames_idtoidand_creationTimetocreatedAt, and converts date strings back toDateobjects.
Sources: packages/adapters/convex/src/transform/index.ts:153-169transformWhereOperators(where): This function translatesbetter-authquery operators (likein) into operators that the target database can handle. The Convex adapter, for instance, converts aninoperator into a series ofeqoperators connected byOR.
Sources: packages/adapters/convex/src/transform/index.ts:19-36
Data Flow for a findOne Operation
The following sequence diagram illustrates the data flow and transformation for a findOne call.
Database Interaction
The adapter must contain logic to communicate with the database. The Convex adapter abstracts this into a db function that handles different action types like insert, query, update, and delete.
This logic often involves:
- Query Building: Constructing a valid query from the
whereclauses provided bybetter-auth. The Convex adapter uses aqueryBuilderto create a query string.
Sources: packages/adapters/convex/src/handler/index.ts, packages/adapters/convex/src/index.ts:65-75 - Executing Calls: Making the actual API or network call to the database endpoint.
Sources: packages/adapters/convex/src/transform/index.ts:74-118 - Handling Pagination: For
findMany, the adapter must correctly handlelimitandoffset. The Convex adapter demonstrates a paginated fetch loop to support offsets, as Convex's cursor-based pagination doesn't directly support it.
Sources: packages/adapters/convex/src/index.ts:111-140
Adapter-Specific Tooling
Beyond the runtime Adapter interface, an adapter can provide additional tooling to improve the developer experience. The Convex adapter includes a schema generation utility.
Schema Generation
The generateSchema function in the Convex adapter reads the schema definitions from better-auth plugins and generates a schema.ts file that is specific to Convex, using defineSchema and defineTable. This automates the process of keeping the database schema in sync with the application's auth configuration.
This process involves:
- Parsing the existing Convex schema file to preserve user-defined tables and fields.
- Getting all table schemas from
better-authoptions and plugins usinggetAuthTables. - Converting the
better-authschema definition (types, required flags, references) into Convex'svvalidator syntax (e.g.,v.string(),v.optional(v.boolean()),v.id("user")). - Merging the generated auth tables with the user's existing tables and writing the result back to the file.
Sources: packages/adapters/convex/test/schema.test.ts:2, packages/adapters/convex/src/generate-schema/generate-schema-builder.ts:16-30
Testing Your Adapter
A robust test suite is essential for a reliable adapter. The better-auth project provides a standard test suite (runAdapterTest) that can be used to verify an adapter's implementation against the expected behavior.
The Convex adapter's tests demonstrate how to:
- Instantiate the adapter with a test client.
- Run the standard adapter tests.
- Add custom tests for database-specific features, like the
offsetandlimitimplementation.
// Example from packages/adapters/convex/test/adapter.test.ts
import { runAdapterTest } from "better-auth/adapters/test";
import { ConvexClient } from "convex/browser";
import { convexAdapter } from "./../src/index";
describe("Run BetterAuth Adapter tests", async () => {
const client = new ConvexClient(process.env.CONVEX_URL as string);
// ... setup and teardown hooks ...
const adapter = convexAdapter(client);
await runAdapterTest({
getAdapter: async (customOptions = {}) => {
return adapter({ ...customOptions });
},
skipGenerateIdTest: true,
});
});
Sources: packages/adapters/convex/test/adapter.test.ts:15-34
Summary
Creating a new adapter for better-auth-kit is a structured process centered around implementing the Adapter interface. The key responsibilities are translating CRUD operations, transforming data between the better-auth core and the database, and handling database-specific features like query syntax and pagination. By following the pattern set by the Convex adapter and leveraging the provided testing utilities, developers can extend better-auth to support any database backend.