PlanetScale

BaaSサーバーレスMySQLデータベースGit風ワークフローブランチング

BaaSプラットフォーム

PlanetScale

概要

PlanetScaleは、Git風のワークフローを採用したサーバーレスMySQLプラットフォームです。データベースのブランチング機能により、スキーマ変更を安全かつ効率的に管理できる革新的なアプローチを提供します。従来のMySQLの強力さとクラウドネイティブの利便性を組み合わせ、開発チームの生産性向上を支援します。

詳細

PlanetScaleは、Vitessクラスタ技術をベースとした次世代のMySQLプラットフォームです。最大の特徴は「データベースブランチング」機能で、コードのブランチと同じようにデータベースのスキーマ変更を管理できます。これにより、開発、テスト、本番環境でのスキーマ変更が安全で追跡可能になります。

Fetch APIベースのJavaScriptクライアントを提供し、従来のコネクションプール管理が不要でサーバーレス環境に最適化されています。また、自動スケーリング、暗号化、バックアップ機能も標準で提供され、運用負荷を大幅に削減します。

メリット・デメリット

メリット

  • Git風ワークフロー: データベースのブランチ作成・マージが可能で、スキーマ変更管理が革新的
  • サーバーレス対応: コネクション管理不要で、Edge環境やサーバーレス関数から直接接続可能
  • ゼロダウンタイム: スキーマ変更やスケーリングが本番環境に影響しない
  • 自動バックアップ: ポイントインタイム復旧とブランチ機能による安全なデータ管理
  • 高いパフォーマンス: Vitessによる分散アーキテクチャで大規模データに対応

デメリット

  • MySQL限定: PostgreSQLなど他のデータベースエンジンは選択できない
  • 学習コスト: 従来のMySQLとは異なるワークフローに慣れる必要がある
  • コスト: 小規模な用途には価格が高めになる場合がある
  • 制限事項: 一部のMySQL機能(外部キー制約等)に制限がある

参考ページ

コード例

1. 基本的な接続とクエリ実行

import { connect } from '@planetscale/database'

const config = {
  host: process.env.DATABASE_HOST,
  username: process.env.DATABASE_USERNAME,
  password: process.env.DATABASE_PASSWORD
}

const conn = connect(config)
const results = await conn.execute('SELECT * FROM users WHERE active = ?', [true])
console.log(results)

2. 環境変数を使用した接続設定

import { connect } from '@planetscale/database'

const config = {
  url: process.env.DATABASE_URL
}

const conn = connect(config)
const result = await conn.execute('SELECT COUNT(*) as total FROM products')
console.log(result.rows[0].total)

3. トランザクション処理

import { connect } from '@planetscale/database'

const conn = connect({
  host: process.env.DATABASE_HOST,
  username: process.env.DATABASE_USERNAME,
  password: process.env.DATABASE_PASSWORD
})

const results = await conn.transaction(async (tx) => {
  const user = await tx.execute('INSERT INTO users (name, email) VALUES (?, ?)', ['太郎', '[email protected]'])
  const profile = await tx.execute('INSERT INTO profiles (user_id, bio) VALUES (?, ?)', [user.insertId, 'エンジニア'])
  return { user, profile }
})
console.log(results)

4. クライアントファクトリーパターン

import { Client } from '@planetscale/database'

const client = new Client({
  host: process.env.DATABASE_HOST,
  username: process.env.DATABASE_USERNAME,
  password: process.env.DATABASE_PASSWORD
})

const conn = client.connection()
const results = await conn.execute('SELECT * FROM orders WHERE status = ?', ['pending'])
console.log(results)

5. カスタム型キャスティング

import { connect, cast } from '@planetscale/database'

function customCast(field, value) {
  if (field.type === 'INT64' || field.type === 'UINT64') {
    return BigInt(value)
  }
  return cast(field, value)
}

const conn = connect({
  cast: customCast,
  host: process.env.DATABASE_HOST,
  username: process.env.DATABASE_USERNAME,
  password: process.env.DATABASE_PASSWORD
})

const result = await conn.execute('SELECT id, balance FROM accounts WHERE id = ?', [1])
console.log(typeof result.rows[0].balance) // 'bigint'

6. Next.js App Router統合例

// app/api/users/route.ts
import { connect } from '@planetscale/database'

const conn = connect({
  url: process.env.DATABASE_URL
})

export async function GET() {
  try {
    const results = await conn.execute('SELECT id, name, email FROM users ORDER BY created_at DESC LIMIT 10')
    return Response.json({ users: results.rows })
  } catch (error) {
    return Response.json({ error: 'Database query failed' }, { status: 500 })
  }
}

export async function POST(request: Request) {
  const { name, email } = await request.json()
  
  try {
    const result = await conn.execute('INSERT INTO users (name, email) VALUES (?, ?)', [name, email])
    return Response.json({ id: result.insertId, name, email }, { status: 201 })
  } catch (error) {
    return Response.json({ error: 'Failed to create user' }, { status: 500 })
  }
}