Skip to content

Express

A thin, optional adapter over the HTTP transport core, which owns the route table, envelopes and hooks. This page covers only what is Express-specific.

Requires Express 5, whose route syntax the middleware is written against. NestJS has been on Express 5 since v11, so a current Nest app already qualifies.

import express from 'express';
import { querierMiddleware } from 'uql-orm/express';
import './uql.config.js'; // setQuerierPool + entity imports
import { Post, User } from './shared/models/index.js';
const app = express();
app.use(express.json()); // required by the write routes and the QUERY transport
app.use('/api', querierMiddleware({ include: [User, Post] }));
app.listen(3000);

That mounts the full wire protocol per entity, including the QUERY transport. 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 core’s hooks apply, with ctx.context bound to the express.Request:

app.use('/api', querierMiddleware({
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 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.

Errors go to next(err), so your own error middleware keeps working. The exported errorHandler renders the canonical envelope and honors a numeric status thrown by a hook:

import { errorHandler } from 'uql-orm/express';
app.use('/api', querierMiddleware({ include: [User] }));
app.use(errorHandler);