Alaskan donut
Alaskan donut
Explore posts from servers
TTCTheo's Typesafe Cult
Created by Alaskan donut on 11/8/2024 in #questions
Are media-processing capabilities on the uploadthing roadmap?
I'd love to hear from someone on the team regarding whether or not uploadthing will be getting media processing capabilities in the future. I believe I remember Theo mentioning something related to this when he was excitedly purporting the benefits of using a dedicated ingest server. I suspect that others, like myself, are very willing to pay for these capabilities. In my mind, uploadthing should serve as the defacto upload/media processing option and this would likely lead to a better longevity for the product.
2 replies
PPrisma
Created by Alaskan donut on 11/3/2024 in #help-and-questions
TypedSQL: Passing null values into raw SQL?
I am currently using an aggregate query that returns a boolean that is either true or false if some user (current user) has reacted to a given post. Here is the query (thanks Jon Harrell--if the code has issues it's likely because I've been modifying it):
-- @param {String} $1:Post ID
-- @param {String} $2:User ID
SELECT
p."id" AS "postId",
reaction_types."id" AS "reactionTypeId",
CAST(COUNT(reactions."id") AS INTEGER) AS "reactionCount",
reaction_types."emoji" AS "emoji",
reaction_types."name" AS "emojiName",
CASE
WHEN COUNT(CASE WHEN reactions."user_id" = $2 THEN 1 END) > 0 THEN TRUE
ELSE FALSE
END AS "reacted"
FROM posts p
LEFT JOIN reactions ON p."id" = reactions."post_id"
LEFT JOIN reaction_types ON reaction_types."id" = reactions."reaction_type_id"
WHERE p."id" = $1::uuid
GROUP BY p."id", p."user_id", reaction_types."id", reaction_types."name", reaction_types."emoji"
ORDER BY p."id", reaction_types."id";
-- @param {String} $1:Post ID
-- @param {String} $2:User ID
SELECT
p."id" AS "postId",
reaction_types."id" AS "reactionTypeId",
CAST(COUNT(reactions."id") AS INTEGER) AS "reactionCount",
reaction_types."emoji" AS "emoji",
reaction_types."name" AS "emojiName",
CASE
WHEN COUNT(CASE WHEN reactions."user_id" = $2 THEN 1 END) > 0 THEN TRUE
ELSE FALSE
END AS "reacted"
FROM posts p
LEFT JOIN reactions ON p."id" = reactions."post_id"
LEFT JOIN reaction_types ON reaction_types."id" = reactions."reaction_type_id"
WHERE p."id" = $1::uuid
GROUP BY p."id", p."user_id", reaction_types."id", reaction_types."name", reaction_types."emoji"
ORDER BY p."id", reaction_types."id";
And I want to be able to pass a null userId value if there is no current user into
prisma.$queryRawTyped(getPostReactionsSql(postId, userId))
prisma.$queryRawTyped(getPostReactionsSql(postId, userId))
but unfortunately this does not work with the type error Argument of type 'string | null | undefined' is not assignable to parameter of type 'string'. Type 'undefined' is not assignable to type 'string'.ts(2345) Up until now, I've been passing two functions conditionally when userId is not known:
const postReactions = (await prisma.$queryRawTyped(
userId
? getPostReactionsSql(postId, userId)
: getPostReactionsNoUserSql(postId),
)) as getPostReactionsSql.Result[] | getPostReactionsNoUserSql.Result[];
const postReactions = (await prisma.$queryRawTyped(
userId
? getPostReactionsSql(postId, userId)
: getPostReactionsNoUserSql(postId),
)) as getPostReactionsSql.Result[] | getPostReactionsNoUserSql.Result[];
But this doesn't play well with the type system, hence the explicit type casting since the only accepted types don't include null or undefined. Is there a way to type this userId value as nullable/nullish, and execute this query without it? Any help is greatly appreciated!
3 replies
TtRPC
Created by Alaskan donut on 11/1/2024 in #❓-help
Fetch once and never again?
Hey there 👋, I'm wanting to fetch some data reactionTypes once when my layout <MainLayout /> renders in Nextjs and never again because this data does not change. Currently, I have placed (what I think is) a headless component into my layout that accesses the query client then sets a staleTime: Infinity default on this specific query. Is this a dumb strategy? How might you do this differently?
"use client";

