Adapters

Convex Adapter

Convex Adapter for Better Auth.

A Convex adapter for Better Auth which allows your Better Auth instance to communicate with your Convex DB.

Note: This isn't full Convex integration, meaning hosting your Better Auth endpoints on Convex is not possible with this library.

Limitations

If you encounter any issues, please report them in the Issues section.

Please note that Convex DB is not inherently designed for this purpose. I have implemented workarounds to facilitate dynamic queries and mutations within Convex, which is typically not supported. Additionally, there are several limitations that may affect the functionality of BetterAuth in certain scenarios. Some plugins may not operate as expected.

Here are the key limitations to consider:

  • Performance Issues: Due to the inability to perform dynamic queries/mutations, we send a request to Convex Actions, which then calls the mutate/query function. This results in at least two calls, not including subsequent database queries or mutation calls.
  • Lack of sortBy Support: BetterAuth requires sorting at the individual field level, while Convex only supports sorting at the table level.
  • Degraded Performance for Pagination Queries: Convex's support for pagination is limited, which may lead to performance issues when working outside its intended scope.
  • No Support for Certain Operators: Operators such as starts_with, ends_with, or contains are not supported. This limitation primarily affects the admin plugin. (As far as I'm aware.)

Benefits

  • If you don't mind the extra requests to make a DB query/mutation and the other limitations, then this adapter is for you!
  • Convex doesn't support dynamic queries/mutations, however we've built a workaround to allow Better Auth to use dynamic queries/mutations on Convex DB. This also means that you can leverage the power of our dynamic queries/mutations in your codebase if you wanted to as well! Read more here.
  • And lastly, if you love Convex, and you really want to use Better Auth, then this adapter is currently the best bet.

Installation

npm install @better-auth-kit/convex

Usage

Initiate the Convex Database Adapter

Head over to your Better Auth server instance, and under database, add the convexAdapter function.

import { betterAuth } from "better-auth";
import { convexAdapter } from "@better-auth-kit/convex";
import { ConvexHttpClient } from "convex/browser";
 
const convexClient = new ConvexHttpClient(process.env.CONVEX_URL);
 
export const auth = betterAuth({
  database: convexAdapter(convexClient),
  plugins: [],
  //... other options
});

Create the Convex Handler

This allows our adapter to communicate with your Convex DB.

Create a new file in convex/betterAuth.ts and add the following code:

NOTE: It's important that the file name is exactly betterAuth.ts and that it is in the convex directory.

import { action, internalQuery, internalMutation } from "./_generated/server";
import { internal } from "./_generated/api";
import { ConvexHandler, type ConvexReturnType } from "@better-auth-kit/convex/handler";
 
const { betterAuth, query, insert, update, delete_, count, getSession } = ConvexHandler({
  action,
  internalQuery,
  internalMutation,
  internal,
}) as ConvexReturnType;
 
export { betterAuth, query, insert, update, delete_, count, getSession };

If you're receiving type errors, make sure that you're running convex dev for Convex to generate the types.

That's it! You're all set up! 🔥


Important Notes

  1. When using Better Auth with Convex, we recommend not using your auth instance in any Convex files. Unexpected errors may occur, as Better Auth isn't designed to be used in Convex enviroments.

  2. If you have mutations or queries which require session verification, check out the Verifying a user's session in a Convex function section.

  3. Better Auth doesn't have Convex integration, meaning you can't host your Better Auth endpoints on Convex. You'll have to use a supported integration from Better Auth to host your endpoints. To Better Auth, Convex would just be your database.


Verifying a user's session in a Convex function

You can verify a user's session in a Convex function by using the getSession query.

It requires that you pass the sessionToken to the query, which you can get from the user's session data.

const session = await ctx.runQuery(internal.betterAuth.getSession, {
    sessionToken: "the user's session token",
});
Example
export const deleteMessage = mutation({
	args: { messageId: v.id("chat"), sessionToken: v.string() },
	handler: async (ctx, args) => {
		const session = await ctx.runQuery(internal.betterAuth.getSession, { 
			sessionToken: args.sessionToken, 
		}); 
 
		if (!session) {
			throw new Error("Unauthorized");
		}
 
		const message = await ctx.db.get(args.messageId);
		if (!message) {
			throw new Error("Message not found");
		}
 
		await ctx.db.delete(args.messageId);
	},
});

Utilizing the Better Auth adapter

Because of the way this adapter works, you actually get a really cool benefit of being able to use Convex DB dynamically. Unlike traditionally, where you must predefine Convex functions to either do queries or mutations, you can now do so dynamically at runtime.

Example
import { auth } from "./auth.ts";
 
const adapter = (await auth.$context).adapter;
 
// Create a row
await adapter.create({
	model: "user",
	data: {
		name: "some-user",
		email: "some-user@example.com",
		password: "password",
	},
});
 
// Update a row
await adapter.update({
	model: "user",
	where: [{
		field: "name",
		value: "some-user"	
	}],
	update: {
		name: "some-other-name",
	}
})
 
// Update multiple rows
await adapter.updateMany({
	model: "user",
	where: [{
		field: "name",
		value: "some-user"
	}],
	update: {
		name: "some-other-name",
	}
})
 
// Delete a row
await adapter.delete({
	model: "user",
	where: [{
		field: "name",
		value: "some-user"
	}]
})
 
// Delete multiple rows
await adapter.deleteMany({
	model: "user",
	where: [{
		field: "name",
		value: "some-user"
	}]
})
 
// Count number of rows
await adapter.count({
	model: "user",
	where: [{
		field: "name",
		value: "some-user"
	}]
})
 
// Get a row
await adapter.findOne({
	model: "user",
	where: [{
		field: "name",
		value: "some-user"
	}]
})
 
// Get multiple rows
await adapter.findMany({
	model: "user",
	where: [{
		field: "name",
		value: "some-user"
	}]
})

Schema generation

Traditionally, you'd use the @better-auth/cli to generate the schema for your database. However, in some cases, you may import your auth instance into your Convex files, which includes this Convex adapter. And for us to allow schema generation from the Better Auth CLI, we'd need to use our schema generation code apart of our adpater, which includes the utilization of the node fs module.

Convex doesn't support the usage of the fs module in it's native enviroment, so this will cause build errors.

To get around this, we intend to build a custom CLI specifically for schema generation, which will be available in the @better-auth-kit/cli package.

Keep an eye out for this in our roadmap.

On this page