feat: Add DynamoDB adapter support #45

Closed
opened 2026-03-13 07:29:42 -05:00 by GiteaMirror · 21 comments
Owner

Originally created by @Bekacru on GitHub (Oct 7, 2024).

We need to add support for a DynamoDB

Originally created by @Bekacru on GitHub (Oct 7, 2024). We need to add support for a DynamoDB
GiteaMirror added the enhancementadapter labels 2026-03-13 07:29:42 -05:00
Author
Owner

@Bekacru commented on GitHub (Nov 17, 2024):

no plans currently

@Bekacru commented on GitHub (Nov 17, 2024): no plans currently
Author
Owner

@rehanvdm commented on GitHub (Dec 27, 2024):

@Bekacru I would be really interested in this, but I guess, it's going to be difficult to stuff a relational model into NoSQL DB?

@rehanvdm commented on GitHub (Dec 27, 2024): @Bekacru I would be really interested in this, but I guess, it's going to be difficult to stuff a relational model into NoSQL DB?
Author
Owner

@dvalbuena1 commented on GitHub (Dec 29, 2024):

I did try using a 'one table' pattern, but it is not easy. The problem is that all adapters are built in terms of 'SQL where clauses' and for DynamoDB, it is not simple to parse those clauses into a Command. Depending on what you are trying to retrieve and the information you have at the moment of retrieval, you must use a specific Command to get the best performance. Essentially, you need to consider all possible clauses to use the best Command for each case.

I compared the implementation of Next-Auth for DynamoDB, and it is less generic. There, you define the adapter in terms of each action you need to perform: 'createUser,' 'getUserByEmail,' 'getSessionAndUser,' etc. Because of that, there the implementation is a piece of cake, but not here.

One possible but not great approach is to use PartiQL, a way to use SQL with DynamoDB. However, the disadvantages are that you cannot use the 'one table' pattern, and depending on the where clause being used, PartiQL might not use the best Command. This is better explained here.

@Bekacru, any comments or suggestions?

