Skip to content
Back to skills

Planetscale Sdk

ASecurity

Integrates PlanetScale using mysql-connector-python with patterns for

  • 4 stars
  • 0 votes
  • 0 copies
  • 10 views
  • Added June 12, 2026
databasespythongosqldjangoawsgitapidatabasedocumentation

Works with

  • cursor
  • cli
  • api

Security analysis

A100/100

Scanned June 12, 2026

npx -y skills add paulpas/agent-skill-router --skill planetscale-sdk --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Planetscale Sdk?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Planetscale Sdk
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/paulpas-planetscale-sdk/badge)](https://www.skillsdirectory.com/skills/paulpas-planetscale-sdk)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---




name: planetscale-sdk
description: Integrates PlanetScale using mysql-connector-python with patterns for
  database branching, deploy requests, schema management, connection pooling, and
  insights for serverless MySQL.
license: MIT
compatibility: opencode
metadata:
  version: "1.0.0"
  domain: coding
  triggers: planetscale, mysql connector, database branching, planetscale api, how
    do i connect to planetscale from python, serverless mysql, deploy request, vitess
  archetypes:
  - tactical
  - generation
  anti_triggers:
  - brainstorming
  - vague ideation
  - code golf
  - over-engineering
  response_profile:
    verbosity: low
    directive_strength: high
    abstraction_level: operational
  role: implementation
  scope: implementation
  output-format: code
  content-types:
  - code
  - guidance
  - do-dont
  - examples
  related-skills: coding-postgresql-sdk, coding-supabase-sdk, coding-database-migrations




---




# PlanetScale Python SDK Integration

Integrates PlanetScale serverless MySQL using `mysql-connector-python` (and the PlanetScale API via HTTP) with patterns for database branching, deploy requests, schema management, connection with SSL, connection pooling for serverless environments, and querying with Python.

## TL;DR Checklist

- [ ] Use `mysql.connector.connect()` with SSL for database connections — never without SSL
- [ ] Use `mysql.connector.pooling.MySQLConnectionPool` for connection pooling in web apps
- [ ] Use the PlanetScale HTTP API (`/v1/databases`, `/v1/branches`) for branch and deploy request management
- [ ] Use `ALTER TABLE ... ONLINE` for schema changes (PlanetScale's non-blocking DDL)
- [ ] Use GitHub integration for schema change deploy requests
- [ ] Use `sqlparse` or `sqlglot` for linting migrations before deploy requests
- [ ] Use `SELECT /*+ SET_VAR(session_timeout=60) */` for long-running queries in production

---

## When to Use

Use this skill when:

- Building Python applications that connect to PlanetScale serverless MySQL databases
- Implementing database branching workflows (development branches, production branches)
- Managing deploy requests to merge schema changes into production branches
- Writing connection code for serverless environments (AWS Lambda, Vercel, etc.)
- Performing database migrations with PlanetScale's non-blocking schema changes
- Querying MySQL-compatible databases through the PlanetScale proxy
- Integrating PlanetScale with SQLAlchemy or Django ORM

---

## When NOT to Use

- For non-MySQL databases (use the appropriate SDK — PostgreSQL, MongoDB, etc.)
- For workloads that require FOREIGN KEY constraints (PlanetScale doesn't support them)
- For applications that need LOCK TABLES or similar MySQL features not supported by Vitess
- For high-volume OLTP that needs sub-millisecond latency (consider dedicated MySQL)
- When you need full-text search indexes (PlanetScale uses Vitess which has limits)

---

## Core Workflow

### 1. Connect to PlanetScale with SSL

```python
import mysql.connector
from mysql.connector import Error as MySQLError
from mysql.connector.pooling import MySQLConnectionPool
import os

# PlanetScale requires SSL — always use ssl_ca or ssl_mode
config = {
    "host": os.environ["PLANETSCALE_HOST"],        # e.g. us-east.connect.psdb.cloud
    "port": 3306,
    "database": os.environ["PLANETSCALE_DATABASE"],
    "user": os.environ["PLANETSCALE_USERNAME"],
    "password": os.environ["PLANETSCALE_PASSWORD"],
    "ssl_ca": "/etc/ssl/cert.pem",                 # Path to CA certificate
    "ssl_mode": "VERIFY_IDENTITY",                 # or "REQUIRED" if CA path fails
    "connection_timeout": 10,
    "pool_name": "planetscale_pool",
    "pool_size": 5,
}

# Single connection
conn = mysql.connector.connect(**config)
```

**Checkpoint:** Verify the connection with `conn.is_connected()`. PlanetScale requires SSL — never set `ssl_mode` to `DISABLED`. Use `VERIFY_IDENTITY` for production (prevents MITM) and `REQUIRED` if you can't configure the CA path.

### 2. Execute Queries

```python
def query_orders(
    conn,
    customer_id: str,
    limit: int = 50,
) -> list[dict]:
    """Query orders with parameterized SQL."""
    query = """
        SELECT id, order_date, total, status, currency
        FROM orders
        WHERE customer_id = %s
        ORDER BY order_date DESC
        LIMIT %s
    """
    with conn.cursor(dictionary=True) as cursor:
        cursor.execute(query, (customer_id, limit))
        return cursor.fetchall()

def create_order(conn, order_data: dict) -> int:
    """Insert a new order and return its ID."""
    query = """
        INSERT INTO orders (customer_id, total, status, currency)
        VALUES (%(customer_id)s, %(total)s, %(status)s, %(currency)s)
    """
    with conn.cursor() as cursor:
        cursor.execute(query, order_data)
        conn.commit()
        return cursor.lastrowid
```

**Checkpoint:** PlanetScale runs MySQL 8.0+ via Vitess. Use `cursor(dictionary=True)` for dict results. Always call `conn.commit()` for write operations. PlanetScale autocommits DDL but explicit commit is needed for DML.

### 3. Connection Pooling for Serverless

```python
class PlanetScalePool:
    """Thread-safe connection pool for PlanetScale."""

    def __init__(self, pool_size: int = 10):
        self._pool = MySQLConnectionPool(
            pool_name="pscale_pool",
            pool_size=pool_size,
            pool_reset_session=True,
            **get_connection_config(),
        )

    def execute(self, query: str, params: tuple | dict | None = None) -> list[dict]:
        """Acquire a connection, execute, return results."""
        conn = self._pool.get_connection()
        try:
            with conn.cursor(dictionary=True) as cursor:
                cursor.execute(query, params or ())
                if cursor.description:  # SELECT-like query
                    return cursor.fetchall()
                conn.commit()
                return []
        except MySQLError as exc:
            conn.rollback()
            raise RuntimeError(f"Query failed: {exc}") from exc
        finally:
            conn.close()  # Returns to pool

pool = PlanetScalePool()

def get_user_orders(user_id: str) -> list[dict]:
    return pool.execute(
        "SELECT * FROM orders WHERE customer_id = %s ORDER BY created_at DESC LIMIT 50",
        (user_id,),
    )
```

**Checkpoint:** PlanetScale's connection proxy handles serverless connection surges. Pool size of 5-10 is typically sufficient. Close connections with `conn.close()` to return them to the pool — never leave connections open in serverless environments.

### 4. PlanetScale API — Database Branches

```python
import httpx

class PlanetScaleAPI:
    """HTTP client for PlanetScale management API."""

    BASE_URL = "https://api.planetscale.com/v1"

    def __init__(self, service_token: str):
        self._client = httpx.Client(
            base_url=self.BASE_URL,
            headers={
                "Authorization": f"Bearer {service_token}",
                "Content-Type": "application/json",
            },
        )

    def list_branches(self, org: str, db: str) -> list[dict]:
        """List all branches for a database."""
        resp = self._client.get(f"/organizations/{org}/databases/{db}/branches")
        resp.raise_for_status()
        return resp.json()["data"]

    def create_branch(self, org: str, db: str, name: str, parent: str = "main") -> dict:
        """Create a new database branch from a parent branch."""
        resp = self._client.post(
            f"/organizations/{org}/databases/{db}/branches",
            json={"name": name, "parent": parent},
        )
        resp.raise_for_status()
        return resp.json()["data"]

    def create_deploy_request(
        self,
        org: str,
        db: str,
        into_branch: str,
        from_branch: str,
        notes: str = "",
    ) -> dict:
        """Create a deploy request to merge schema changes."""
        resp = self._client.post(
            f"/organizations/{org}/databases/{db}/deploy-requests",
            json={
                "into_branch": into_branch,
                "from_branch": from_branch,
                "notes": notes,
            },
        )
        resp.raise_for_status()
        return resp.json()["data"]

    def deploy_request_status(self, org: str, db: str, dr_id: int) -> dict:
        """Check deploy request status (pending, ready, deploying, complete)."""
        resp = self._client.get(
            f"/organizations/{org}/databases/{db}/deploy-requests/{dr_id}",
        )
        resp.raise_for_status()
        return resp.json()["data"]
```

**Checkpoint:** The PlanetScale API requires a service token (not the database password). Create tokens in the PlanetScale dashboard under Settings > Service Tokens. Deploy requests have states: `pending` → `ready` → `deploying` → `complete` (or `closed` on conflict).

---

## Implementation Patterns

### Pattern 1: Migration Workflow with Deploy Requests

```python
def run_migration(
    api: PlanetScaleAPI,
    org: str,
    db: str,
    branch_name: str,
    migration_sql: str,
) -> dict:
    """Run a schema migration through PlanetScale's branching workflow."""
    # 1. Create a branch for the migration
    branch = api.create_branch(org, db, branch_name)

    # 2. Connect to the branch and run migration
    branch_config = get_connection_config(branch_name=branch_name)
    conn = mysql.connector.connect(**branch_config)
    try:
        with conn.cursor() as cursor:
            for statement in migration_sql.split(";"):
                statement = statement.strip()
                if statement:
                    cursor.execute(statement)
    finally:
        conn.close()

    # 3. Create a deploy request
    dr = api.create_deploy_request(org, db, "main", branch_name, notes=f"Migration: {branch_name}")
    return {"branch": branch["name"], "deploy_request_id": dr["id"]}
```

### Pattern 2: SQLAlchemy Integration

```python
from sqlalchemy import create_engine, text

def create_planetscale_engine() -> Engine:
    """Create a SQLAlchemy engine for PlanetScale."""
    connection_string = (
        f"mysql+mysqlconnector://{os.environ['PLANETSCALE_USERNAME']}:"
        f"{os.environ['PLANETSCALE_PASSWORD']}@"
        f"{os.environ['PLANETSCALE_HOST']}:3306/"
        f"{os.environ['PLANETSCALE_DATABASE']}"
        "?ssl_ca=/etc/ssl/cert.pem&ssl_mode=VERIFY_IDENTITY"
    )
    return create_engine(connection_string, pool_size=5, max_overflow=10)

def query_with_sqlalchemy(engine: Engine, user_id: str) -> list[dict]:
    with engine.connect() as conn:
        result = conn.execute(
            text("SELECT * FROM orders WHERE customer_id = :uid ORDER BY created_at DESC"),
            {"uid": user_id},
        )
        return [row._asdict() for row in result]
```

### BAD vs GOOD: Connection Pattern

```python
# ❌ BAD — Connecting without SSL
def connect_bad():
    return mysql.connector.connect(
        host="us-east.connect.psdb.cloud",
        user="user",
        password="pass",
        database="mydb",
        # No SSL config — will fail on PlanetScale!
    )

# ✅ GOOD — SSL connection with VERIFY_IDENTITY
def connect_good():
    return mysql.connector.connect(
        host=os.environ["PLANETSCALE_HOST"],
        user=os.environ["PLANETSCALE_USERNAME"],
        password=os.environ["PLANETSCALE_PASSWORD"],
        database=os.environ["PLANETSCALE_DATABASE"],
        ssl_ca="/etc/ssl/cert.pem",
        ssl_mode="VERIFY_IDENTITY",
    )
```

### BAD vs GOOD: Schema Changes

```python
# ❌ BAD — Blocking DDL (might lock table on MySQL, unsupported on Vitess)
# ALTER TABLE orders MODIFY COLUMN status VARCHAR(50) NOT NULL;

# ✅ GOOD — Online DDL using PlanetScale branching + deploy request
# 1. Create a branch: pscale branch create myapp-add-status-index
# 2. Make schema change on the branch
# 3. Create a deploy request
# 4. Deploy (zero-downtime)
```

---

## Constraints

### MUST DO
- Always connect with SSL (`ssl_ca` + `ssl_mode=VERIFY_IDENTITY`) — PlanetScale rejects non-SSL connections
- Use connection pooling for production — PlanetScale proxy connections have overhead
- Use database branching for all schema changes — never run DDL directly on production branches
- Use deploy requests to merge schema changes — enables review and rollback
- Always call `conn.commit()` explicitly for write operations
- Use parameterized queries with `%s` placeholders
- Use `cursor(dictionary=True)` for readable results

### MUST NOT DO
- Never connect without SSL — PlanetScale enforces SSL and will drop non-SSL connections
- Do not create connections per request in serverless functions — use a pool or connection proxy
- Avoid FOREIGN KEY constraints — PlanetScale (Vitess) does not support them
- Never run ALTER TABLE directly on production branches — use branching + deploy requests
- Do not use MyISAM tables — PlanetScale only supports InnoDB
- Avoid subqueries in FROM clauses that are not correlated — Vitess may not optimize them well
- Never hardcode database credentials — use environment variables

---

## Output Template

When writing PlanetScale integration code, structure your output as:

1. **Connection Setup** — mysql.connector.connect() with SSL config, host, credentials
2. **Query Execution** — Parameterized SQL with cursor(dictionary=True) for dict results
3. **Pool Management** — MySQLConnectionPool or SQLAlchemy engine for connection reuse
4. **Branching / Deploy Request** — PlanetScale API calls for branch management
5. **Error Handling** — Catch MySQLError, handle connection drops, rollback on write failures

---

## Related Skills

| Skill | Purpose |
|---|---|
| `coding-postgresql-sdk` | PostgreSQL SDK patterns (alternative relational database) |
| `coding-supabase-sdk` | Supabase BaaS patterns for PostgreSQL |
| `coding-database-migrations` | Migration workflow patterns compatible with PlanetScale branching |

---

## Live References

- [PlanetScale Python Connection Guide](https://planetscale.com/docs/tutorials/connect-python) — Official Python connection examples
- [PlanetScale API Reference](https://api-docs.planetscale.com/v1/) — REST API for branch and deploy request management
- [mysql-connector-python Docs](https://dev.mysql.com/doc/connector-python/en/) — Official MySQL Connector/Python documentation
- [PlanetScale Branching Docs](https://planetscale.com/docs/concepts/branching) — Database branching workflow
- [PlanetScale Deploy Requests](https://planetscale.com/docs/concepts/deploy-requests) — Schema change deploy workflow
- [PlanetScale with SQLAlchemy](https://planetscale.com/docs/tutorials/connect-python) — SQLAlchemy engine configuration
- [Vitess MySQL Compatibility](https://planetscale.com/docs/reference/vitess-compatibility) — MySQL features supported by PlanetScale

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…