Use when deciding which pgdbm pattern to use (standalone, dual-mode library, or shared pool) - provides decision tree based on deployment context without requiring doc exploration
Provides a decision tree to choose between standalone, dual-mode library, or shared pool patterns based on your deployment context. Use when starting a new pgdbm project or refactoring database architecture.
/plugin marketplace add juanre/pgdbm/plugin install pgdbm@juanre-ai-toolsThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Core Principle: Choose pattern based on deployment context and reusability needs.
pgdbm supports three main patterns. This skill helps you choose the right one in <30 seconds without reading multiple docs.
What are you building?
│
├─ Reusable library/package for PyPI?
│ └─ → DUAL-MODE LIBRARY pattern
│ • Accept connection_string OR db_manager
│ • Works standalone AND embedded
│
├─ Single application with multiple services/modules?
│ └─ → SHARED POOL pattern
│ • ONE pool, many schema-isolated managers
│ • Most efficient for production
│
└─ Simple standalone service/microservice?
└─ → STANDALONE pattern
• AsyncDatabaseManager(DatabaseConfig(...))
• Simplest setup
| If you have... | Use this pattern | Key indicator |
|---|---|---|
| Library published to PyPI | Dual-Mode | Code needs to work in someone else's app |
| FastAPI monolith with routers | Shared Pool | Multiple services, same process |
| Multiple services, same app | Shared Pool | Need connection efficiency |
| Background worker (separate process) | Standalone | Different OS process |
| Simple microservice | Standalone | One service, own database |
| Multi-tenant SaaS | Shared Pool | Many tenants, schema isolation |
Triggers:
Key characteristics:
{{tables.}}Red flags you need this:
import mylib in your READMEExample minimal setup:
class MyLibrary:
def __init__(
self,
connection_string: Optional[str] = None,
db_manager: Optional[AsyncDatabaseManager] = None,
):
if not connection_string and not db_manager:
raise ValueError("Provide one or the other")
self._external_db = db_manager is not None
self.db = db_manager
self._connection_string = connection_string
async def initialize(self):
if not self._external_db:
config = DatabaseConfig(connection_string=self._connection_string)
self.db = AsyncDatabaseManager(config)
await self.db.connect()
# ALWAYS run migrations
migrations = AsyncMigrationManager(
self.db, "migrations", module_name="mylib"
)
await migrations.apply_pending_migrations()
For complete implementation: See pgdbm:dual-mode-library skill
Triggers:
Key characteristics:
Red flags you need this:
AsyncDatabaseManager(DatabaseConfig(...)) in same appExample minimal setup:
# In lifespan
config = DatabaseConfig(connection_string="postgresql://...")
shared_pool = await AsyncDatabaseManager.create_shared_pool(config)
# Each service gets schema-isolated manager
users_db = AsyncDatabaseManager(pool=shared_pool, schema="users")
orders_db = AsyncDatabaseManager(pool=shared_pool, schema="orders")
# Run migrations for each
for db, path, name in [(users_db, "migrations/users", "users"), ...]:
migrations = AsyncMigrationManager(db, path, name)
await migrations.apply_pending_migrations()
For complete implementation: See pgdbm:shared-pool-pattern skill
Triggers:
Key characteristics:
Red flags you need this:
Example minimal setup:
config = DatabaseConfig(connection_string="postgresql://...")
db = AsyncDatabaseManager(config)
await db.connect()
migrations = AsyncMigrationManager(db, "migrations", module_name="myservice")
await migrations.apply_pending_migrations()
# Use db
user_id = await db.fetch_value(
"INSERT INTO {{tables.users}} (email) VALUES ($1) RETURNING id",
email
)
await db.disconnect()
For complete implementation: See pgdbm:standalone-service skill
Using Standalone but should use Shared Pool:
AsyncDatabaseManager(DatabaseConfig(...)) to same databasemax_connections limitUsing Shared Pool but should use Dual-Mode:
Using Dual-Mode but should use Standalone:
Question to ask: Same process or different process?
Answer: Each container = Standalone pattern
AsyncDatabaseManager(DatabaseConfig(...))Answer: Still use Dual-Mode if used by multiple apps
| Aspect | Standalone | Dual-Mode | Shared Pool |
|---|---|---|---|
| Complexity | Low | Medium | Medium |
| Flexibility | Low | High | Medium |
| Connection Efficiency | Low | Varies | High |
| Use Case | Simple services | Reusable libraries | Multi-service apps |
| Pool Creation | Creates own | Conditional | Uses provided |
| Migration Management | Owns | Always runs own | Each service runs own |
| Best For | Microservices, workers | PyPI packages | Monoliths, multi-tenant |
Scenario: "I'm building a FastAPI app with user authentication, blog posts, and comments"
Decision process:
Answer: Shared Pool Pattern
Why:
Before implementing, ask:
Who creates the database manager?
How many services need database access?
Will my code be imported by other projects?
Once you've chosen:
pgdbm:dual-mode-library for full implementationpgdbm:shared-pool-pattern for full implementationpgdbm:standalone-service for full implementationAll patterns use:
{{tables.}} syntax (mandatory)module_name in migrations (mandatory)Whatever pattern you choose, NEVER:
module_name in AsyncMigrationManagerdb.schema at runtimeThese violations break pgdbm's core assumptions.
This skill should be used when the user asks to "create a hookify rule", "write a hook rule", "configure hookify", "add a hookify rule", or needs guidance on hookify rule syntax and patterns.
Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.