epsilon42
epsilon42
Explore posts from servers
TTCTheo's Typesafe Cult
Created by epsilon42 on 7/27/2024 in #questions
useQuery in NextJS app router client components
No description
4 replies
DTDrizzle Team
Created by epsilon42 on 5/15/2024 in #help
.unique() doesn't seem to work
I am coming from a front end background so am in unfamiliar territory with DB/Postgres stuff so apologies if I've missed something obvious. I started with the following table in Drizzle (removed irrelevant columns):

export const users = createTable(
"user",
{
id: uuid("id").defaultRandom().primaryKey(),
username: varchar("username", { length: 32 })
}
);

export const users = createTable(
"user",
{
id: uuid("id").defaultRandom().primaryKey(),
username: varchar("username", { length: 32 })
}
);
I then wanted to ensure that the username column was unique so changed it to:

export const users = createTable(
"user",
{
id: uuid("id").defaultRandom().primaryKey(),
username: varchar("username", { length: 32 }).unique()
}
);

export const users = createTable(
"user",
{
id: uuid("id").defaultRandom().primaryKey(),
username: varchar("username", { length: 32 }).unique()
}
);
After doing a drizzle-kit push I was expecting it to say that changes needed to be applied but to my surprise it just said "No changes detected". I then checked for uniqueness by going to Drizzle Studio and found I was still able to have multiple rows with the same username. The only way I was able to get the unique constraint to work properly was by doing:
export const users = createTable(
"user",
{
id: uuid("id").defaultRandom().primaryKey(),
username: varchar("username", { length: 32 })
},
(table) => {
return {
usernameIndex: uniqueIndex("usernameIndex").on(table.username),
};
},
);
export const users = createTable(
"user",
{
id: uuid("id").defaultRandom().primaryKey(),
username: varchar("username", { length: 32 })
},
(table) => {
return {
usernameIndex: uniqueIndex("usernameIndex").on(table.username),
};
},
);
Am I missing something with how .unique() should be used or what it's for? Thanks.
4 replies
TTCTheo's Typesafe Cult
Created by epsilon42 on 5/13/2024 in #questions
How to prevent multiple DB calls in this situation
I am using T3 stack with: - Drizzle - WorkOS AuthKit - Next JS app router The auth/user data from AuthKit only has email address, first/last name so I wanted to store some additional data for each user like username so as a result I created a custom getUser function that wraps the AuthKit getUser function:

// app/_lib/getUser.ts

import { getUser as workOsGetUser } from "@workos-inc/authkit-nextjs";
import { db } from "~/server/db";
import { users } from "~/server/db/schema";

interface Options {
ensureSignedIn?: boolean;
}

// Only use this getUser in /app/
export const getUser = async (options: Options = {}) => {
console.log("customGetUser");
const { ensureSignedIn } = options;
const authUser = ensureSignedIn
? await workOsGetUser({ ensureSignedIn: true })
: await workOsGetUser();

let dbUser;
if (authUser.user) {
dbUser = await db.query.users.findFirst({
where: (model, { eq }) => eq(model.authProviderId, authUser.user.id),
});
if (!dbUser) {
dbUser = await db
.insert(users)
.values({
authProviderId: authUser.user.id,
})
.returning();
}
}

const singleDbUser = Array.isArray(dbUser) ? dbUser[0] : dbUser;

return {
user: authUser.user,
role: authUser.role,
dbUser: singleDbUser,
hasRegistered: authUser.user && dbUser,
hasUsername: singleDbUser?.username,
};
};

// app/_lib/getUser.ts

import { getUser as workOsGetUser } from "@workos-inc/authkit-nextjs";
import { db } from "~/server/db";
import { users } from "~/server/db/schema";

interface Options {
ensureSignedIn?: boolean;
}

// Only use this getUser in /app/
export const getUser = async (options: Options = {}) => {
console.log("customGetUser");
const { ensureSignedIn } = options;
const authUser = ensureSignedIn
? await workOsGetUser({ ensureSignedIn: true })
: await workOsGetUser();

let dbUser;
if (authUser.user) {
dbUser = await db.query.users.findFirst({
where: (model, { eq }) => eq(model.authProviderId, authUser.user.id),
});
if (!dbUser) {
dbUser = await db
.insert(users)
.values({
authProviderId: authUser.user.id,
})
.returning();
}
}

const singleDbUser = Array.isArray(dbUser) ? dbUser[0] : dbUser;

return {
user: authUser.user,
role: authUser.role,
dbUser: singleDbUser,
hasRegistered: authUser.user && dbUser,
hasUsername: singleDbUser?.username,
};
};
8 replies
TTCTheo's Typesafe Cult
Created by epsilon42 on 5/8/2024 in #questions
Help me understand third party auth and storing user information
Do most apps that use third party auth still maintain their own Users table? I have very little experience on backend/auth so lack a fundamental understanding of how this works, but the basic examples I've followed using Clerk simply displays the currently logged in users name and email address which is stored in the JWT so I'm uncertain if I'm missing something where a Users table isn't actually required. Say for instance I have a blog with multiple authors/users and I wanted a page that displayed a list of all posts and the name of the user who posted it. To get the names of all the post authors, how would you do this? I'm guessing this means also maintaining a Users table which duplicates the user meta data any time a user signs up. This I guess would also give the flexibility of adding additional things like: username, date of birth, favourite Pokemon, etc. Is this duplcating user data into Users table handled in the auth call back every time a user logs in (i.e. check if user exists in DB and create them if they don't), or is this done elsewhere? FWIW: I'm using WorkOS AuthKit for auth.
2 replies
TTCTheo's Typesafe Cult
Created by epsilon42 on 12/28/2023 in #questions
Does Account table/model in create-t3-app get used at all if using EmailProvider for NextAuth?
In the Prisma schema for a create-t3-app with NextAuth, the following Account model is in the schema.prisma file:

