Skip to content

TanStack Start

Start is full-stack and fetch-native, so there are two ways in and they coexist: call the pool from type-safe server functions, or mount the HTTP core as a catch-all server route. Nothing to install beyond uql-orm.

A UQL query is plain JSON, so it passes through as the validated input with end-to-end types:

import { createServerFn } from '@tanstack/react-start';
import type { Query } from 'uql-orm/type';
import { pool } from './uql.config.js';
import { User } from './shared/models/index.js';
export const listUsers = createServerFn({ method: 'GET' })
.validator((query: Query<User>) => query) // type-only pass-through
.handler(({ data }) => pool.findMany(User, data));
const users = await listUsers({
data: {
$select: { id: true, name: true },
$where: { status: 'active' },
$populate: { posts: { $select: { title: true }, $where: { published: true }, $limit: 5 } },
$limit: 10,
},
});
// typed User[], each with a typed posts: Post[]

(query) => query declares the type without checking it, the same trust model as tRPC and oRPC. For untrusted callers, validate with a schema and build the query server-side; for tenant isolation run the handler inside withContext with a security filter. See Multi-tenancy.

src/routes/api/uql/$.ts
import { createFileRoute } from '@tanstack/react-router';
import { createFetchHandler } from 'uql-orm/http';
import './uql.config.js';
import { Post, User } from './shared/models/index.js';
const handler = createFetchHandler({ include: [User, Post], basePath: '/api/uql' });
export const Route = createFileRoute('/api/uql/$')({
server: {
handlers: {
GET: ({ request }) => handler(request),
HEAD: ({ request }) => handler(request),
POST: ({ request }) => handler(request),
PUT: ({ request }) => handler(request),
PATCH: ({ request }) => handler(request),
DELETE: ({ request }) => handler(request),
},
},
});

Start does not strip the prefix, hence basePath. Every entity now has typed REST endpoints (/api/uql/user, …) for HttpQuerier. The handlers keys are standard verbs, so the QUERY transport is not routed here; keep GET.

createFetchHandler takes the core’s hooks, with the web Request as the hook context, and getContext for tenant scoping.