Skip to content

Next.js + UQL

This guide shows how to use UQL with Next.js App Router to build a type-safe API and server actions.

You will:

  • Configure UQL in a shared module.
  • Use it from route handlers.
  • Optionally, call it from server actions.

Terminal window
npm install uql-orm pg

Create an entities.ts file in your app (for example in src/db/entities.ts):

import { Entity, Id, Field } from 'uql-orm';
@Entity()
export class User {
@Id({ type: 'uuid' })
id?: string;
@Field({ unique: true })
email?: string;
@Field()
name?: string;
}

Then create a UQL pool in src/db/uql.ts:

import { PgQuerierPool } from 'uql-orm/postgres';
import { User } from './entities';
export const uqlPool = new PgQuerierPool(
{
host: process.env.POSTGRES_HOST,
database: process.env.POSTGRES_DB,
user: process.env.POSTGRES_USER,
password: process.env.POSTGRES_PASSWORD,
},
{
entities: [User],
},
);

In Next.js, this module will be treated as a server-only file when imported from server components or route handlers.


Create an API route such as app/api/users/route.ts:

import { NextResponse } from 'next/server';
import { uqlPool } from '@/db/uql';
import { User } from '@/db/entities';
export async function GET() {
const users = await uqlPool.transaction(async (querier) => {
return await querier.findMany(User, {
$select: { id: true, name: true, email: true },
$limit: 20,
});
});
return NextResponse.json(users);
}

This keeps your query fully type-safe and works across all supported databases.


You can also use UQL directly inside server actions for form submissions or mutations:

'use server';
import { uqlPool } from '@/db/uql';
import { User } from '@/db/entities';
export async function createUser(formData: FormData) {
const email = formData.get('email') as string;
const name = formData.get('name') as string;
await uqlPool.transaction(async (querier) => {
await querier.insertOne(User, { email, name });
});
}

Once you have entities in place, you can plug in semantic search for AI features:

import { Article } from '@/db/entities';
export async function searchArticles(queryEmbedding: number[]) {
return await uqlPool.transaction(async (querier) => {
return await querier.findMany(Article, {
$select: { id: true, title: true },
$sort: { embedding: { $vector: queryEmbedding, $distance: 'cosine' } },
$limit: 10,
});
});
}

See the AI & Semantic Search page for end-to-end patterns.