MiNiMAL
MiNiMAL
Explore posts from servers
TtRPC
Created by MiNiMAL on 11/2/2023 in #❓-help
Function to release context post-batching?
In our application every customer has their own schema, therefore we need a db client per request which I was hoping to leverage with TRPC context. Ideally, we'd be able to: - Connect to the database in context ✅ - Set the search_path to match the client's JWT in context ✅ - Run our procedures using that database connection from context ✅ - Release the database connection after all batching is finished ❌ Is this not currently possible with TRPC? Right now I think I can only connect to the database using a per procedure middleware.
const db = // connect to db

await db.execute('SET search_path=...')

try {
return await next({ ctx: { ...opts.ctx, db } })
} catch () {
// release connection
} finally {
// release connection
}
const db = // connect to db

await db.execute('SET search_path=...')

try {
return await next({ ctx: { ...opts.ctx, db } })
} catch () {
// release connection
} finally {
// release connection
}
This works, but it requires establishing a connection/search_path for every procedure in a batch, instead of just once in context and releasing after the batch. Is there a way to accomplish this?
13 replies
TtRPC
Created by MiNiMAL on 10/15/2023 in #❓-help
Dynamic input not leading to changes on the front end
I'm building a dynamic procedure to access my database via Drizzle ORM. Ideally a developer would be able to send input parameters to the procedure and have it's output adjusted by that input. For example, in the below, I have a table called demotable which has an updatedAt column. If a developer passes in columns: { updatedAt: false } to the procedure, then ideally the column would not get included in the result. If I hard code the columns parameter in the backend itself, then it works great. Is there a way to accomplish this for the full round trip?
// Inferring type for the findMany argument
type FindManyArg = Parameters<DB['query']['demotable']['findMany']>[0]

// ...

//Setting up the procedure
discordTest: privateProcedure
.input(input => input as FindManyArg)
.query(async ({ input }) => {
const result = await db.query.demotable.findMany(input)
return result
}),

// On the front end,
const { data: demo } = trpc.discordTest.useQuery({
columns: { updatedAt: false }, // <== this is properly typed and intellisense is working
})
console.log(demo?.[0].updatedAt) // <=== does not error

// ============================

// Hardcoding columns does properly reflect on the front end
const result = await db.query.demotable.findMany({
...input,
columns: { updatedAt: false },
})
return result

// On the front end,
const { data: demo } = trpc.discordTest.useQuery({})
console.log(demo?.[0].updatedAt) // <=== errors as expected
// Inferring type for the findMany argument
type FindManyArg = Parameters<DB['query']['demotable']['findMany']>[0]

// ...

//Setting up the procedure
discordTest: privateProcedure
.input(input => input as FindManyArg)
.query(async ({ input }) => {
const result = await db.query.demotable.findMany(input)
return result
}),

// On the front end,
const { data: demo } = trpc.discordTest.useQuery({
columns: { updatedAt: false }, // <== this is properly typed and intellisense is working
})
console.log(demo?.[0].updatedAt) // <=== does not error

// ============================

// Hardcoding columns does properly reflect on the front end
const result = await db.query.demotable.findMany({
...input,
columns: { updatedAt: false },
})
return result

// On the front end,
const { data: demo } = trpc.discordTest.useQuery({})
console.log(demo?.[0].updatedAt) // <=== errors as expected
4 replies
DTDrizzle Team
Created by MiNiMAL on 10/14/2023 in #help
Front to back end type inference with parameters sent via request
Hey there, I've been exploring using drizzle in a TRPC environment. This is probably a pipe dream, but ideally, our developers would be able to dynamically pass a findMany input and have the query's results inferred all the way to the front end.
// Front end
const { data } = trpc.tablename.get.useQuery({
where: { } // <== where clause options are dynamically inferred
with: { nestedEntities: true } // <== with clause options are dynamically inferred
})

console.log(data?.[0]?.nestedEntities)
// ^ This is valid when the with clause is provided to the hook, but will not if it's not there
// Front end
const { data } = trpc.tablename.get.useQuery({
where: { } // <== where clause options are dynamically inferred
with: { nestedEntities: true } // <== with clause options are dynamically inferred
})

console.log(data?.[0]?.nestedEntities)
// ^ This is valid when the with clause is provided to the hook, but will not if it's not there
The first issue I encountered is that there needs to be a translation layer for the where statements, as we cannot send drizzle operator functions over a request. To handle this, I built out a recursive layer for where statements at the root and also in any nested with and it works functionally very well (though is only strongly typed at the root layer). Example:
where: {
columnA: { equal_to: 123 },
or: [
columnB: { isNull: false },
columnA: { likeInsensitive: '%TRPC%' }
]
}
where: {
columnA: { equal_to: 123 },
or: [
columnB: { isNull: false },
columnA: { likeInsensitive: '%TRPC%' }
]
}
The above's operators and column names all have type inference/intellisense based on the DB model. The above turns into this on the backend:
where: (table, { and, eq, or, isNull, ilike }) =>
and(...[
eq(table['columnA'], 123),
or(...[
isNull(table['columnB']),
ilike(table['columnA'], '%TRPC%')
])
)
where: (table, { and, eq, or, isNull, ilike }) =>
and(...[
eq(table['columnA'], 123),
or(...[
isNull(table['columnB']),
ilike(table['columnA'], '%TRPC%')
])
)
Is this a fine approach? Unfortunately, while I have it functionally working with nested where statements inside of with clauses, when I try to get the types to be inferred all the way down I end up with:
error TS7056: The inferred type of this node exceeds the maximum length the compiler will serialize. An explicit type annotation is needed.
My question is - has anyone tried full front to back end type inference? Or even just how to best handle findMany params via the request?
16 replies
TtRPC
Created by MiNiMAL on 10/11/2022 in #❓-help
Best practices for implementing an offline application
Hey there! I'm building a full stack react-native app with Expo and a tRPC backend. I'd like for this app to be functional offline. Currently I'm accomplishing this via a simple optimistic update like:
const createUser = trpc.userCreate.useMutation({
onMutate: async () => {
// Cancel users fetch
await utils.users.cancel()
// Get current users list
const previousData = utils.users.getData() || []
// Append new user
utils.users.setData([...previousData, { id: uuid.v1(), name } as User])
return { previousData }
},
})
const createUser = trpc.userCreate.useMutation({
onMutate: async () => {
// Cancel users fetch
await utils.users.cancel()
// Get current users list
const previousData = utils.users.getData() || []
// Append new user
utils.users.setData([...previousData, { id: uuid.v1(), name } as User])
return { previousData }
},
})
This is working pretty well (it's late and I plan on extensively testing this tomorrow). I'd like to confirm what best practices, if any, have been established by the community before continuing.
2 replies