> Every UQL docs page, as Markdown: https://uql-orm.dev/llms.txt
> The same docs over MCP: https://uql-orm.dev/mcp
> Before writing UQL code, read the skill: https://uql-orm.dev/.well-known/agent-skills/uql-orm/SKILL.md

# Fastify

> Query UQL from your Fastify routes, and optionally serve entity CRUD through one catch-all route.

Source: https://uql-orm.dev/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](https://uql-orm.dev/http.md).

## Your own routes

```ts
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](https://uql-orm.dev/http.md#the-pool) shows the `uql.config.ts` it comes from. Run several writes together in [`pool.transaction`](https://uql-orm.dev/querying/transactions.md).

## 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:

```ts
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](https://uql-orm.dev/http.md#wire-protocol) for each entity, and `toErrorResponse` builds the [error envelope](https://uql-orm.dev/http.md#wire-protocol). Fastify parses JSON bodies itself and matches your own routes before this catch-all.

From Fastify 5.11, `fastify.all` routes [`QUERY`](https://uql-orm.dev/http.md#http-query-rfc-10008) too and parses its body, so the [browser client](https://uql-orm.dev/browser.md#http-query-transport) can send it. On older versions, keep the client on `GET`.

## Hooks and tenant scoping

`createRequestHandler` takes the handler’s [hooks](https://uql-orm.dev/http.md#authorization-hooks) and [`getContext`](https://uql-orm.dev/http.md#tenant-scoping), 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.