// Necessary for Next auth
model Account {
id String @id @default(cuid())
userId String
type String
provider String
providerAccountId String
refresh_token String? @db.Text
access_token String? @db.Text
expires_at Int?
token_type String?
scope String?
id_token String? @db.Text
session_state String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)

@@unique([provider, providerAccountId])
}

// Necessary for Next auth
model Account {
id String @id @default(cuid())
userId String
type String
provider String
providerAccountId String
refresh_token String? @db.Text
access_token String? @db.Text
expires_at Int?
token_type String?
scope String?
id_token String? @db.Text
session_state String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)

@@unique([provider, providerAccountId])
}
I have set up the project to use EmailProvider for NextAuth so I can sign in using a magic link. However, when I view the Account table in Prisma Studio, I don't ever see anything in the Account table. What is this table used for? Is it only used for other providers? TIA
2 replies
TTCTheo's Typesafe Cult
Created by epsilon42 on 11/28/2023 in #questions
Function sometimes running twice when deployed to Vercel
I'm experiencing an issue with my NextJS app (create-t3-app starter) where an API route is intermittently running twice when called when deployed to Vercel. I've also posted the issue on the Vercel discussions on Github so take a look here for the function code and more details: https://github.com/orgs/vercel/discussions/4966 Would be grateful if anyone had some insights 🙏
1 replies
TTCTheo's Typesafe Cult
Created by epsilon42 on 7/8/2023 in #questions
Disable batching multiple useQuery
2 replies
TTCTheo's Typesafe Cult
Created by epsilon42 on 6/9/2023 in #questions
Add token requirement to public TRPC endpoint to add to DB (create-t3-app)
I've created a public TRPC endpoint on the exampleRouter like so:

export const exampleRouter = createTRPCRouter({
// ... other routes
create: publicProcedure
.mutation(async ({ ctx, input }) => {
// Fetch/scrape data from somewhere
// ...

// Add to DB
const newItem = ctx.prisma.example.create({
data: {
someData: "fetched data goes here",
},
});

return newItem;
}),
});

export const exampleRouter = createTRPCRouter({
// ... other routes
create: publicProcedure
.mutation(async ({ ctx, input }) => {
// Fetch/scrape data from somewhere
// ...

// Add to DB
const newItem = ctx.prisma.example.create({
data: {
someData: "fetched data goes here",
},
});

return newItem;
}),
});
The above works if I POST to /api/trpc/example.create. I want to be able to call this from Github Actions (this part seems pretty trivial), but I don't want just anyone to be able to call the route in order to trigger it, so I thought I should add a token:

export const exampleRouter = createTRPCRouter({
// ... other routes
create: publicProcedure
.input(
z.object({
token: z.string(), // Token for authentication
})
)
.mutation(async ({ ctx, input }) => {
if (input.token !== "SECRETSAUCE") { // Use .env variable
throw new Error("Invalid token");
}
// Fetch/scrape data from somewhere
// ...

// Add to DB
const newItem = ctx.prisma.example.create({
data: {
someData: "fetched data goes here",
},
});

return newItem;
}),
});

export const exampleRouter = createTRPCRouter({
// ... other routes
create: publicProcedure
.input(
z.object({
token: z.string(), // Token for authentication
})
)
.mutation(async ({ ctx, input }) => {
if (input.token !== "SECRETSAUCE") { // Use .env variable
throw new Error("Invalid token");
}
// Fetch/scrape data from somewhere
// ...

// Add to DB
const newItem = ctx.prisma.example.create({
data: {
someData: "fetched data goes here",
},
});

return newItem;
}),
});
But I get the following error when trying to POST to the endpoint with the following body:
{
"token": "SECRETSAUCE"
}
{
"token": "SECRETSAUCE"
}
tRPC failed on example.create: [
{
"code": "invalid_type",
"expected": "object",
"received": "undefined",
"path": [],
"message": "Required"
}
]
tRPC failed on example.create: [
{
"code": "invalid_type",
"expected": "object",
"received": "undefined",
"path": [],
"message": "Required"
}
]
(Post continued in comment below)
5 replies
TTCTheo's Typesafe Cult
Created by epsilon42 on 5/29/2023 in #questions
runtimeEnv property in env.mjs in create-t3-app
Hey all, Was playing around entering some vars in the .env file and noticed that my app was building fine and using these new variables even though I hadn't added these vars to the env.mjs file. Upon further reading it looks like the server: property in here is more of a safeguard to make sure you don't accidentally miss out env vars when building which is pretty neat 👍 I read the code comment for the runtimeEnv: property but couldn't quite make sense of it:
/**
* You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g.
* middlewares) or client-side so we need to destruct manually.
*/
/**
* You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g.
* middlewares) or client-side so we need to destruct manually.
*/
I didn't add my env variable to runtimeEnv either and things still seemed to work so just trying to figure out the importance of this and when it matters. Thanks!
6 replies