Claude Skills Guide

MongoDB to PostgreSQL Migration Workflow with Claude Code

Migrating from MongoDB to PostgreSQL represents a significant architectural shift that many development teams face as their applications mature. This guide walks through a practical workflow using Claude Code to automate and streamline the migration process, reducing manual effort and potential errors.

Understanding the Migration Challenge

MongoDB’s document-oriented model and PostgreSQL’s relational structure serve different use cases. When your application outgrows MongoDB’s flexibility or when you need stronger ACID guarantees, foreign key constraints, or complex reporting capabilities, PostgreSQL becomes the natural choice. The migration involves more than just moving data—it requires rethinking schema design, query patterns, and application logic.

Claude Code accelerates this process by generating migration scripts, validating data transformations, and helping you refactor application code to work with the new database model.

Schema Analysis and Design

Begin by analyzing your existing MongoDB collections to understand the data patterns. Extract your MongoDB schema information and feed it to Claude Code for analysis.

// Example: Export MongoDB schema information
db.getCollectionNames().forEach(function(collectionName) {
  var sample = db.getCollection(collectionName).findOne();
  print("Collection: " + collectionName);
  printjson(Object.keys(sample));
});

Claude Code can then suggest PostgreSQL table structures based on the document patterns it discovers. Look for repeating subdocuments that warrant their own tables, arrays that could become relation tables, and embedded data that fits naturally into JSONB columns when normalization proves impractical.

Data Migration Script Generation

Create a Python migration script that Claude Code helps you construct. The script reads from MongoDB and inserts into PostgreSQL with proper type conversions.

from pymongo import MongoClient
import psycopg2
from psycopg2.extras import Json

mongo_client = MongoClient('mongodb://localhost:27017')
mongo_db = mongo_client['your_database']

pg_conn = psycopg2.connect(
    host="localhost",
    database="your_database",
    user="postgres",
    password="your_password"
)

def migrate_users():
    mongo_collection = mongo_db['users']
    cursor = mongo_collection.find()
    
    with pg_conn.cursor() as cur:
        for doc in cursor:
            cur.execute("""
                INSERT INTO users (id, email, profile, created_at, settings)
                VALUES (%s, %s, %s, %s, %s)
                ON CONFLICT (id) DO UPDATE SET
                    email = EXCLUDED.email,
                    profile = EXCLUDED.profile,
                    settings = EXCLUDED.settings
            """, (
                str(doc['_id']),
                doc.get('email'),
                Json(doc.get('profile', {})),
                doc.get('created_at'),
                Json(doc.get('settings', {}))
            ))
        pg_conn.commit()

This pattern uses PostgreSQL’s JSONB for flexible fields while maintaining proper relational structures for core entities. Adjust field mappings based on your specific MongoDB document structure.

Validating Data Integrity

After migration, verify data integrity using validation queries that compare record counts and check for data corruption.

-- Compare document counts
SELECT 'mongodb' as source, COUNT(*) as total FROM users
UNION ALL
SELECT 'postgresql', COUNT(*) FROM users;

-- Sample data comparison
SELECT 
    m.email as mongo_email,
    p.email as pg_email,
    m.created_at as mongo_created,
    p.created_at as pg_created
FROM mongo_users m
JOIN postgresql_users p ON m._id::text = p.id
WHERE m.email != p.email;

You can automate these checks using the tdd skill to create test suites that validate migration correctness. Write tests that verify record counts, sample data accuracy, and referential integrity across related tables.

Application Code Refactoring

With data migrated, update your application layer to use the PostgreSQL driver. This typically involves changing connection strings, updating query builders, and modifying ORM configurations.

# Before: MongoDB connection (Motor)
from motor.motor_asyncio import AsyncIOMotorClient

client = AsyncIOMotorClient('mongodb://localhost:27017')
db = client['your_database']
user = await db.users.find_one({'email': email})

# After: PostgreSQL connection (asyncpg)
import asyncpg

pool = await asyncpg.create_pool(
    host='localhost',
    database='your_database',
    user='postgres'
)

async with pool.acquire() as conn:
    user = await conn.fetchrow(
        'SELECT * FROM users WHERE email = $1', 
        email
    )

Claude Code assists with these refactoring tasks by identifying MongoDB-specific patterns in your codebase and suggesting equivalent PostgreSQL implementations. Focus on replacing aggregation pipelines with SQL queries, document lookups with JOIN operations, and $set updates with standard UPDATE statements.

Using Claude Skills for Testing

Use the pdf skill to generate migration reports documenting the schema mapping decisions, data transformation rules, and any manual cleanup steps required. Create comprehensive documentation that future developers can reference.

Implement regression tests using the tdd skill to ensure your application behaves identically after the migration. Test critical user paths that involve database operations—authentication flows, data retrieval, and update operations should produce identical results.

The supermemory skill helps you maintain institutional knowledge about migration decisions. Record why certain collections were denormalized, which fields use JSONB, and any performance considerations discovered during testing.

Performance Optimization

PostgreSQL offers different optimization strategies than MongoDB. Create appropriate indexes based on your query patterns.

-- Index for frequently queried fields
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_created_at ON users(created_at DESC);

-- Composite index for complex queries
CREATE INDEX idx_orders_user_status 
    ON orders(user_id, status) 
    WHERE status IN ('pending', 'processing');

-- GIN index for JSONB fields
CREATE INDEX idx_users_settings_gin 
    ON users USING gin(settings);

Analyze query performance using EXPLAIN ANALYZE and adjust indexes accordingly. PostgreSQL’s query planner handles complex JOIN operations efficiently, but proper indexing remains essential for read-heavy workloads.

Deployment Strategy

Deploy the migration incrementally using a blue-green approach. Maintain both databases temporarily, with application code that can write to either or both.

# Dual-write pattern during migration period
async def create_user(user_data):
    # Write to PostgreSQL (primary)
    user_id = await pg_create_user(user_data)
    
    # Write to MongoDB (legacy) during transition
    await mongo_db.users.insert_one({
        '_id': user_id,
        **user_data,
        'migrated': True
    })
    
    return user_id

This dual-write pattern lets you migrate data gradually while maintaining consistency. Once all legacy data transfers and application code updates complete, remove the MongoDB dependencies and decommission the old database.

Conclusion

Migrating from MongoDB to PostgreSQL requires careful planning and execution, but Claude Code significantly reduces the manual effort involved. By generating migration scripts, validating data integrity, and assisting with application refactoring, you can complete the transition with confidence. Document your decisions using skills like pdf for reports and supermemory for institutional knowledge, ensuring your team can maintain and evolve the new database architecture effectively.

Built by theluckystrike — More at zovo.one