> 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

# Express

> Query UQL from your own Express routes, and optionally auto-generate REST endpoints with the querier middleware.

Source: https://uql-orm.dev/express

`pool` is a plain ORM, so an Express app needs nothing else from UQL: your own routes query it directly. The [middleware](#auto-generated-entity-routes) below that is optional, a thin adapter over the [HTTP transport core](https://uql-orm.dev/http.md) for when you want CRUD across many entities without writing it.

## Your own routes

```ts
import express from 'express';
import { pool } from './uql.config.js';
import { Post } from './shared/models/index.js';

const app = express();

app.get('/posts', async (_req, res) => {
  const posts = await pool.findMany(Post, {
    $where: { published: true },
    $limit: 20,
  });
  res.json(posts);
});

app.listen(3000);
```

Work spanning several statements goes in `pool.transaction`, which hands you the [querier](https://uql-orm.dev/querying/querier.md) to run all of them on.

## Auto-generated entity routes

Requires Express 5, whose route syntax the middleware is written against. NestJS has been on Express 5 since v11, so a current [Nest app](https://uql-orm.dev/nestjs.md) already qualifies.

```ts
import { querierMiddleware } from 'uql-orm/express';
import { User } from './shared/models/index.js';

app.use(express.json()); // the write routes and the QUERY transport need a parsed body
app.use('/api', querierMiddleware({ pool, include: [User, Post] }));
```

That mounts the full [wire protocol](https://uql-orm.dev/http.md#wire-protocol) per entity, including the [`QUERY` transport](https://uql-orm.dev/http.md#http-query-rfc-10008). Unknown entities and routes fall through via `next()`, so your own routes (webhooks, payments, SSE) share the prefix.

`:id` is not hardcoded: the route parameter maps to whatever property carries `@Id()`, so `uuid` or `itemNo` work unchanged, the latter [naming its key](https://uql-orm.dev/entities/basic.md#naming-the-key), as any unconventional one does.

## Hooks

The [core’s hooks](https://uql-orm.dev/http.md#authorization-hooks) apply, with `ctx.context` bound to the `express.Request`:

```ts
app.use(
  '/api',
  querierMiddleware({
    pool,
    include: [User, Post],
    async pre({ context }) {
      if (!context.user) {
        throw Object.assign(new Error('unauthorized'), { status: 401 }); // numeric status becomes the HTTP status
      }
    },
    preSave(ctx) {
      ctx.body = { ...(ctx.body as object), creatorId: ctx.context.user?.id };
    },
  }),
);
```

For tenant isolation pass `getContext` plus a `security` [filter](https://uql-orm.dev/querying/filters.md) rather than folding `$where` in `preFilter`: it scopes every query in the request, cannot be bypassed from the wire, and fails closed. See [Multi-tenancy](https://uql-orm.dev/multi-tenancy.md).

## Error handling

Errors go to `next(err)`, so your own error middleware keeps working. The exported `errorHandler` renders the canonical [envelope](https://uql-orm.dev/http.md#wire-protocol) and honors a numeric `status` thrown by a hook:

```ts
import { errorHandler } from 'uql-orm/express';

app.use('/api', querierMiddleware({ pool, include: [User] }));
app.use(errorHandler);
```