import { trpc } from "@/utils/trpc";
import { useQueryClient } from "@tanstack/react-query";
import { getQueryKey } from "@trpc/react-query";

export function TrpcMainLayoutDefaults() {
const queryClient = useQueryClient();

// Reaction Types
const reactionTypesKey = getQueryKey(trpc.reaction.getTypes);
queryClient.setQueryDefaults(reactionTypesKey, {
staleTime: Infinity,
});
// I had originally prefetched, but it seems this just adds an unneeded request (understandably)
// trpc.useUtils().reaction.getTypes.prefetch()

// Other...

return <></>;
}
"use client";

import { trpc } from "@/utils/trpc";
import { useQueryClient } from "@tanstack/react-query";
import { getQueryKey } from "@trpc/react-query";

export function TrpcMainLayoutDefaults() {
const queryClient = useQueryClient();

// Reaction Types
const reactionTypesKey = getQueryKey(trpc.reaction.getTypes);
queryClient.setQueryDefaults(reactionTypesKey, {
staleTime: Infinity,
});
// I had originally prefetched, but it seems this just adds an unneeded request (understandably)
// trpc.useUtils().reaction.getTypes.prefetch()

// Other...

return <></>;
}
2 replies
TtRPC
Created by Alaskan donut on 10/21/2024 in #❓-help
Opinionated help: Where to place data access functions with respect to tRPC router(s)?
Hey there 👋 I'm currently trying to decide where to place my data access functions with respect to my tRPC routers. Currently, I have them placed at src/data-access/post/mutate-thing.ts, and I am wanting to move them into a folder structure like this: src/server/routers/post/functions/mutate-thing.ts but this feels a bit too nested (maybe not a bad thing) and for someone new to my codebase, this might make it difficult to find these functions. My routers are all obviously defined at src/server/routers, and I import my data access functions directly into each procedure within each router. Should I leave these data functions where they are at?
2 replies
TtRPC
Created by Alaskan donut on 10/14/2024 in #❓-help
Dynamically generate url for httpBatchLink
Hey there 👋 is it possible to generate a url for httpBatchLink instead of hardcoding one? I attempted the below code, but this doesn't work. Understandably, the fallback value is used for url each time the component mounts. I believe that I wouldn't even need a useEffect if I wasn't using Next, as I could directly grab these values from window.location, but since I'm using SSR, this code doesn't make it through the SSR pass.
"use client";

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { httpBatchLink } from "@trpc/client";
import { useEffect, useState } from "react";
import { trpc } from "@/utils/trpc";
import SuperJSON from "superjson";

export function TRPCQueryClientProvider({
children,
}: {
children: React.ReactNode;
}) {
const [url, setUrl] = useState<string | null>(null);

useEffect(() => {
if (typeof window !== "undefined") {
const { hostname, protocol, port } = window.location;
setUrl(`${protocol}//${hostname}:${port}/api/trpc`);
}
}, []);

const [queryClient] = useState(() => new QueryClient());
const [trpcClient] = useState(() =>
trpc.createClient({
links: [
httpBatchLink({
url: url ?? "http://localhost:3000/api/trpc", // Fallback in case url is not ready,
// url: "http://192.168.0.16:3000/api/trpc", // For mobile device testing
transformer: SuperJSON,
}),
],
}),
);
return (
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
</trpc.Provider>
);
}
"use client";

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { httpBatchLink } from "@trpc/client";
import { useEffect, useState } from "react";
import { trpc } from "@/utils/trpc";
import SuperJSON from "superjson";

