Help with Seeding Existing Users into Better Auth

Hi everyone, I’m looking for guidance on integrating my existing users with Better Auth. My users are already formatted in JSON, all using email/password authentication, and their passwords are already encrypted. I’ve already set up Better Auth’s options to work correctly with BCryptJS. My setup: Database: Neon PostgreSQL ORM: Drizzle Framework: Next.js 15 My concerns: User IDs – Better Auth seems to generate its own IDs rather than using the database’s auto-incremented IDs. Accounts Table – Since my users log in via email/password, I believe I need to create an Account record for each user. I’m unsure how to properly seed my existing users so that Better Auth recognizes them. If anyone has experience with this or can point me in the right direction, I’d really appreciate the help! Thanks for your time!
24 Replies
Jon Higger (He / Him)
It's not ideal but what I did was this, and I just made sure my local server was running when doing so:
import authClient from "../auth-client";
import { db } from "./db";
import { account, user } from "./schema/auth.schema";

async function seed() {
console.log("🌱 Seeding database...");

// Clean up existing data
await db.delete(account);
await db.delete(user);

await authClient.signUp
.email({
password: "Password123",
name: "jonathan higger",
})
.then(console.log);

await authClient.signUp
.email({
password: "Password123",
name: "jimmy userton",
})
.then(console.log);
// // Create regular user with email/password
}
seed()
.catch((error) => {
console.error("Error seeding database:", error);
process.exit(1);
})
.finally(() => {
process.exit();
});
import authClient from "../auth-client";
import { db } from "./db";
import { account, user } from "./schema/auth.schema";

