Skip to content
NewFrom Mongoose to SQL: migrating MongoDB to PostgreSQL7 min read

Express

Your Express routes query the pool directly. If you also want CRUD routes for many entities without writing them, uql-orm/express mounts the HTTP handler as middleware.

import express from 'express';
import { Post } from './shared/models/index.js';
import { pool } from './uql.config.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);

The pool shows the uql.config.ts it comes from. Run several writes together in pool.transaction.

Requires Express 5.

import { querierMiddleware } from 'uql-orm/express';
import { Post, User } from './shared/models/index.js';
app.use(express.json()); // writes and QUERY read the parsed body
app.use('/api', querierMiddleware({ pool, include: [User, Post] }));

This serves the wire protocol for each entity, QUERY included. A request for an unknown entity or route falls through with next(), so your own routes can share /api.

querierMiddleware takes the handler’s hooks and getContext. Their context is the express.Request:

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

req.user is whatever your auth middleware declares on Express.Request (Passport does it for you).

Errors go to next(err), so your error middleware still runs. errorHandler renders the error envelope, honoring a numeric status a hook threw:

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