| name | core-api-reference |
| description | Use when implementing pgdbm database operations - provides complete AsyncDatabaseManager and DatabaseConfig API with all methods and parameters |
pgdbm Core API Reference
Overview
Complete API reference for AsyncDatabaseManager, DatabaseConfig, and TransactionManager.
All signatures, parameters, return types, and usage examples. No documentation lookup needed.
AsyncDatabaseManager
Initialization
AsyncDatabaseManager(config: DatabaseConfig)
AsyncDatabaseManager(
pool: asyncpg.Pool,
schema: Optional[str] = None
)
Rules:
- Cannot provide both
config and pool
schema only valid with external pool
- Must call
connect() if using config
- Never call
connect() if using external pool
Connection Lifecycle
pool = await AsyncDatabaseManager.create_shared_pool(config: DatabaseConfig) -> asyncpg.Pool
await db.connect() -> None
await db.disconnect() -> None
Query Methods
All methods automatically apply {{tables.}} template substitution.
await db.execute(
query: str,
*args: Any,
timeout: Optional[float] = None
) -> str
await db.execute_and_return_id(
query: str,
*args: Any
) -> Any
await db.fetch_value(
query: str,
*args: Any,
column: int = 0,
timeout: Optional[float] = None
) -> Any
await db.fetch_one(
query: str,
*args: Any,
timeout: Optional[float] = None
) -> Optional[dict[str, Any]]
await db.fetch_all(
query: str,
*args: Any,
timeout: Optional[float] = None
) -> list[dict[str, Any]]
await db.executemany(
query: str,
args_list: list[tuple]
) -> None
Examples:
user_id = await db.execute_and_return_id(
"INSERT INTO {{tables.users}} (email, name) VALUES ($1, $2)",
"alice@example.com",
"Alice"
)
email = await db.fetch_value(
"SELECT email, name FROM {{tables.users}} WHERE id = $1",
user_id,
column=0
)
users = [
("alice@example.com", "Alice"),
("bob@example.com", "Bob"),
("charlie@example.com", "Charlie"),
]
await db.executemany(
"INSERT INTO {{tables.users}} (email, name) VALUES ($1, $2)",
users
)
Bulk Operations
await db.copy_records_to_table(
table_name: str,
records: list[tuple],
columns: Optional[list[str]] = None
) -> int
records = [
("alice@example.com", "Alice"),
("bob@example.com", "Bob"),
]
count = await db.copy_records_to_table(
"users",
records=records,
columns=["email", "name"]
)
Pydantic Integration
from pydantic import BaseModel
class User(BaseModel):
id: int
email: str
name: str
user = await db.fetch_as_model(
User,
query: str,
*args: Any,
timeout: Optional[float] = None
) -> Optional[User]
users = await db.fetch_all_as_model(
User,
query: str,
*args: Any,
timeout: Optional[float] = None
) -> list[User]
user = await db.fetch_as_model(
User,
"SELECT * FROM {{tables.users}} WHERE id = $1",
user_id
)
Schema Operations
exists = await db.table_exists(table_name: str) -> bool
exists = await db.table_exists("users")
exists = await db.table_exists("other_schema.users")
prepared = db.prepare_query(query: str) -> str
print(db.prepare_query("SELECT * FROM {{tables.users}}"))
Note: In shared-pool mode, pgdbm does NOT change search_path. Schema isolation happens via template substitution at query time, not connection configuration.
Transaction Management
async with db.transaction() as tx:
user_id = await tx.fetch_value(
"INSERT INTO {{tables.users}} (email) VALUES ($1) RETURNING id",
email
)
await tx.execute(
"INSERT INTO {{tables.profiles}} (user_id) VALUES ($1)",
user_id
)
async with db.transaction() as tx:
await tx.execute("INSERT INTO {{tables.users}} ...")
async with tx.transaction() as nested:
await nested.execute("UPDATE {{tables.users}} ...")
Monitoring and Performance
stats = await db.get_pool_stats() -> dict[str, Any]
db.add_prepared_statement(
name: str,
query: str
) -> None
Advanced Operations
async with db.acquire() as conn:
await conn.execute("...")
DatabaseConfig
Complete Parameter Reference
from pgdbm import DatabaseConfig
config = DatabaseConfig(
connection_string: Optional[str] = None,
host: str = "localhost",
port: int = 5432,
database: str = "postgres",
user: str = "postgres",
password: Optional[str] = None,
schema: Optional[str] = None,
min_connections: int = 10,
max_connections: int = 20,
max_queries: int = 50000,
max_inactive_connection_lifetime: float = 300.0,
command_timeout: float = 60.0,
server_settings: Optional[dict[str, str]] = None,
init_commands: Optional[list[str]] = None,
ssl_enabled: bool = False,
ssl_mode: Optional[str] = None,
ssl_ca_file: Optional[str] = None,
ssl_cert_file: Optional[str] = None,
ssl_key_file: Optional[str] = None,
ssl_key_password: Optional[str] = None,
statement_timeout_ms: Optional[int] = 60000,
idle_in_transaction_session_timeout_ms: Optional[int] = 60000,
lock_timeout_ms: Optional[int] = 5000,
retry_attempts: int = 3,
retry_delay: float = 1.0,
retry_backoff: float = 2.0,
retry_max_delay: float = 30.0,
)
Common Configurations
Development:
config = DatabaseConfig(
connection_string="postgresql://localhost/myapp_dev",
min_connections=2,
max_connections=10,
)
Production with TLS:
config = DatabaseConfig(
connection_string="postgresql://db.example.com/myapp",
min_connections=20,
max_connections=100,
ssl_enabled=True,
ssl_mode="verify-full",
ssl_ca_file="/etc/ssl/certs/ca.pem",
statement_timeout_ms=30000,
lock_timeout_ms=5000,
)
Custom initialization:
config = DatabaseConfig(
connection_string="postgresql://localhost/myapp",
init_commands=[
"SET timezone TO 'UTC'",
"SET statement_timeout TO '30s'",
],
server_settings={
"jit": "off",
"application_name": "myapp",
},
)
TransactionManager
Same API as AsyncDatabaseManager but within transaction context:
async with db.transaction() as tx:
await tx.execute(query, *args, timeout=None) -> str
await tx.executemany(query, args_list) -> None
await tx.fetch_one(query, *args, timeout=None) -> Optional[dict]
await tx.fetch_all(query, *args, timeout=None) -> list[dict]
await tx.fetch_value(query, *args, column=0, timeout=None) -> Any
async with tx.transaction() as nested_tx:
...
conn = tx.connection
Complete Method Summary
AsyncDatabaseManager - All Methods
| Method | Parameters | Returns | Use Case |
|---|
execute | query, *args, timeout | str | No results needed |
execute_and_return_id | query, *args | Any | INSERT with auto RETURNING id |
executemany | query, args_list | None | Batch execute same query |
fetch_value | query, *args, column, timeout | Any | Single value |
fetch_one | query, *args, timeout | dict|None | Single row |
fetch_all | query, *args, timeout | list[dict] | Multiple rows |
fetch_as_model | model, query, *args, timeout | Model|None | Single row as Pydantic |
fetch_all_as_model | model, query, *args, timeout | list[Model] | Rows as Pydantic |
copy_records_to_table | table, records, columns | int | Bulk COPY (fast) |
table_exists | table_name | bool | Schema checking |
prepare_query | query | str | Debug template expansion |
transaction | - | TransactionManager | Transaction context |
get_pool_stats | - | dict | Pool monitoring |
add_prepared_statement | name, query | None | Performance optimization |
acquire | - | Connection | Advanced: raw connection |
connect | - | None | Initialize pool (config-based only) |
disconnect | - | None | Close pool (config-based only) |
create_shared_pool | config | asyncpg.Pool | Class method: create shared pool |
Compatibility aliases
fetch_val(...) → fetch_value(...)
execute_many(...) → executemany(...)
TransactionManager - All Methods
| Method | Parameters | Returns |
|---|
execute | query, *args, timeout | str |
executemany | query, args_list | None |
fetch_value | query, *args, column, timeout | Any |
fetch_one | query, *args, timeout | dict|None |
fetch_all | query, *args, timeout | list[dict] |
transaction | - | TransactionManager (nested) |
connection | - | Connection (property) |
Note: TransactionManager does NOT have:
- execute_and_return_id
- copy_records_to_table
- fetch_as_model
- table_exists
- Pool management methods
Use regular fetch_value for IDs within transactions.
Template Syntax
All query methods support template substitution:
{{tables.tablename}}
{{schema}}
query = "SELECT * FROM {{tables.users}} WHERE created_at > $1"
Usage Examples
Basic Queries
user_id = await db.execute_and_return_id(
"INSERT INTO {{tables.users}} (email, name) VALUES ($1, $2)",
"alice@example.com",
"Alice"
)
count = await db.fetch_value(
"SELECT COUNT(*) FROM {{tables.users}}"
)
email = await db.fetch_value(
"SELECT email, name FROM {{tables.users}} WHERE id = $1",
user_id,
column=0
)
user = await db.fetch_one(
"SELECT * FROM {{tables.users}} WHERE id = $1",
user_id
)
users = await db.fetch_all(
"SELECT * FROM {{tables.users}} WHERE is_active = $1",
True
)
await db.execute(
"DELETE FROM {{tables.users}} WHERE id = $1",
user_id
)
if await db.table_exists("users"):
print("Users table exists")
Batch Operations
users = [
("alice@example.com", "Alice"),
("bob@example.com", "Bob"),
("charlie@example.com", "Charlie"),
]
await db.executemany(
"INSERT INTO {{tables.users}} (email, name) VALUES ($1, $2)",
users
)
records = [
("alice@example.com", "Alice"),
("bob@example.com", "Bob"),
]
count = await db.copy_records_to_table(
"users",
records=records,
columns=["email", "name"]
)
Pydantic Models
from pydantic import BaseModel
class User(BaseModel):
id: int
email: str
name: str
is_active: bool = True
user = await db.fetch_as_model(
User,
"SELECT * FROM {{tables.users}} WHERE id = $1",
user_id
)
users = await db.fetch_all_as_model(
User,
"SELECT * FROM {{tables.users}} WHERE is_active = $1",
True
)
Transactions
async with db.transaction() as tx:
user_id = await tx.fetch_value(
"INSERT INTO {{tables.users}} (email) VALUES ($1) RETURNING id",
email
)
await tx.execute(
"INSERT INTO {{tables.profiles}} (user_id, bio) VALUES ($1, $2)",
user_id,
"Bio text"
)
async with db.transaction() as tx:
await tx.execute("INSERT INTO {{tables.users}} ...")
try:
async with tx.transaction() as nested:
await nested.execute("UPDATE {{tables.users}} SET risky_field = $1", value)
except Exception:
pass
Monitoring
stats = await db.get_pool_stats()
print(f"Total connections: {stats['size']}")
print(f"Active: {stats['used_size']}")
print(f"Idle: {stats['free_size']}")
print(f"Usage: {stats['used_size'] / stats['size']:.1%}")
usage = stats['used_size'] / stats['size']
if usage > 0.8:
logger.warning(f"High pool usage: {usage:.1%}")
Prepared Statements
db.add_prepared_statement(
"get_user_by_email",
"SELECT * FROM {{tables.users}} WHERE email = $1"
)
DatabaseConfig Complete Reference
Connection Parameters
config = DatabaseConfig(
connection_string="postgresql://user:pass@host:port/database"
)
config = DatabaseConfig(
host="localhost",
port=5432,
database="myapp",
user="postgres",
password="secret",
schema="myschema",
)
Pool Configuration
config = DatabaseConfig(
connection_string="...",
min_connections=5,
max_connections=20,
max_queries=50000,
max_inactive_connection_lifetime=300.0,
command_timeout=60.0,
)
SSL/TLS Configuration
config = DatabaseConfig(
connection_string="postgresql://db.example.com/myapp",
ssl_enabled=True,
ssl_mode="verify-full",
ssl_ca_file="/etc/ssl/certs/ca.pem",
ssl_cert_file="/etc/ssl/certs/client.crt",
ssl_key_file="/etc/ssl/private/client.key",
ssl_key_password="keypass",
)
SSL Modes:
require: Encrypt connection (don't verify certificate)
verify-ca: Verify certificate is signed by trusted CA
verify-full: Verify certificate AND hostname match
Server-Side Timeouts
Prevent runaway queries and stuck transactions:
config = DatabaseConfig(
connection_string="...",
statement_timeout_ms=30000,
idle_in_transaction_session_timeout_ms=60000,
lock_timeout_ms=5000,
)
Default values:
statement_timeout_ms: 60000 (60 seconds)
idle_in_transaction_session_timeout_ms: 60000
lock_timeout_ms: 5000
Set to None to disable.
Connection Initialization
config = DatabaseConfig(
connection_string="...",
server_settings={
"jit": "off",
"application_name": "myapp",
"timezone": "UTC",
},
init_commands=[
"SET timezone TO 'UTC'",
"SET work_mem TO '256MB'",
],
)
Retry Configuration
config = DatabaseConfig(
connection_string="...",
retry_attempts=3,
retry_delay=1.0,
retry_backoff=2.0,
retry_max_delay=30.0,
)
Related Skills
- For patterns:
pgdbm:using-pgdbm, pgdbm:choosing-pattern
- For migrations:
pgdbm:migrations-api-reference
- For testing:
pgdbm:testing-database-code