The following was written by @Fumler and shared in the announcements board.
Once the default (TypeORM) adapter is production ready, and the adapter API is more stable I'd like to have a go at implementing, so am saving a copy of it here where it's easy to find.
Note: This example probably doesn't work exactly as is with the current version as a few things have changed during the beta, but it should be close enough it's possible to adapt it easily!
model User {
id String @default(uuid()) @id
email String @unique
avatarUrl String?
name String?
sessions Session[]
accounts Account[]
createdAt DateTime @default(now())
}
model Session {
id String @default(uuid()) @id
userId String
user User @relation(fields: [userId], references: [id])
accessToken String @default(cuid()) @unique
accessTokenExpires DateTime
expires DateTime
createdAt DateTime @default(now())
}
model Account {
id String @default(uuid()) @id
userId String
user User @relation(fields: [userId], references: [id])
providerId String
providerType String
providerAccountId String @unique
refreshToken String?
accessToken String
accessTokenExpires DateTime?
createdAt DateTime @default(now())
}
import { PrismaClient, User } from '@prisma/client'
interface Profile {
name: string
email: string
image: string
}
const PrismaAdapter = () => {
function debug(...args) {
if (process.env.NODE_ENV === 'development') console.log(...args)
}
let connection = new PrismaClient()
async function getAdapter() {
if (!connection) {
connection = new PrismaClient()
}
// Called when a user signs in
async function createUser(profile: Profile) {
debug('Create user account', profile)
return connection.user.create({
data: {
name: profile.name,
email: profile.email,
avatarUrl: profile.image,
},
})
}
async function updateUser(user: User) {
debug('Update user account', user)
return new Promise((resolve, reject) => {
// @TODO Save changes to user object in DB
resolve(true)
})
}
async function getUserById(id = '') {
debug('Get user account by ID', id)
return connection.user.findOne({ where: { id } })
}
async function getUserByProviderAccountId(providerId: string, providerAccountId: string) {
debug('Get user account by provider account ID', providerId, providerAccountId)
return connection.account
.findOne({
where: { providerAccountId },
})
.user()
}
async function getUserByEmail(email: string) {
debug('Get user account by email address', email)
return new Promise((resolve, reject) => {
// @TODO Get user from DB
resolve(false)
})
}
async function getUserByCredentials(credentials: string) {
debug('Get user account by credentials', credentials)
return new Promise((resolve, reject) => {
// @TODO Get user from DB
resolve(true)
})
}
async function deleteUserById(userId: string) {
debug('Delete user account', userId)
return new Promise((resolve, reject) => {
// @TODO Delete user from DB
resolve(true)
})
}
async function linkAccount(
userId: string,
providerId: string,
providerType: string,
providerAccountId: string,
refreshToken: string,
accessToken: string,
accessTokenExpires: string
) {
debug(
'Link provider account',
userId,
providerId,
providerType,
providerAccountId,
refreshToken,
accessToken,
accessTokenExpires
)
return connection.account.create({
data: {
accessToken,
refreshToken,
providerAccountId,
providerId,
providerType,
user: {
connect: {
id: userId,
},
},
accessTokenExpires,
},
})
}
async function unlinkAccount(userId: string, providerId: string, providerAccountId: string) {
debug('Unlink provider account', userId, providerId, providerAccountId)
return new Promise((resolve, reject) => {
// @TODO Get current user from DB
// @TODO Delete [provider] object from user object
// @TODO Save changes to user object in DB
resolve(true)
})
}
async function createSession(user: User) {
debug('Create session for user', user)
const date = new Date()
const sessionExpiryInDays = 30
const accessTokenExpiryInDays = 30
return connection.session.create({
data: {
accessTokenExpires: new Date(
date.setDate(date.getDate() + accessTokenExpiryInDays)
).toISOString(),
expires: new Date(date.setDate(date.getDate() + sessionExpiryInDays)).toISOString(),
user: {
connect: {
id: user.id,
},
},
},
})
}
async function getSessionById(id = '') {
debug('Get session by ID', id)
return connection.session.findOne({ where: { id } })
}
async function deleteSessionById(id: string) {
debug('Delete session by ID', id)
return connection.session.delete({ where: { id } })
}
return Promise.resolve({
createUser,
updateUser,
getUserById,
getUserByProviderAccountId,
getUserByEmail,
getUserByCredentials,
deleteUserById,
linkAccount,
unlinkAccount,
createSession,
getSessionById,
deleteSessionById,
})
}
return {
getAdapter,
}
}
export default PrismaAdapter
Very excited about the possibility of a first-party Prisma adapter for this amazing library!
Cool! I had just ran prisma instrospect on the nextauth tables haha
This is now implemented in the v3 beta #302
Most helpful comment
This is now implemented in the v3 beta #302