@dvalbuena1 commented on GitHub (Dec 29, 2024): I did try using a 'one table' pattern, but it is not easy. The problem is that all adapters are built in terms of 'SQL where clauses' and for DynamoDB, it is not simple to parse those clauses into a Command. Depending on what you are trying to retrieve and the information you have at the moment of retrieval, you must use a specific Command to get the best performance. Essentially, you need to consider all possible clauses to use the best Command for each case. I compared the implementation of Next-Auth for DynamoDB, and it is less generic. There, you define the adapter in terms of each action you need to perform: 'createUser,' 'getUserByEmail,' 'getSessionAndUser,' etc. Because of that, there the implementation is a piece of cake, but not here. One possible but not great approach is to use PartiQL, a way to use SQL with DynamoDB. However, the disadvantages are that you cannot use the 'one table' pattern, and depending on the where clause being used, PartiQL might not use the best Command. This is better explained [here](https://medium.com/@jvroig/dynamodb-partiql-is-fun-but-dangerous-fec0f2803220). @Bekacru, any comments or suggestions?
Author
Owner

@rehanvdm commented on GitHub (Jan 2, 2025):

The one-table design has been overhyped and advocated. I would advise to stay away from it to keep things simple. It does reduce the amount of effort to plan query patterns upfront a little but I know all too well how complex DynamoDB can be.

Yeah PartiQL can be useful. To prevent "accidental scans" you can deny that on the IAM role that is used to query the table.

But anyway, maybe if DynamoDB would be too much effort, the newly announced Aurora DSQL (still in preview, should be GA around Q2/3) would be easier. I wrote about it here https://blog.datachef.co/aurora-dsql-a-new-boring-aws-serverless-postgres-compatible-database its much closer to Postgres so hopefully it won't be that much work?

@rehanvdm commented on GitHub (Jan 2, 2025): The one-table design has been overhyped and advocated. I would advise to stay away from it to keep things simple. It does reduce the amount of effort to plan query patterns upfront a little but I know all too well how complex DynamoDB can be. Yeah PartiQL can be useful. To prevent "accidental scans" you can deny that on the IAM role that is used to query the table. But anyway, maybe if DynamoDB would be too much effort, the newly announced Aurora DSQL (still in preview, should be GA around Q2/3) would be easier. I wrote about it here https://blog.datachef.co/aurora-dsql-a-new-boring-aws-serverless-postgres-compatible-database its much closer to Postgres so hopefully it won't be that much work?
Author
Owner

@FeLiNa22 commented on GitHub (Feb 8, 2025):

If it is possible to change the adapter pattern to something like next-auth/authjs where a generic interface is required that implements methods like 'createUser,' 'getUserByEmail,' 'getSessionAndUser', and each plugin just adds/extends this interface, then it would open up better-auth to every single database, like DynamoDB.

@FeLiNa22 commented on GitHub (Feb 8, 2025): If it is possible to change the adapter pattern to something like next-auth/authjs where a generic interface is required that implements methods like 'createUser,' 'getUserByEmail,' 'getSessionAndUser', and each plugin just adds/extends this interface, then it would open up better-auth to every single database, like DynamoDB.
Author
Owner

@anay-208 commented on GitHub (Jun 19, 2025):

I agree with @FeLiNa22, that would make everything much easier.
I was looking to make an adapter for it, but this is the only hindrance.

@anay-208 commented on GitHub (Jun 19, 2025): I agree with @FeLiNa22, that would make everything much easier. I was looking to make an adapter for it, but this is the only hindrance.
Author
Owner

@rowberry-grant commented on GitHub (Jul 29, 2025):

Is there any way to do this? It would be great for mcp servers.

@rowberry-grant commented on GitHub (Jul 29, 2025): Is there any way to do this? It would be great for mcp servers.
Author
Owner

@renanwilliam commented on GitHub (Aug 5, 2025):

I'm trying to implement using DynamoDB and it seems I managed to get some things working. Maybe it can serve as a starting point for those who want to contribute. Repository link:
https://github.com/renanwilliam/better-auth-dynamodb

@renanwilliam commented on GitHub (Aug 5, 2025): I'm trying to implement using DynamoDB and it seems I managed to get some things working. Maybe it can serve as a starting point for those who want to contribute. Repository link: https://github.com/renanwilliam/better-auth-dynamodb
Author
Owner

@bestickley commented on GitHub (Aug 12, 2025):

DynamoDB will not be able to support list APIs in a scalable way (i.e. ListUsers, ListOrganizations, etc.). DynamoDB is a key-value store. It will work great for secondary storage, but not primary. You can use DynamoDB's Scan command as @renanwilliam has in his example above, but you'll have to paginate through 1MB chunks of data and filter client side until you find enough items based on the page size to return. This is not scalable and will be slow with lots of users (and expensive). DynamoDB does not support arbitrary where commands which BetterAuth supports. You must design the index up front to account for the access patterns.

One could potentially use DynamoDB if they never used any BetterAuth functionality that called the findMany adapter method, but then you're handicapping yourself from using all that BetterAuth has to offer.

@bestickley commented on GitHub (Aug 12, 2025): DynamoDB will not be able to support list APIs in a scalable way (i.e. ListUsers, ListOrganizations, etc.). DynamoDB is a key-value store. It will work great for secondary storage, but not primary. You can use DynamoDB's Scan command as @renanwilliam has in his example above, but you'll have to paginate through 1MB chunks of data and filter client side until you find enough items based on the page size to return. This is not scalable and will be slow with lots of users (and expensive). DynamoDB does not support arbitrary `where` commands which BetterAuth supports. You must design the index up front to account for the access patterns. One could potentially use DynamoDB if they never used any BetterAuth functionality that called the `findMany` adapter method, but then you're handicapping yourself from using all that BetterAuth has to offer.
Author
Owner

@rehanvdm commented on GitHub (Aug 12, 2025):

The main reason I wanted DynamoDB was that it is serverless and scales. I can see how it can be difficult to integrate that and support as better-auth, so if it was me, I would just say it is not supported and there isn't plans to..

I would be happy if DSQL gets integration, which is much more relational and will probably be very similar to PostgreSQL.

@rehanvdm commented on GitHub (Aug 12, 2025): The main reason I wanted DynamoDB was that it is serverless and scales. I can see how it can be difficult to integrate that and support as better-auth, so if it was me, I would just say it is not supported and there isn't plans to.. I would be happy if DSQL gets integration, which is much more relational and will probably be very similar to PostgreSQL.
Author
Owner

@renanwilliam commented on GitHub (Aug 12, 2025):

It's not as difficult and scalable as you might think. This repository was created using Claude Code with minimal supervision; it serves as a starting point for anyone interested.

As soon as I have some time, I'll work on this further, focusing on more performant queries without using scans.

@renanwilliam commented on GitHub (Aug 12, 2025): It's not as difficult and scalable as you might think. This repository was created using Claude Code with minimal supervision; it serves as a starting point for anyone interested. As soon as I have some time, I'll work on this further, focusing on more performant queries without using scans.
Author
Owner

@bestickley commented on GitHub (Aug 12, 2025):

@rehanvdm, are you familiar with Aurora Postgres Serverless V2? It's serverless and scales. My team will be using that with DynamoDB for secondary storage (sessions).

@renanwilliam, I'm very interested to see if you can make your implementation scalable but I'm afraid it's a limitation of the fundamental design of DynamoDB. Relational data is not what it was built for.

@bestickley commented on GitHub (Aug 12, 2025): @rehanvdm, are you familiar with [Aurora Postgres Serverless V2](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-serverless-v2.html)? It's serverless and scales. My team will be using that with DynamoDB for secondary storage (sessions). @renanwilliam, I'm very interested to see if you can make your implementation scalable but I'm afraid it's a limitation of the fundamental design of DynamoDB. Relational data is not what it was built for.
Author
Owner

@rehanvdm commented on GitHub (Aug 12, 2025):

@bestickley Yes, but Aurora does not scale to 0, DSQL does it has a small storage price but otherwise it is actually serverless, you pay per request. Aurora Serverless is just a smart managed service that does autoscaling.

Friendship ended with DynamoDB, new friend DSQL (for this specific ticket request). So would be very happy to see support for DSQL instead of DynamoDB.

@rehanvdm commented on GitHub (Aug 12, 2025): @bestickley Yes, but Aurora does not scale to 0, DSQL does it has a small storage price but otherwise it is actually serverless, you pay per request. Aurora Serverless is just a smart managed service that does autoscaling. Friendship ended with DynamoDB, new friend DSQL (for this specific ticket request). So would be very happy to see support for DSQL instead of DynamoDB.
Author
Owner

@bestickley commented on GitHub (Aug 12, 2025):

That was v1 :)

