Fastify
Your Fastify routes query the pool directly. If you also want CRUD routes for many entities without writing them, one catch-all route forwards to the HTTP handler.
Your own routes
Section titled “Your own routes”import Fastify from 'fastify';import { Post } from './shared/models/index.js';import { pool } from './uql.config.js';
const fastify = Fastify();
fastify.get('/posts', () => pool.findMany(Post, { $where: { published: true }, $limit: 20 }),);
await fastify.listen({ port: 3000 });The pool shows the uql.config.ts it comes from. Run several writes together in pool.transaction.
Auto-generated CRUD
Section titled “Auto-generated CRUD”Fastify’s request is not a web Request, so use createRequestHandler. It takes a plain request object and returns { status, body }, or undefined for a route it does not serve:
import { createRequestHandler, toErrorResponse } from 'uql-orm/http';import { Post, User } from './shared/models/index.js';
const handle = createRequestHandler({ pool, include: [User, Post] });
fastify.all<{ Params: { entityPath: string; subPath?: string }; Querystring: Record<string, unknown>;}>('/api/:entityPath/:subPath?', async (req, reply) => { const { entityPath, subPath } = req.params; const pending = handle({ method: req.method, entityPath, subPath, query: req.query, body: req.body, context: req, // what the hooks receive }); if (!pending) return reply.callNotFound(); try { const { status, body } = await pending; return reply.status(status).send(body); } catch (err) { const { status, body } = toErrorResponse(err); return reply.status(status).send(body); }});This serves the wire protocol for each entity, and toErrorResponse builds the error envelope. Fastify parses JSON bodies itself and matches your own routes before this catch-all.
From Fastify 5.11, fastify.all routes QUERY too and parses its body, so the browser client can send it. On older versions, keep the client on GET.
Hooks and tenant scoping
Section titled “Hooks and tenant scoping”createRequestHandler takes the handler’s hooks and getContext, whose context is whatever you passed in. Name its type as the type parameter, for example createRequestHandler<FastifyRequest & { user?: User }>(...), to read the user your auth plugin sets.