Skip to content

feat: drop user id generation in createUser #9381

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
13 changes: 4 additions & 9 deletions packages/adapter-azure-tables/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,24 +94,19 @@ export function withoutKeys<T>(
export const TableStorageAdapter = (client: TableClient): Adapter => {
return {
async createUser(user) {
const id = crypto.randomUUID()
const newUser = {
...user,
id,
}
await Promise.all([
client.createEntity({
...newUser,
...user,
partitionKey: keys.userByEmail,
rowKey: user.email,
}),
client.createEntity({
...newUser,
...user,
partitionKey: keys.user,
rowKey: id,
rowKey: user.id,
}),
])
return newUser
return user
},
async getUser(id: string) {
try {
Expand Down
5 changes: 2 additions & 3 deletions packages/adapter-d1/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,15 +248,14 @@ export function D1Adapter(db: D1Database): Adapter {

return {
async createUser(user) {
const id: string = crypto.randomUUID()
const createBindings = [
id,
user.id,
user.name,
user.email,
user.emailVerified?.toISOString(),
user.image,
]
const getBindings = [id]
const getBindings = [user.id]

const newUser = await createRecord<AdapterUser>(
db,
Expand Down
13 changes: 3 additions & 10 deletions packages/adapter-drizzle/src/lib/mysql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,9 @@ export function mySqlDrizzleAdapter(
createTables(tableFn)

return {
async createUser(data) {
const id = crypto.randomUUID()

await client.insert(users).values({ ...data, id })

return await client
.select()
.from(users)
.where(eq(users.id, id))
.then((res) => res[0])
async createUser(user) {
await client.insert(users).values(user)
return user
},
async getUser(data) {
const thing =
Expand Down
4 changes: 2 additions & 2 deletions packages/adapter-drizzle/src/lib/pg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,10 @@ export function pgDrizzleAdapter(
createTables(tableFn)

return {
async createUser(data) {
async createUser(user) {
return await client
.insert(users)
.values({ ...data, id: crypto.randomUUID() })
.values(user)
.returning()
.then((res) => res[0] ?? null)
},
Expand Down
8 changes: 2 additions & 6 deletions packages/adapter-drizzle/src/lib/sqlite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,8 @@ export function SQLiteDrizzleAdapter(
createTables(tableFn)

return {
async createUser(data) {
return await client
.insert(users)
.values({ ...data, id: crypto.randomUUID() })
.returning()
.get()
async createUser(user) {
return await client.insert(users).values(user).returning().get()
},
async getUser(data) {
const result = await client
Expand Down
3 changes: 2 additions & 1 deletion packages/adapter-drizzle/tests/fixtures.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// This work is needed as workaround to Drizzle truncating millisecond precision.
// This is needed as a workaround to Drizzle truncating millisecond precision.
// https://github.com/drizzle-team/drizzle-orm/pull/668

import { randomUUID } from "utils/adapter"
Expand All @@ -17,6 +17,7 @@ ONE_MONTH_FROM_NOW.setMilliseconds(0)

export const fixtures = {
user: {
id: randomUUID(),
email: "[email protected]",
image: "https://www.fillmurray.com/460/300",
name: "Fill Murray",
Expand Down
7 changes: 1 addition & 6 deletions packages/adapter-dynamodb/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,12 +183,7 @@ export function DynamoDBAdapter(
const GSI1SK = options?.indexSortKey ?? "GSI1SK"

return {
async createUser(data) {
const user: AdapterUser = {
...(data as any),
id: crypto.randomUUID(),
}

async createUser(user) {
await client.put({
TableName,
Item: format.to({
Expand Down
3 changes: 1 addition & 2 deletions packages/adapter-kysely/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,7 @@ export function KyselyAdapter(db: Kysely<Database>): Adapter {
/** If the database is SQLite, turn ISO strings into dates */
const from = isSqlite ? format.from : <T>(x: T) => x as T
return {
async createUser(data) {
const user = { ...data, id: crypto.randomUUID() }
async createUser(user) {
await db.insertInto("User").values(to(user)).executeTakeFirstOrThrow()
return user
},
Expand Down
149 changes: 58 additions & 91 deletions packages/adapter-mongodb/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,38 +52,31 @@ export const defaultCollections: Required<
}

export const format = {
/** Takes a mongoDB object and returns a plain old JavaScript object */
from<T = Record<string, unknown>>(object: Record<string, any>): T {
const newObject: Record<string, unknown> = {}
for (const key in object) {
const value = object[key]
if (key === "_id") {
newObject.id = value.toHexString()
} else if (key === "userId") {
newObject[key] = value.toHexString()
} else {
newObject[key] = value
}
}
return newObject as T
/** Takes an object that's coming from a database and converts it to plain JavaScript. */
from<T extends Record<string, any> | null>(object: T, withId?: boolean) {
if (!object) return null
const newObject: any = {}
for (const [key, value] of Object.entries(object))
if (key === "_id") newObject.id = value.toHexString()
else if (key === "userId") newObject[key] = value.toHexString()
else newObject[key] = value
if (!withId) delete newObject.id
return newObject
},
/** Takes a plain old JavaScript object and turns it into a mongoDB object */
to<T = Record<string, unknown>>(object: Record<string, any>) {
const newObject: Record<string, unknown> = {
_id: _id(object.id),
}
for (const key in object) {
const value = object[key]
if (key === "userId") newObject[key] = _id(value)
else if (key === "id") continue
/** Takes an object that's coming from Auth.js and prepares it to be written to the database. */
to<T extends Record<string, any>>(object: T) {
const newObject: any = {}
for (const [key, value] of Object.entries(object))
if (key === "id") newObject._id = _id(value)
else if (key === "userId") newObject[key] = _id(value)
else newObject[key] = value
}
return newObject as T & { _id: ObjectId }
return newObject as NonNullable<T>
},
}

/** @internal */
export function _id(hex?: string) {
export function _id(hex?: any) {
if (hex instanceof ObjectId) return hex
if (hex?.length !== 24) return new ObjectId()
return new ObjectId(hex)
}
Expand Down Expand Up @@ -147,123 +140,97 @@ export function MongoDBAdapter(
client: Promise<MongoClient>,
options: MongoDBAdapterOptions = {}
): Adapter {
const { collections } = options
const { from, to } = format
const c = { ...defaultCollections, ...options.collections }

const db = (async () => {
const _db = (await client).db(options.databaseName)
const c = { ...defaultCollections, ...collections }
const db = client.then((db) => {
const _db = db.db(options.databaseName)
return {
U: _db.collection<AdapterUser>(c.Users),
A: _db.collection<AdapterAccount>(c.Accounts),
S: _db.collection<AdapterSession>(c.Sessions),
V: _db.collection<VerificationToken>(c?.VerificationTokens),
}
})()
})

return {
async createUser(data) {
const user = to<AdapterUser>(data)
const user = to(data)
await (await db).U.insertOne(user)
return from<AdapterUser>(user)
return from(user, true)
},
async getUser(id) {
const user = await (await db).U.findOne({ _id: _id(id) })
if (!user) return null
return from<AdapterUser>(user)
return from(await (await db).U.findOne({ _id: _id(id) }), true)
},
async getUserByEmail(email) {
const user = await (await db).U.findOne({ email })
if (!user) return null
return from<AdapterUser>(user)
return from(await (await db).U.findOne({ email }), true)
},
async getUserByAccount(provider_providerAccountId) {
const account = await (await db).A.findOne(provider_providerAccountId)
if (!account) return null
const user = await (
await db
).U.findOne({ _id: new ObjectId(account.userId) })
const user = await (await db).U.findOne({ _id: _id(account.userId) })
if (!user) return null
return from<AdapterUser>(user)
return from(user, true)
},
async updateUser(data) {
const { _id, ...user } = to<AdapterUser>(data)

const result = await (
const user = await (
await db
).U.findOneAndUpdate({ _id }, { $set: user }, { returnDocument: "after" })

return from<AdapterUser>(result!)
).U.findOneAndUpdate(
{ _id: _id(data.id) },
{ $set: data },
{ returnDocument: "after" }
)
return from(user, true)
},
async deleteUser(id) {
const userId = _id(id)
const userId: any = _id(id)
const m = await db
await Promise.all([
m.A.deleteMany({ userId: userId as any }),
m.S.deleteMany({ userId: userId as any }),
m.U.deleteOne({ _id: userId }),
const [user] = await Promise.all([
m.U.findOneAndDelete({ _id: userId }),
m.A.deleteMany({ userId }),
m.S.deleteMany({ userId }),
])
return from(user, true)
},
linkAccount: async (data) => {
const account = to<AdapterAccount>(data)
await (await db).A.insertOne(account)
async linkAccount(account) {
await (await db).A.insertOne(to(account))
return account
},
async unlinkAccount(provider_providerAccountId) {
const account = await (
await db
).A.findOneAndDelete(provider_providerAccountId)
return from<AdapterAccount>(account!)
return from(account)
},
async getSessionAndUser(sessionToken) {
const session = await (await db).S.findOne({ sessionToken })
if (!session) return null
const user = await (
await db
).U.findOne({ _id: new ObjectId(session.userId) })
if (!user) return null
return {
user: from<AdapterUser>(user),
session: from<AdapterSession>(session),
}
const user = await (await db).U.findOne({ _id: _id(session.userId) })
return { user: from(user, true), session: from(session) }
},
async createSession(data) {
const session = to<AdapterSession>(data)
await (await db).S.insertOne(session)
return from<AdapterSession>(session)
async createSession(session) {
await (await db).S.insertOne(to(session))
return session
},
async updateSession(data) {
const { _id, ...session } = to<AdapterSession>(data)

async updateSession(session) {
const updatedSession = await (
await db
).S.findOneAndUpdate(
{ sessionToken: session.sessionToken },
{ $set: session },
{ returnDocument: "after" }
)
return from<AdapterSession>(updatedSession!)
return from(updatedSession)
},
async deleteSession(sessionToken) {
const session = await (
await db
).S.findOneAndDelete({
sessionToken,
})
return from<AdapterSession>(session!)
return from(await (await db).S.findOneAndDelete({ sessionToken }))
},
async createVerificationToken(data) {
await (await db).V.insertOne(to(data))
return data
async createVerificationToken(verificationToken) {
await (await db).V.insertOne(to(verificationToken))
return verificationToken
},
async useVerificationToken(identifier_token) {
const verificationToken = await (
await db
).V.findOneAndDelete(identifier_token)

if (!verificationToken) return null
const { _id, ...rest } = verificationToken
return rest
async useVerificationToken(params) {
return from(await (await db).V.findOneAndDelete(params))
},
}
}
Loading