You can specify that Aurora Serverless v2 DB instances scale down to zero ACUs and automatically pause,

Scaling to Zero ACUs with automatic pause and resume for Aurora Serverless v2

I'm sure DSQL is great though. I haven't had change to try it out and cannot in my current AWS environment (GovCloud). The postgres incompatibilities don't seem too great. See them here.

@bestickley commented on GitHub (Aug 12, 2025): That was v1 :) > You can specify that Aurora Serverless v2 DB instances scale down to zero ACUs and automatically pause, [Scaling to Zero ACUs with automatic pause and resume for Aurora Serverless v2](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-serverless-v2-auto-pause.html) I'm sure DSQL is great though. I haven't had change to try it out and cannot in my current AWS environment (GovCloud). The postgres incompatibilities don't seem too great. See them [here](https://docs.aws.amazon.com/aurora-dsql/latest/userguide/working-with-postgresql-compatibility-unsupported-features.html).
Author
Owner

@rehanvdm commented on GitHub (Aug 12, 2025):

Yeah auto-pause is not Scale to 0, Pay per request. It is not serverless, it is managed https://www.gomomento.com/blog/fighting-off-fake-serverless-bandits-with-the-true-definition-of-serverless/

Well aware of incompatibilities (author of the post), but DSQL is the closest and best relational serverless DB that AWS has to offer. It would be a much easier plug and play than trying to bend Better Auth to work with DynamoDB

