PostgreSQL Memory
The @voltagent/postgres
package provides a PostgresStorage
provider that uses PostgreSQL for persistent storage of agent memory.
This is ideal for production applications requiring enterprise-grade database storage, complex queries, or integration with existing PostgreSQL infrastructure.
Setup
Install Package
First, install the necessary packages:
- npm
- yarn
- pnpm
npm install @voltagent/postgres
yarn add @voltagent/postgres
pnpm add @voltagent/postgres
Database Requirements
You need a PostgreSQL server (version 12 or higher recommended). The provider automatically creates all necessary tables and indexes when initialized, so no manual SQL setup is required.
Credentials
You'll need your PostgreSQL connection details:
- Host: Your PostgreSQL server hostname
- Port: Usually 5432
- Database: Database name
- User: Database username
- Password: Database password
Store these credentials securely, typically as environment variables or use a connection string format.
Configuration
Import PostgresStorage
and initialize it with your credentials:
import { Agent } from "@voltagent/core";
import { PostgresStorage } from "@voltagent/postgres";
import { VercelAIProvider } from "@voltagent/vercel-ai";
import { openai } from "@ai-sdk/openai";
// Using connection string (recommended)
const memory = new PostgresStorage({
connection: process.env.DATABASE_URL || "postgresql://postgres:password@localhost:5432/mydb",
// Optional: Adjust connection pool size
maxConnections: 10,
// Optional: Specify a custom base table name prefix
tablePrefix: "voltagent_memory", // Defaults to 'voltagent_memory'
// Optional: Storage limit (max number of messages per user/conversation)
storageLimit: 100, // Defaults to 100
});
// Alternative: Using connection object
const memory = new PostgresStorage({
connection: {
host: process.env.DB_HOST || "localhost",
port: parseInt(process.env.DB_PORT || "5432"),
database: process.env.DB_NAME || "mydb",
user: process.env.DB_USER || "postgres",
password: process.env.DB_PASSWORD,
ssl: process.env.NODE_ENV === "production", // Enable SSL for production
},
maxConnections: 10,
tablePrefix: "voltagent_memory",
storageLimit: 100,
});
const agent = new Agent({
name: "PostgreSQL Memory Agent",
instructions: "An agent using PostgreSQL for memory.",
llm: new VercelAIProvider(),
model: openai("gpt-4o"),
memory: memory, // Assign the memory provider instance
});
Configuration Options:
connection
(string or object, required): Database connection details.- Connection string:
"postgresql://user:password@host:port/database"
- Connection object:
{ host, port, database, user, password, ssl }
- Connection string:
maxConnections
(number, optional): Maximum connections in the pool. Defaults to10
.tablePrefix
(string, optional): Prefix for database table names. Defaults tovoltagent_memory
.storageLimit
(number, optional): Maximum messages to retain per conversation. Defaults to100
.debug
(boolean, optional): Enable debug logging. Defaults tofalse
.
Conversation Management
Get User's Conversations
// Get recent conversations for a user
const conversations = await storage.getConversationsByUserId("user-123", {
limit: 50,
orderBy: "updated_at",
orderDirection: "DESC",
});
// Display in sidebar like ChatGPT
conversations.forEach((conv) => {
console.log(`${conv.title} - ${conv.updatedAt}`);
});
Advanced Query Builder
// Fluent query interface
const recentChats = await storage
.getUserConversations("user-123")
.limit(20)
.orderBy("updated_at", "DESC")
.execute();
// Get paginated conversations
const page1 = await storage.getPaginatedUserConversations("user-123", 1, 10);
console.log(`Page 1 of ${page1.hasMore ? "many" : page1.conversations.length}`);
Querying Conversations
The PostgreSQL storage provides powerful conversation querying capabilities with filtering, pagination, and sorting options:
// Query with multiple filters
const workConversations = await storage.queryConversations({
userId: "user-123",
resourceId: "work-agent",
limit: 25,
offset: 0,
orderBy: "created_at",
orderDirection: "DESC",
});
// Get all conversations for a user
const userConversations = await storage.queryConversations({
userId: "user-123",
limit: 50,
});
// Get conversations for a specific resource
const resourceConversations = await storage.queryConversations({
resourceId: "chatbot-v1",
limit: 100,
orderBy: "updated_at",
});
// Admin view - get all conversations
const allConversations = await storage.queryConversations({
limit: 200,
orderBy: "created_at",
orderDirection: "ASC",
});
Query Options:
userId
(optional): Filter conversations by specific userresourceId
(optional): Filter conversations by specific resourcelimit
(optional): Maximum number of conversations to return (default: 50)offset
(optional): Number of conversations to skip for pagination (default: 0)orderBy
(optional): Field to sort by: 'created_at', 'updated_at', or 'title' (default: 'updated_at')orderDirection
(optional): Sort direction: 'ASC' or 'DESC' (default: 'DESC')
Getting Conversation Messages
Retrieve messages for a specific conversation with pagination support:
// Get all messages for a conversation
const messages = await storage.getConversationMessages("conversation-456");
// Get messages with pagination
const firstBatch = await storage.getConversationMessages("conversation-456", {
limit: 50,
offset: 0,
});
// Get next batch
const nextBatch = await storage.getConversationMessages("conversation-456", {
limit: 50,
offset: 50,
});
// Process messages in batches for large conversations
const batchSize = 100;
let offset = 0;
let hasMore = true;
while (hasMore) {
const batch = await storage.getConversationMessages("conversation-456", {
limit: batchSize,
offset: offset,
});
// Process batch
processBatch(batch);
hasMore = batch.length === batchSize;
offset += batchSize;
}
Message Query Options:
limit
(optional): Maximum number of messages to return (default: 100)offset
(optional): Number of messages to skip for pagination (default: 0)
Messages are returned in chronological order (oldest first) for natural conversation flow.
Automatic Table Creation
Unlike manual database setup, PostgresStorage
automatically creates the necessary tables (messages
, conversations
, agent_history
, agent_history_steps
, agent_history_timeline_events
, with the configured tablePrefix
) and indexes in your PostgreSQL database if they don't already exist. This simplifies setup for both development and production.
Production Considerations
For production applications, consider:
- SSL Connections: Enable SSL by setting
ssl: true
in your connection configuration. - Connection Pooling: Adjust
maxConnections
based on your application's concurrent usage. - Environment Variables: Store database credentials securely using environment variables.
- Database Backups: Implement regular backup strategies for your PostgreSQL database.
Use Cases
- Production Applications: Enterprise-grade applications requiring robust, scalable database storage.
- Existing PostgreSQL Infrastructure: Applications already using PostgreSQL for other data.
- Complex Queries: Scenarios requiring advanced SQL capabilities or data analytics.
- High Availability: Applications requiring database replication and failover capabilities.
- Team Collaboration: Multi-user applications where conversation data needs to be shared or analyzed.
Error Handling
try {
await storage.addMessage(message, userId, conversationId);
} catch (error) {
if (error.message.includes("foreign key constraint")) {
console.error("Conversation does not exist");
} else {
console.error("Database error:", error);
}
}