Use the Postgres client your framework or app already owns. Agentuity writes a normal DATABASE_URL; pg, Prisma, Kysely, Drizzle, and other Postgres clients can connect to it directly.
Use this path when you want direct SQL with parameter binding. If your app already uses Prisma, Kysely, or Drizzle, keep that client and point it at the same DATABASE_URL.
npm install pg
npm install -D @types/pgimport { Pool } from 'pg';
const databaseUrl = process.env.DATABASE_URL;
if (!databaseUrl) {
throw new Error('DATABASE_URL is required');
}
export const pool = new Pool({
connectionString: databaseUrl,
});
export async function listActiveUsers(): Promise<
Array<{ readonly id: string; readonly email: string }>
> {
const { rows } = await pool.query<{
readonly id: string;
readonly email: string;
}>('SELECT id, email FROM users WHERE active = $1 ORDER BY created_at DESC', [true]);
return rows;
}Use parameter binding for values that come from users, requests, or jobs. Keep the pool at module scope so framework runtimes can reuse connections between requests.
Get DATABASE_URL
Link a database to the project once:
agentuity project add database app_dataThat command writes DATABASE_URL to .env. To recover the URL later, use:
agentuity cloud db get app_data --show-credentialsThe default terminal output masks credentials. Use --show-credentials only in a trusted shell.
Pooling
Create one Pool at module scope, then import it where the app needs queries. Avoid creating a new pool inside every request handler; that can exhaust database connections under load.
import { Pool } from 'pg';
const databaseUrl = process.env.DATABASE_URL;
if (!databaseUrl) {
throw new Error('DATABASE_URL is required');
}
export const pool = new Pool({
connectionString: databaseUrl,
max: 10,
idleTimeoutMillis: 30_000,
});Tune max for your app's request concurrency and database size. For many framework routes, the default is a reasonable starting point; set it explicitly when you know the workload.
Route Usage
Import the shared pool into server-only code. This example uses a standard Web Response, so the same query shape works in Next.js route handlers, Hono handlers, SvelteKit server routes, Nuxt Nitro routes, Astro endpoints, or a standalone server.
import { pool } from '@/src/db/client';
export async function GET(request: Request): Promise<Response> {
const { searchParams } = new URL(request.url);
const email = searchParams.get('email');
if (!email) {
return Response.json({ error: 'email query param is required' }, { status: 400 });
}
const { rows } = await pool.query<{
readonly id: string;
readonly email: string;
}>('SELECT id, email FROM users WHERE email = $1 LIMIT 1', [email]);
return Response.json({ user: rows[0] ?? null });
}Do not concatenate request values into SQL strings. Use $1, $2, and later placeholders for every dynamic value.
Transactions
Use a pooled client for explicit transactions. Always release the client in finally so the pool can reuse the connection.
import { pool } from './client';
const client = await pool.connect();
try {
await client.query('BEGIN');
await client.query('UPDATE accounts SET balance = balance - $1 WHERE name = $2', [100, 'Alice']);
await client.query('UPDATE accounts SET balance = balance + $1 WHERE name = $2', [100, 'Bob']);
await client.query('COMMIT');
} catch (error) {
await client.query('ROLLBACK');
throw error;
} finally {
client.release();
}Use your ORM's transaction helper when the app already uses Prisma, Kysely, or Drizzle. Keep one transaction boundary in the route or job that owns the write.
Existing Clients
Agentuity does not require a special relational database package in v3. If your app already has one of these clients, keep it and configure the connection URL:
| App client | Configure |
|---|---|
| Prisma | datasource db { url = env("DATABASE_URL") } |
| Kysely | new PostgresDialect({ pool }) with a pg pool |
| Drizzle | drizzle(pool, { schema }) from drizzle-orm/node-postgres |
| custom SQL module | import the shared pg pool |
Use DBClient only for trusted admin scripts that call the Agentuity database service API by resource name and org ID. It is not a parameterized query layer for app traffic.
Next Steps
- Database Overview: create or link Agentuity-managed Postgres
- Drizzle ORM: use schema-first type-safe queries
- Database API Reference: inspect the service API used by trusted admin scripts