@rehanvdm commented on GitHub (Aug 12, 2025): Yeah auto-pause is not Scale to 0, Pay per request. It is not serverless, it is managed https://www.gomomento.com/blog/fighting-off-fake-serverless-bandits-with-the-true-definition-of-serverless/ [Well aware of incompatibilities](https://blog.datachef.co/aurora-dsql-a-new-boring-aws-serverless-postgres-compatible-database) (author of the post), but DSQL is the closest and best relational serverless DB that AWS has to offer. It would be a much easier plug and play than trying to bend Better Auth to work with DynamoDB
Author
Owner

@bestickley commented on GitHub (Aug 12, 2025):

What's serverless and what's not is up to interpretation, but Aurora Serverless V2 allows you to scale down to 0 ACUs so you pay $0 for compute during that time. You still have to pay for storage but that's expected IMO.

Secondary Storage DDB Implementation:

import { type SecondaryStorage } from "better-auth";
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import {
  DynamoDBDocumentClient,
  PutCommand,
  GetCommand,
  DeleteCommand,
} from "@aws-sdk/lib-dynamodb";
import { getEnvVar } from "@census/shared-core";

const ddbClient = new DynamoDBClient();
const docClient = DynamoDBDocumentClient.from(ddbClient);
const tableName = getEnvVar("AUTH_TABLE");
/**
 * DynamoDB Partition Key Attribute Name. Must configure in infra.
 */
const pkAttributeName = "PK";
/**
 * DynamoDB TTL Attribute Name. Must configure in infra.
 */
const expiresAtAttributeName = "expiresAt";
const valueAttributeName = "value";

export const secondaryStorage: SecondaryStorage = {
  async get(key) {
    const res = await docClient.send(
      new GetCommand({
        TableName: tableName,
        Key: { [pkAttributeName]: key },
      }),
    );
    const item = res.Item;
    if (!item) return null;

    const now = getUnixEpochTime();
    const expiresAt = item[expiresAtAttributeName];
    if (expiresAt && expiresAt < now) {
      return null;
    }

    return item[valueAttributeName] ?? null;
  },
  async set(key, value, ttl) {
    // DynamoDB stores TTL in unix epoch time format at seconds granularity
    // whereas BetterAuth supplies number of seconds until expired so must convert
    const expiresAt = ttl ? getUnixEpochTime() + ttl : undefined;
    await docClient.send(
      new PutCommand({
        TableName: tableName,
        Item: {
          [pkAttributeName]: key,
          [valueAttributeName]: value,
          [expiresAtAttributeName]: expiresAt,
        },
      }),
    );
    return value;
  },
  async delete(key) {
    await docClient.send(
      new DeleteCommand({
        TableName: tableName,
        Key: { [pkAttributeName]: key },
      }),
    );
  },
};

function getUnixEpochTime() {
  return Math.floor(Date.now() / 1000);
}

