Store structured data with queries, joins, and transactions in a managed Postgres database. Inside an Agentuity project, the CLI writes the database connection string to DATABASE_URL; use @agentuity/postgres when you want automatic reconnects around Bun's SQL driver.
Use the @agentuity/db standalone package to query an Agentuity-managed database by database and orgId from any Node.js or Bun app.
When to Use Database Storage
| Storage Type | Best For |
|---|---|
| Database | Structured data, complex queries, transactions, relational data |
| Key-Value | Fast lookups, caching, configuration |
| Vector | Semantic search, embeddings, RAG |
| Object (S3) | Files, images, documents, media |
| Durable Streams | Large exports, audit logs |
When you create a database with agentuity cloud db create from a project, the DATABASE_URL is added to your local .env file. Deployed apps receive DATABASE_URL when the deployment is associated with that database resource; otherwise, set it as an environment variable before deploy.
Creating a Database
# Create with default settings
agentuity cloud db create
# Create with name and description
agentuity cloud db create --name users_db --description "Primary user data store"Quick Start
bun add @agentuity/postgresimport { postgres } from '@agentuity/postgres';
const sql = postgres();
// Query with automatic SQL injection protection
const users = await sql`SELECT * FROM users WHERE active = ${true}`;
// Insert data
await sql`INSERT INTO users (name, email) VALUES (${'Alice'}, ${'alice@example.com'})`;
// Update data
await sql`UPDATE users SET active = ${false} WHERE id = ${userId}`;
// Delete data
await sql`DELETE FROM users WHERE id = ${userId}`;Always use template literal parameters (${value}) for dynamic values. Never concatenate strings into queries.
Using in Agents
import { createAgent } from '@agentuity/runtime';
import { postgres } from '@agentuity/postgres';
const sql = postgres();
const agent = createAgent('UserQuery', {
handler: async (ctx, input) => {
const users = await sql`
SELECT * FROM users
WHERE active = ${true}
AND created_at > ${input.since}
ORDER BY created_at DESC
LIMIT 10
`;
ctx.logger.info('Query results', { count: users.length });
return { users };
},
});Using in Routes
import { Hono } from 'hono';
import type { Env } from '@agentuity/runtime';
import { postgres } from '@agentuity/postgres';
const router = new Hono<Env>();
const sql = postgres();
router.get('/users', async (c) => {
const limit = parseInt(c.req.query('limit') || '10');
const users = await sql`
SELECT id, name, email FROM users
ORDER BY created_at DESC
LIMIT ${limit}
`;
return c.json({ users });
});
router.post('/users', async (c) => {
const body = await c.req.json();
const result = await sql`
INSERT INTO users (name, email)
VALUES (${body.name}, ${body.email})
RETURNING id, name, email
`;
return c.json({ user: result[0] }, 201);
});
export default router;Transactions
Use transactions for operations that must succeed or fail together:
import { postgres } from '@agentuity/postgres';
const sql = postgres();
const tx = await sql.begin();
try {
await tx`UPDATE accounts SET balance = balance - ${amount} WHERE id = ${fromAccount}`;
await tx`UPDATE accounts SET balance = balance + ${amount} WHERE id = ${toAccount}`;
await tx`INSERT INTO transfers (from_id, to_id, amount) VALUES (${fromAccount}, ${toAccount}, ${amount})`;
await tx.commit();
} catch (error) {
await tx.rollback();
throw error;
}Managed Postgres
Agentuity database resources are managed Postgres databases. agentuity cloud db create writes DATABASE_URL, and postgres() reads it by default:
import { postgres } from '@agentuity/postgres';
const sql = postgres();
const users = await sql`SELECT * FROM users`;For raw Bun SQL behavior outside the Agentuity wrapper, see the Bun SQL docs.
Next Steps
- Resilient Postgres Client: Auto-reconnecting PostgreSQL client for serverless
- Drizzle ORM: Type-safe database access with Drizzle
- Key-Value Storage: Fast caching and session data
- Vector Storage: Semantic search and embeddings