export function TRPCQueryClientProvider({
children,
}: {
children: React.ReactNode;
}) {
const [url, setUrl] = useState<string | null>(null);

useEffect(() => {
if (typeof window !== "undefined") {
const { hostname, protocol, port } = window.location;
setUrl(`${protocol}//${hostname}:${port}/api/trpc`);
}
}, []);

const [queryClient] = useState(() => new QueryClient());
const [trpcClient] = useState(() =>
trpc.createClient({
links: [
httpBatchLink({
url: url ?? "http://localhost:3000/api/trpc", // Fallback in case url is not ready,
// url: "http://192.168.0.16:3000/api/trpc", // For mobile device testing
transformer: SuperJSON,
}),
],
}),
);
return (
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
</trpc.Provider>
);
}
3 replies
TtRPC
Created by Alaskan donut on 9/16/2024 in #❓-help
How to get pageParam from useInfiniteQuery()?
How would I write a tRPC call equivalent to this traditional useInfiniteQuery? My primary concern here is that I need to pass in a cursor to my procedure, and upon each successive query I need to update this cursor for the next call.
const {
data: posts,
isFetching,
error,
hasNextPage,
fetchNextPage,
} = useInfiniteQuery({
queryKey: ["profile-posts", username],
queryFn: ({ pageParam }) =>
kyInstance
.get(
`/api/profiles/${username}/posts`,
pageParam ? { searchParams: { cursor: pageParam } } : {},
)
.json<TProfilePostsPage>(),
initialPageParam: null as string | null,
getNextPageParam: (lastPage) => lastPage.nextCursor,
});
const {
data: posts,
isFetching,
error,
hasNextPage,
fetchNextPage,
} = useInfiniteQuery({
queryKey: ["profile-posts", username],
queryFn: ({ pageParam }) =>
kyInstance
.get(
`/api/profiles/${username}/posts`,
pageParam ? { searchParams: { cursor: pageParam } } : {},
)
.json<TProfilePostsPage>(),
initialPageParam: null as string | null,
getNextPageParam: (lastPage) => lastPage.nextCursor,
});
Maybe it should be something like this?
const [nextCursor, setNextCursor] = useState<string | null>(null)

const {
data: posts,
isFetching,
error,
hasNextPage,
fetchNextPage,
} = trpc.getProfilePosts.useInfiniteQuery({ username, cursor: nextCursor }, {
queryKey: ["profile-posts", username],
initialPageParam: null as string | null,
getNextPageParam: (lastPage) => {
setNextCursor(lastPage.nextCursor)
return lastPage.nextCursor
},
});
const [nextCursor, setNextCursor] = useState<string | null>(null)

