Claude Code Plugins

Community-maintained marketplace

Feedback

standalone-service

@juanre/pgdbm
0
0

Use when building simple standalone service or microservice with its own database - provides complete setup without shared pool complexity

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name standalone-service
description Use when building simple standalone service or microservice with its own database - provides complete setup without shared pool complexity

Standalone Service Pattern

Overview

Core Principle: Create own pool with DatabaseConfig, run migrations, use database.

Simplest pgdbm pattern for services that own their database.

When to Use This

From pgdbm:choosing-pattern skill - use when:

  • Single service with dedicated database
  • Background worker (separate process)
  • Simple microservice
  • Development/testing
  • Learning pgdbm

Complete Implementation

from pgdbm import AsyncDatabaseManager, AsyncMigrationManager, DatabaseConfig

# Create configuration
config = DatabaseConfig(
    connection_string="postgresql://localhost/myservice",
    min_connections=5,
    max_connections=20,
)

# Create database manager
db = AsyncDatabaseManager(config)
await db.connect()

# Run migrations
migrations = AsyncMigrationManager(
    db,
    migrations_path="./migrations",
    module_name="myservice"  # Required
)

result = await migrations.apply_pending_migrations()

# Use database
user_id = await db.execute_and_return_id(
    "INSERT INTO {{tables.users}} (email, name) VALUES ($1, $2)",
    "alice@example.com",
    "Alice"
)

# Cleanup (on shutdown)
await db.disconnect()

That's it. Complete standalone setup.

FastAPI Integration

from contextlib import asynccontextmanager
from fastapi import FastAPI, Request
from pgdbm import AsyncDatabaseManager, AsyncMigrationManager, DatabaseConfig

@asynccontextmanager
async def lifespan(app: FastAPI):
    # Startup: Create database connection
    config = DatabaseConfig(
        connection_string="postgresql://localhost/myapp",
        min_connections=5,
        max_connections=20,
    )

    db = AsyncDatabaseManager(config)
    await db.connect()

    # Run migrations
    migrations = AsyncMigrationManager(
        db,
        migrations_path="./migrations",
        module_name="myapp"
    )
    await migrations.apply_pending_migrations()

    # Store in app state
    app.state.db = db

    yield

    # Shutdown: Close connection
    await db.disconnect()

app = FastAPI(lifespan=lifespan)

@app.post("/users")
async def create_user(email: str, name: str, request: Request):
    db = request.app.state.db

    user_id = await db.execute_and_return_id(
        "INSERT INTO {{tables.users}} (email, name) VALUES ($1, $2)",
        email,
        name
    )

    return {"id": user_id, "email": email}

@app.get("/users/{user_id}")
async def get_user(user_id: int, request: Request):
    db = request.app.state.db

    user = await db.fetch_one(
        "SELECT * FROM {{tables.users}} WHERE id = $1",
        user_id
    )

    if not user:
        raise HTTPException(status_code=404, detail="User not found")

    return user

Project Structure

myservice/
├── main.py              # FastAPI app or entry point
├── migrations/
│   ├── 001_create_users.sql
│   ├── 002_create_posts.sql
│   └── 003_add_indexes.sql
├── tests/
│   ├── conftest.py
│   └── test_users.py
├── .env                 # DATABASE_URL
└── pyproject.toml

Environment Configuration

# .env
DATABASE_URL=postgresql://user:password@localhost:5432/myservice
import os
from pgdbm import DatabaseConfig

# Load from environment
config = DatabaseConfig(
    connection_string=os.getenv("DATABASE_URL"),
    min_connections=int(os.getenv("DB_MIN_CONN", "5")),
    max_connections=int(os.getenv("DB_MAX_CONN", "20")),
)

Background Worker Example

import asyncio
from pgdbm import AsyncDatabaseManager, AsyncMigrationManager, DatabaseConfig

async def process_job(db, job):
    """Process a single job."""
    result = await do_work(job)

    await db.execute(
        "INSERT INTO {{tables.job_results}} (job_id, result) VALUES ($1, $2)",
        job.id,
        result
    )

async def main():
    # Setup database
    config = DatabaseConfig(
        connection_string="postgresql://localhost/worker",
        min_connections=2,
        max_connections=10,
    )

    db = AsyncDatabaseManager(config)
    await db.connect()

    # Run migrations
    migrations = AsyncMigrationManager(
        db,
        migrations_path="./migrations",
        module_name="worker"
    )
    await migrations.apply_pending_migrations()

    # Worker loop
    while True:
        job = await get_next_job()
        if job:
            await process_job(db, job)
        else:
            await asyncio.sleep(1)

if __name__ == "__main__":
    asyncio.run(main())

With Schema Isolation

Even standalone services can use schemas:

config = DatabaseConfig(
    connection_string="postgresql://localhost/mydb",
    schema="myservice",  # Service gets own schema
    min_connections=5,
    max_connections=20,
)

db = AsyncDatabaseManager(config)
await db.connect()

# Migrations automatically go to "myservice" schema
migrations = AsyncMigrationManager(
    db,
    migrations_path="./migrations",
    module_name="myservice"
)
await migrations.apply_pending_migrations()

# All queries automatically scoped to schema
# {{tables.users}} → "myservice".users

When to use schema:

  • Sharing database with other services
  • Want namespace isolation
  • Multi-tenant within service

Testing

# tests/conftest.py
from pgdbm.fixtures.conftest import *

# tests/test_users.py
async def test_create_user(test_db):
    """Test user creation."""
    # Apply your migrations to test database
    migrations = AsyncMigrationManager(
        test_db,
        migrations_path="./migrations",
        module_name="test"
    )
    await migrations.apply_pending_migrations()

    # Test your code
    user_id = await test_db.execute_and_return_id(
        "INSERT INTO {{tables.users}} (email) VALUES ($1)",
        "test@example.com"
    )

    assert user_id == 1

When NOT to Use Standalone

Don't use standalone if:

  • You have multiple services in same Python process → Use shared pool
  • Building reusable library → Use dual-mode
  • Multiple routers in FastAPI app → Use shared pool

Standalone is for truly independent services.

Quick Checklist

  • Create DatabaseConfig with connection_string
  • Create AsyncDatabaseManager(config)
  • Call await db.connect()
  • Create AsyncMigrationManager with unique module_name
  • Apply migrations
  • Use {{tables.}} syntax in all SQL
  • Call await db.disconnect() on shutdown

That's the complete standalone pattern.

Related Skills

  • For pattern selection: pgdbm:choosing-pattern
  • For mental model: pgdbm:using-pgdbm
  • For complete API: pgdbm:core-api-reference, pgdbm:migrations-api-reference
  • For testing: pgdbm:testing-database-code