Skip to content

tRPC

UQL queries are plain JSON, so they pass through tRPC procedures without any adapter. Nothing to install beyond your existing tRPC v11 setup: procedures call the querier pool directly.

import { initTRPC } from '@trpc/server';
import { z } from 'zod';
import type { Query, Type } from 'uql-orm/type';
import { pool } from './uql.config.js';
import { User } from './shared/models/index.js';
const t = initTRPC.create();
function entityRouter<E extends object>(entity: Type<E>) {
return t.router({
findMany: t.procedure
.input(z.custom<Query<E>>()) // declares the input type; no cast, no per-procedure schema
.query(({ input }) => pool.findMany(entity, input)),
insertOne: t.procedure
.input(z.custom<E>())
.mutation(({ input }) => pool.insertOne(entity, input)),
});
}
export const appRouter = t.router({
user: entityRouter(User),
});

On the client the whole query is typed end to end, filters, sorting and nested relation loading alike, and reaches the server as plain JSON with no per-procedure schema to keep in sync:

const users = await trpc.user.findMany.query({
$select: { id: true, name: true },
$where: { status: 'active', email: { $endsWith: '@domain.com' } },
$populate: { posts: { $select: { title: true }, $where: { published: true }, $limit: 5 } },
$limit: 10,
});
// typed User[], each with a typed posts: Post[]