const {
data: posts,
isFetching,
error,
hasNextPage,
fetchNextPage,
} = trpc.getProfilePosts.useInfiniteQuery({ username, cursor: nextCursor }, {
queryKey: ["profile-posts", username],
initialPageParam: null as string | null,
getNextPageParam: (lastPage) => {
setNextCursor(lastPage.nextCursor)
return lastPage.nextCursor
},
});
2 replies
TTCTheo's Typesafe Cult
Created by Alaskan donut on 9/14/2024 in #questions
uploadthing replacing name and adding customId in middleware fails to run
This middleware does not run, despite this being close to what is provided in the documentation for changing file names and adding customIds (https://docs.uploadthing.com/file-routes#middleware). Logging the error provided by useUploadThing()'s onUploadError gives UploadThingError: Failed to run middleware... Caused by: Object { … }
.middleware(async ({ req, files }) => {
// This code runs on your server before upload
const { user } = await validateRequest();

// If you throw, the user will not be able to upload
if (!user) throw new UploadThingError("Unauthorized");

// Override file name and add customId
const fileOverrides = files.map((file) => {
const newName = uniqueNamesGenerator({
dictionaries: [adjectives, animals, colors],
length: 4,
});
const uuid = crypto.randomUUID();
return { ...file, name: newName, customId: uuid };
});

// Whatever is returned here is accessible in onUploadComplete as `metadata`
// Return userId to be used in onUploadComplete
return { userId: user.id, [UTFiles]: fileOverrides };
// return { userId: user.id };
})
.middleware(async ({ req, files }) => {
// This code runs on your server before upload
const { user } = await validateRequest();

// If you throw, the user will not be able to upload
if (!user) throw new UploadThingError("Unauthorized");

// Override file name and add customId
const fileOverrides = files.map((file) => {
const newName = uniqueNamesGenerator({
dictionaries: [adjectives, animals, colors],
length: 4,
});
const uuid = crypto.randomUUID();
return { ...file, name: newName, customId: uuid };
});

// Whatever is returned here is accessible in onUploadComplete as `metadata`
// Return userId to be used in onUploadComplete
return { userId: user.id, [UTFiles]: fileOverrides };
// return { userId: user.id };
})
1 replies
PPrisma
Created by Alaskan donut on 8/28/2024 in #help-and-questions
Complicated groupBy query
Hey there, I am attempting to do something with Prisma that may be undoable. I am still very new to web dev and Prisma, so be aware I don't doubt I'm missing something obvious here. I would like to use post.findMany() to find posts for a specific user, but in this query I would like to include the number of reactions associated with a post for a kind of reactionType. In my project, I am using emoji reactions on posts, and I need to return the number of reactions per emoji type (reactionType). However, I am at a loss for how to do this without many queries per request. Using groupBy() works if I grab the posts first and then use the post.id of each post...
prisma.reaction.groupBy({
by: "reactionTypeId",
where: { postId: { in: postList } },
_count: true,
})
prisma.reaction.groupBy({
by: "reactionTypeId",
where: { postId: { in: postList } },
_count: true,
})
^ This returns the following:
[
{ _count: 1, reactionTypeId: 'U+1FAE0' },
{ _count: 2, reactionTypeId: 'U+2764' }
]
[
{ _count: 1, reactionTypeId: 'U+1FAE0' },
{ _count: 2, reactionTypeId: 'U+2764' }
]
...and then merging the results from this with the original posts list, but this method is fairly complicated and I still haven't solved how to include a boolean value which indicates whether or not the current user has reacted to this post with a given emoji/reactionType... this would require additional queries. I think the ideal structure would be an object called reactionTypes of the following type appended to the post object:
type ReactionType = {
[key: string]: {
id: string,
reacted: boolean
}
}
type ReactionType = {
[key: string]: {
id: string,
reacted: boolean
}
}
... where the key here is the id of the reactionType like "U+2764" which is the unicode for a red heart emoji. Of course, I don't exactly need this format, I just need this data 🙂 (i.e., reacted boolean, count number) If anyone has thought about how I might be best include this reaction data, I would greatly appreciate it. I've been struggling for hours on this. For schema-related details, see the below schema.prisma (comment)
7 replies
PPrisma
Created by Alaskan donut on 6/6/2024 in #help-and-questions
Flat response from relational query?
I am trying to use the following Prisma query to get a post object back containing post details and the username of the user that posted it: const posts = await prisma.post.findMany({ where: { users: { username: { equals: username, mode: "insensitive", }, }, }, select: { contentText: true, users: { select: { username: true, }, }, }, }); This returns [ { contentText: 'Embarking on a thrilling journey in Glacier National Park ⛰️ was an unforgettable experie nce! Amidst stunning landscapes, we had a heart-pounding encounter with a majestic bear, a reminder of the untamed beauty of nature. Days were spent foraging mushrooms 🍄, adding a delicious touch to our lakeside feasts. Nights by the crackling fire on the lakeshore were filled with laughter, storytelling, and a deep sense of camaraderie. Surrounded by towering mountains and the tranquil lake, each moment felt like a cherished memory in the making. With my partner and friends by my side, we immersed ourselves in the serenity of the wilderness, forging bonds that will last a lifetime. 🪄' , users: { username: 'Alaskan' } } ] I would like username to be level with contentText without having to manually flatten the objects returned in this response array. How is this accomplished?
4 replies