Skip to content

TanStack Start

TanStack Start is full-stack and fetch-native, so UQL plugs in two ways: call the pool from type-safe server functions, or mount the HTTP transport core as a catch-all server route for zero-boilerplate CRUD. Nothing to install beyond uql-orm.

A UQL query is plain JSON, so it passes through a server function as the validated input with full end-to-end types, no adapter:

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));
export const createUser = createServerFn({ method: 'POST' })
.validator((user: User) => user)
.handler(({ data }) => pool.transaction((querier) => querier.insertOne(User, data)));

From a component or route loader, the whole query, including nested relation loading, stays typed end to end:

const users = await listUsers({
data: {
$select: { id: true, name: true },
$where: { status: 'active' },
$populate: { posts: { $select: { title: true }, $where: { published: true }, $limit: 5 } },
$sort: { createdAt: 'desc' },
$limit: 10,
},
});
// `users` is typed User[], each with a typed `posts: Post[]`

Auto-generated CRUD (catch-all server route)

Section titled “Auto-generated CRUD (catch-all server route)”

To expose every entity as REST without a function per operation, mount createFetchHandler in a splat server route, src/routes/api/uql/$.ts:

import { createFileRoute } from '@tanstack/react-router';
import { createFetchHandler } from 'uql-orm/http';
import './uql.config.js'; // registers the default querier pool
import { User, Post } 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),
},
},
});

TanStack Start does not strip the route prefix, so pass basePath: '/api/uql' to match the splat mount. Every included entity now has typed REST endpoints (/api/uql/user, …) consumable from the browser with HttpQuerier.

createFetchHandler accepts the core’s pre, preSave, preFilter, and post hooks; the hook context is the web Request. For real tenant isolation prefer getContext plus a security filter, so every query in the request is scoped automatically, can’t be bypassed from the wire, and fails closed if the context is missing. See Multi-tenancy.

Morty Proxy This is a proxified and sanitized view of the page, visit original site.