async function seed() {
console.log("🌱 Seeding database...");

// Clean up existing data
await db.delete(account);
await db.delete(user);

await authClient.signUp
.email({
password: "Password123",
name: "jonathan higger",
})
.then(console.log);

await authClient.signUp
.email({
password: "Password123",
name: "jimmy userton",
})
.then(console.log);
// // Create regular user with email/password
}
seed()
.catch((error) => {
console.error("Error seeding database:", error);
process.exit(1);
})
.finally(() => {
process.exit();
});
It would be super nice if there were a better auth server instance that you don't have to authenticate with
DragonCoder99
DragonCoder99OP2mo ago
Wouldn't that end up re-encrypting the already encrypted passwords, rendering them unusable?! 😦
lonelyplanet
lonelyplanet2mo ago
Encypted or Hashed? encyption can be reversed hashing cant be.
DragonCoder99
DragonCoder99OP2mo ago
Hashed ofcourse it are passwords
lonelyplanet
lonelyplanet2mo ago
Just asking for clarity. Just migrate the database by mapping your columns to the betterauth columns and then Setup better auth custom hash and verify functions to work with your current verify and hashing
lonelyplanet
lonelyplanet2mo ago
Email & Password | Better Auth
Implementing email and password authentication with Better Auth.
DragonCoder99
DragonCoder99OP2mo ago
No worries and thanks!
lonelyplanet
lonelyplanet2mo ago
I missed your concerns sorry! UserIds - https://www.better-auth.com/docs/concepts/database#id-generation Accounts - if you currently have a single table like
+-------------------+------------------+---------+
| user_email | password_hash | id_user |
+-------------------+------------------+---------+
| [email protected] | Vfafaf2asDAF23 | 123 |
| [email protected] | aFAsh23ifdhanfgs | 124 |
+-------------------+------------------+---------+
+-------------------+------------------+---------+
| user_email | password_hash | id_user |
+-------------------+------------------+---------+
| [email protected] | Vfafaf2asDAF23 | 123 |
| [email protected] | aFAsh23ifdhanfgs | 124 |
+-------------------+------------------+---------+
you will need to create both a user record in your db with the better auth fields and create a related account field with the password User Schema, Account Schema You would want to work with the database directly in the migration process and not use the BetterAuth API to ensure existing data is migrated properly use SQL directly or Drizzle ORM in a script and highly recommend using Transactions where possible.
Database | Better Auth
Learn how to use a database with Better Auth.
lonelyplanet
lonelyplanet2mo ago
@DragonCoder99
DragonCoder99
DragonCoder99OP2mo ago
I tried using the additional fields, but it wants be to included all those fields every time every time; otherwise, I get a type error.
export const auth = betterAuth({
...stuff,
user: {
additionalFields: {
oldId: {
type: "string",
nullable: true,
optional: true,
},
oldFormat: {
type: "boolean",
defaultValue: false,
optional: true,
},
firstName: {
type: "string",
nullable: true,
optional: true,
},
lastName: {
type: "string",
nullable: true,
optional: true,
},
phone: {
type: "string",
nullable: true,
optional: true,
},
zipCode: {
type: "string",
nullable: true,
optional: true,
},
defaultTip: {
type: "number",
defaultValue: 0,
optional: true,
},
defaultPaymentType: {
type: "string",
defaultValue: "cash",
optional: true,
},
town: {
type: "string",
defaultValue: "Brooklyn",
optional: true,
},
isBusiness: {
type: "boolean",
defaultValue: false,
optional: true,
},
businessName: {
type: "string",
nullable: true,
optional: true,
},
businessType: {
type: "string",
nullable: true,
optional: true,
},
isAccountSetupComplete: {
type: "boolean",
defaultValue: false,
optional: true,
},
adminNotes: {
type: "string",
nullable: true,
optional: true,
},
},
deleteUser: {
enabled: false,
},
},
plugins: [admin(), twoFactor(), nextCookies()],
});
export const auth = betterAuth({
...stuff,
user: {
additionalFields: {
oldId: {
type: "string",
nullable: true,
optional: true,
},
oldFormat: {
type: "boolean",
defaultValue: false,
optional: true,
},
firstName: {
type: "string",
nullable: true,
optional: true,
},
lastName: {
type: "string",
nullable: true,
optional: true,
},
phone: {
type: "string",
nullable: true,
optional: true,
},
zipCode: {
type: "string",
nullable: true,
optional: true,
},
defaultTip: {
type: "number",
defaultValue: 0,
optional: true,
},
defaultPaymentType: {
type: "string",
defaultValue: "cash",
optional: true,
},
town: {
type: "string",
defaultValue: "Brooklyn",
optional: true,
},
isBusiness: {
type: "boolean",
defaultValue: false,
optional: true,
},
businessName: {
type: "string",
nullable: true,
optional: true,
},
businessType: {
type: "string",
nullable: true,
optional: true,
},
isAccountSetupComplete: {
type: "boolean",
defaultValue: false,
optional: true,
},
adminNotes: {
type: "string",
nullable: true,
optional: true,
},
},
deleteUser: {
enabled: false,
},
},
plugins: [admin(), twoFactor(), nextCookies()],
});
lonelyplanet
lonelyplanet2mo ago
1. If your getting errors on the authclient read up about inferring addtional fields (Read the whole typescript page, is useful) if your have numbered ID's Interger than convert to string before migrating.
TypeScript | Better Auth
Better Auth TypeScript integration.
lonelyplanet
lonelyplanet2mo ago
2. If the fields should not be updated by the user use input:false as in documentation and you dont have to add your extra fields to the adtionalfields column you could instead use hooks https://www.better-auth.com/docs/concepts/database#database-hooks to fill in those extra fields on signup like adminNotes you could leave it out and add a hook before hook that adds the adminNotes field to be like "User Signed up", If you need to update the adminNotes field use your ORM directly. 3. I dont recommend adding oldId as means more complexity use the single userId filed and migrate your current ids to strings if they are Ints and use the advanded option to either let the db generate Ids or add a custom id generate function
Database | Better Auth
Learn how to use a database with Better Auth.
DragonCoder99
DragonCoder99OP2mo ago
That didn't help I still get errors on my "body" in the better-auth functions.
await auth.api.signUpEmail({
// here typescript get mad on me, for not including all the fields...
body: {
password: "1PASS23456",
name: "Testing Tester",
},
});
await auth.api.signUpEmail({
// here typescript get mad on me, for not including all the fields...
body: {
password: "1PASS23456",
name: "Testing Tester",
},
});
lonelyplanet
lonelyplanet2mo ago
Are you using signUpEmail to migrate? you shouldn't instead use SQL directly or ORM and use db transactions
DragonCoder99
DragonCoder99OP2mo ago
yeah kinda what the first answer told me to do 😦
lonelyplanet
lonelyplanet2mo ago
You cant since signUpEmail takes the plaintext password which since you have hashed passwords you can do it.
DragonCoder99
DragonCoder99OP2mo ago
yeah will try to do it with Drizzle it self
lonelyplanet
lonelyplanet2mo ago
Unless you have plaintext password then you can. I think the confussion can when you said seeding as this usally referrs to mockdata. You sound like you need to migrate user data
DragonCoder99
DragonCoder99OP2mo ago
yep indeed I need to migrate and only got the hashed version of the passwords
lonelyplanet
lonelyplanet2mo ago
In that case use SQL or Drizzle and write a script to do it for you
DragonCoder99
DragonCoder99OP2mo ago
I'm on it thanks for all your help @lonelyplanet !!!
lonelyplanet
lonelyplanet2mo ago
Your welcome again couldnt stress it enough https://orm.drizzle.team/docs/transactions, I promise you can save your self from failed half migrations
Ping
Ping2mo ago
If it helps at all, my seeding package is open source which you can take inspiration: https://www.better-auth-kit.com/docs/cli/seed
Better Auth Kit
Better Auth Kit
A handy collection of plugins, adapters, libraries and more.
Ping
Ping2mo ago
You guys have a different usage of seeding existing users, (and it's still do-able with my package), but it's primarily made for random data seeding.

Did you find this page helpful?