Skip to content
NewFrom Mongoose to SQL6 min read

TanStack Start

Server functions run on the server, so they query the pool directly. For CRUD routes across many entities, mount the HTTP handler in a catch-all server route as well.

A UQL query is plain JSON, so it can be a server function’s input, typed end to end:

import { createServerFn } from '@tanstack/react-start';
import type { WireQuery } from 'uql-orm';
import { User } from './shared/models/index.js';
import { pool } from './uql.config.js';
export const listUsers = createServerFn({ method: 'GET' })
.validator((query: WireQuery<User>) => query) // declares the type, checks nothing
.handler(({ data }) => pool.findMany(User, data));
const users = await listUsers({
data: {
$select: { id: true, name: true },
$where: { status: 'active' },
$limit: 10,
},
});

That validator trusts the caller. For untrusted callers, validate a few arguments with a schema and build the query on the server. For tenant isolation, run the handler inside withContext and declare a security filter (Multi-tenancy).

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

ANY answers every method no other handler names, QUERY included.