Prisma schema changes when moving from NextAuth to Clerk

With NextAuth I have a User schema that contains many relation fields. For example:
model User {
id Int @id @default(autoincrement())
posts Post[]
}

model Post {
id Int @id @default(autoincrement())
author User @relation(fields: [authorId], references: [id])
authorId Int // relation scalar field (used in the `@relation` attribute above)
}
model User {
id Int @id @default(autoincrement())
posts Post[]
}

model Post {
id Int @id @default(autoincrement())
author User @relation(fields: [authorId], references: [id])
authorId Int // relation scalar field (used in the `@relation` attribute above)
}
If I were to move to clerk and therefore no longer manage my users in my database, would I just have to delete the User model and then remove the relation in the Post like so?
model Post {
id Int @id @default(autoincrement())
authorId Int
}
model Post {
id Int @id @default(autoincrement())
authorId Int
}
and then adjust my procedures accordingly? Is this all I would have to do or are there other changes to keep in mind?
13 Replies
whatplan
whatplan15mo ago
heres what I did
model Account {
clerkId String @id @unique
//any other data you want to store with an account

@@index([clerkId])
}
model Account {
clerkId String @id @unique
//any other data you want to store with an account

@@index([clerkId])
}
and then whenever you have a write involving a account just upsert
await ctx.prisma.account.upsert({
where: {
clerkId: ctx.auth.userId,
},
update: {
// ...
},
create: {
// ...
},
})
await ctx.prisma.account.upsert({
where: {
clerkId: ctx.auth.userId,
},
update: {
// ...
},
create: {
// ...
},
})
Ramsay
Ramsay15mo ago
Cool thanks
Unknown User
Unknown User15mo ago
Message Not Public
Sign In & Join Server To View
whatplan
whatplan15mo ago
did not even know that was a thing
whatplan
whatplan15mo ago
Sync data to your backend | Clerk
Learn how to sync Clerk data to your application's database
whatplan
whatplan15mo ago
@Ramsay I would look into this as well
import type { NextApiRequest, NextApiResponse } from "next";
import { prisma } from "~/server/clients/db";
import { z } from "zod";

export const clerkWebhookSchema = z.object({
type: z.enum(["user.created", "user.deleted"]),
data: z.object({
id: z.string(),
}),
});

export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
console.log(req);

if (req.method !== "POST") {
res.status(405).json({ message: "Method not allowed" });
return;
} else {
const valid = clerkWebhookSchema.safeParse(req.body);
if (!valid.success) {
res.status(400).json({ message: "Invalid request" });
return;
} else {
const parsed = valid.data;

switch (parsed.type) {
case "user.created": {
await prisma.account.create({
data: {
clerkId: parsed.data.id,
},
});
console.log("user created", parsed.data.id);
break;
}
case "user.deleted": {
await prisma.account.delete({
where: {
clerkId: parsed.data.id,
},
});
console.log("user deleted", parsed.data.id);
break;
}
}
res.status(200).end();
}
}
}
import type { NextApiRequest, NextApiResponse } from "next";
import { prisma } from "~/server/clients/db";
import { z } from "zod";

export const clerkWebhookSchema = z.object({
type: z.enum(["user.created", "user.deleted"]),
data: z.object({
id: z.string(),
}),
});

export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
console.log(req);

if (req.method !== "POST") {
res.status(405).json({ message: "Method not allowed" });
return;
} else {
const valid = clerkWebhookSchema.safeParse(req.body);
if (!valid.success) {
res.status(400).json({ message: "Invalid request" });
return;
} else {
const parsed = valid.data;

switch (parsed.type) {
case "user.created": {
await prisma.account.create({
data: {
clerkId: parsed.data.id,
},
});
console.log("user created", parsed.data.id);
break;
}
case "user.deleted": {
await prisma.account.delete({
where: {
clerkId: parsed.data.id,
},
});
console.log("user deleted", parsed.data.id);
break;
}
}
res.status(200).end();
}
}
}
heres what I am moving to then you can just
await ctx.prisma.account.update({
where: {
clerkId: ctx.auth.userId,
},
data: { ... }
})
await ctx.prisma.account.update({
where: {
clerkId: ctx.auth.userId,
},
data: { ... }
})
the one asterisk is that as pointed out in the clerk page I linked it is asynchronous, so there is a slight delay between a user creating an account and the account existing in the database. Should be fine as long as you arent doing anything with that account at the exact same time as they sign in actually I was going to say theres no need to store the other data besides the id in my database because it would just get out of sync with clerk, and its easy enough to fetch from clerk whenever you need a users email or something but with a webhook you are informed whenever a update changes so you could have a cached version of the user's entire info if you wanted that updates on a webhook just depends on your usecase
Ramsay
Ramsay15mo ago
Awesome! I'll check that out
Unknown User
Unknown User15mo ago
Message Not Public
Sign In & Join Server To View
whatplan
whatplan15mo ago
So full disclaimer I am not a database person so very open to being corrected on this. Basically my understanding (I watched this video https://youtu.be/xAQga907NVU) is that indexing is a way to store a specific part of your table in a faster to search data structure. In this case we know that the majority of our operations on this table will be searching by ID, so by indexing the ID we are greatly improving the performance of any lookups.
Computer Science
YouTube
Database Index Fundamentals
This video explains the fundamental principles of indexing table columns in a database to speed up queries. It illustrates the difference between clustered indexes and non-clustered indexes, which are also known as secondary keys. It explains that the primary key of a table is normally the one and only clustered index, because this defines the...
Unknown User
Unknown User15mo ago
Message Not Public
Sign In & Join Server To View
whatplan
whatplan15mo ago
thanks for sending this, my method was very scrappy and this looks like the right way to do this only question (and just lmk if this is a question for clerk and not you) is how do I replicate the svix header signature during testing (I'm using webhookthing) I see these are what they should be but just copying over is not valid
Unknown User
Unknown User15mo ago
Message Not Public
Sign In & Join Server To View
whatplan
whatplan15mo ago
Just for any future people who search this clerk is working on a intergration with webhookthing: https://discord.com/channels/856971667393609759/1082526277216522251/1087493688453771346