Finn
Finn9mo ago

How to properly handle Prima selects/includes with tRPC?

Hello, my understanding of optimizing procedures to be the most efficient would be to enable one procedure to handle different use-cases. So I'd like a procedure like getById to take a list that allows all possible attributes from the prisma models and optionally a list of models to include. Currently, I define a list of all attributes on a global level for each router, that includes all includable Fields (I'd then do the same for the selectable fields). But that - even with just the includable fields - ends in this madness:
const includableFields = z.enum([
"comments",
"readingProgress",
"novelInsights",
"author",
"userLists",
// ...
]);

export const novelRouter = createTRPCRouter({
getById: privateProcedure
.input(
z.object({
id: z.string(),
include: z.array(includableFields).optional(),
})
)
.query(async ({ ctx, input }) => {
return await ctx.db.novel.findUnique({
where: {
id: input.id,
},
include: {
comments: input.include?.includes("comments") ?? false,
readingProgress:
input.include?.includes("readingProgress") ?? false,
novelInsights:
input.include?.includes("novelInsights") ?? false,
author: input.include?.includes("author") ?? false,
userLists: input.include?.includes("userLists") ?? false,
chapters: input.include?.includes("chapters") ?? false,
genre: input.include?.includes("genre") ?? false,
customInsights:
input.include?.includes("customInsights") ?? false,
},
});
}),
});
const includableFields = z.enum([
"comments",
"readingProgress",
"novelInsights",
"author",
"userLists",
// ...
]);

export const novelRouter = createTRPCRouter({
getById: privateProcedure
.input(
z.object({
id: z.string(),
include: z.array(includableFields).optional(),
})
)
.query(async ({ ctx, input }) => {
return await ctx.db.novel.findUnique({
where: {
id: input.id,
},
include: {
comments: input.include?.includes("comments") ?? false,
readingProgress:
input.include?.includes("readingProgress") ?? false,
novelInsights:
input.include?.includes("novelInsights") ?? false,
author: input.include?.includes("author") ?? false,
userLists: input.include?.includes("userLists") ?? false,
chapters: input.include?.includes("chapters") ?? false,
genre: input.include?.includes("genre") ?? false,
customInsights:
input.include?.includes("customInsights") ?? false,
},
});
}),
});
The problem I have with this, is that it looks horrendous and needs to be manually adjusted everytime the schema changes
Solution:
```ts const includableFields = z.enum([ "comments", "readingProgress", "novelInsights",...
Jump to solution
5 Replies
Finn
FinnOP9mo ago
So, my question basically is, how you usually do this. Do you just allow all strings to be passed and try-catch possible errors being thrown, or am I severly misunderstanding how to use tRPC.
NeoBean
NeoBean9mo ago
why not make items of includableFields booleans ?
julius
julius9mo ago
yea i'd keep the include an object and just passthrough
Solution
julius
julius9mo ago
const includableFields = z.enum([
"comments",
"readingProgress",
"novelInsights",
"author",
"userLists",
// ...
]);

export const novelRouter = createTRPCRouter({
getById: privateProcedure
.input(
z.object({
id: z.string(),
include: z.record(includableFields, z.boolean()).default({}),
})
)
.query(async ({ ctx, input }) => {
return await ctx.db.novel.findUnique({
where: {
id: input.id,
},
include: input.include,
});
}),
});
const includableFields = z.enum([
"comments",
"readingProgress",
"novelInsights",
"author",
"userLists",
// ...
]);

export const novelRouter = createTRPCRouter({
getById: privateProcedure
.input(
z.object({
id: z.string(),
include: z.record(includableFields, z.boolean()).default({}),
})
)
.query(async ({ ctx, input }) => {
return await ctx.db.novel.findUnique({
where: {
id: input.id,
},
include: input.include,
});
}),
});
Finn
FinnOP8mo ago
Sorry it took me so long to respond. I've read your answer after you've posted it, but I completely forget about it because I was immersed into something else. Just wanted to say thanks.