@bestickley commented on GitHub (Aug 12, 2025): What's serverless and what's not is up to interpretation, but Aurora Serverless V2 allows you to scale down to 0 ACUs so you pay $0 for compute during that time. You still have to pay for storage but that's expected IMO. Secondary Storage DDB Implementation: ```ts import { type SecondaryStorage } from "better-auth"; import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { DynamoDBDocumentClient, PutCommand, GetCommand, DeleteCommand, } from "@aws-sdk/lib-dynamodb"; import { getEnvVar } from "@census/shared-core"; const ddbClient = new DynamoDBClient(); const docClient = DynamoDBDocumentClient.from(ddbClient); const tableName = getEnvVar("AUTH_TABLE"); /** * DynamoDB Partition Key Attribute Name. Must configure in infra. */ const pkAttributeName = "PK"; /** * DynamoDB TTL Attribute Name. Must configure in infra. */ const expiresAtAttributeName = "expiresAt"; const valueAttributeName = "value"; export const secondaryStorage: SecondaryStorage = { async get(key) { const res = await docClient.send( new GetCommand({ TableName: tableName, Key: { [pkAttributeName]: key }, }), ); const item = res.Item; if (!item) return null; const now = getUnixEpochTime(); const expiresAt = item[expiresAtAttributeName]; if (expiresAt && expiresAt < now) { return null; } return item[valueAttributeName] ?? null; }, async set(key, value, ttl) { // DynamoDB stores TTL in unix epoch time format at seconds granularity // whereas BetterAuth supplies number of seconds until expired so must convert const expiresAt = ttl ? getUnixEpochTime() + ttl : undefined; await docClient.send( new PutCommand({ TableName: tableName, Item: { [pkAttributeName]: key, [valueAttributeName]: value, [expiresAtAttributeName]: expiresAt, }, }), ); return value; }, async delete(key) { await docClient.send( new DeleteCommand({ TableName: tableName, Key: { [pkAttributeName]: key }, }), ); }, }; function getUnixEpochTime() { return Math.floor(Date.now() / 1000); } ```
Author
Owner

@mggevaer commented on GitHub (Oct 7, 2025):

I have no experience with DSQL, but I does seem like AWS advertises API compatibility with MongoDB: https://aws.amazon.com/documentdb/features/#topic-4
And MongoDB is supported by better-auth.

Perhaps that is a low-effort, low-maintenance way to get DynamoDB support?
DSQL is quite new, DynamoDB does seem to be supported by a bit more infra managing projects such as SST.

@mggevaer commented on GitHub (Oct 7, 2025): I have no experience with DSQL, but I does seem like AWS advertises API compatibility with MongoDB: https://aws.amazon.com/documentdb/features/#topic-4 And MongoDB is supported by better-auth. Perhaps that is a low-effort, low-maintenance way to get DynamoDB support? DSQL is quite new, DynamoDB does seem to be supported by a bit more infra managing projects such as SST.
Author
Owner

@anay-208 commented on GitHub (Oct 7, 2025):

@mggevaer The article you have sent is for DocumentDB, Not DynamoDB.

DocumentDB is mongodb compatible but DynamoDB isn't?

@anay-208 commented on GitHub (Oct 7, 2025): @mggevaer The article you have sent is for DocumentDB, Not DynamoDB. DocumentDB is mongodb compatible but DynamoDB isn't?
Author
Owner

@anay-208 commented on GitHub (Oct 7, 2025):

@bestickley We can create indexes and just query on the data, won't that work fine?

@anay-208 commented on GitHub (Oct 7, 2025): @bestickley We can create indexes and just query on the data, won't that work fine?
Author
Owner

@bestickley commented on GitHub (Oct 7, 2025):

@anay-208, no. DDB indexes do not allow for the full feature set of querying/filtering that SQL provides as DDB is a key-value data store. You can get creative with begins_with on SKs but it's still pretty limiting and not what DDB is intended for. BetterAuth's list APIs won't fully work. You'll only be able to filter based on features that DDB provides. You'll always need equality operator on PK and can only use the KeyConditionExpressions for SKs shown here.

However, DDB is perfect for secondary storage which only requires key-value store. See my sample implementation above.

@bestickley commented on GitHub (Oct 7, 2025): @anay-208, no. DDB indexes do not allow for the full feature set of querying/filtering that SQL provides as DDB is a key-value data store. You can get creative with `begins_with` on SKs but it's still pretty limiting and not what DDB is intended for. BetterAuth's list APIs won't fully work. You'll only be able to filter based on features that DDB provides. You'll always need equality operator on PK and can only use the `KeyConditionExpression`s for SKs shown [here](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.KeyConditionExpressions.html#Query.KeyConditionExpressions-example). However, DDB is perfect for secondary storage which only requires key-value store. See my sample implementation above.
Author
Owner

@mggevaer commented on GitHub (Oct 7, 2025):

@anay-208 You are right, apologies for wasting everyone's time.

@mggevaer commented on GitHub (Oct 7, 2025): @anay-208 You are right, apologies for wasting everyone's time.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#45