Newer
Older
supabase-sample / src / app / api / export-csv / route.ts
Shinya Tomozumi on 31 May 5 KB テーブル情報の更新
// src/app/api/export-csv/route.ts
import { NextResponse } from 'next/server';
import { createClient } from '@supabase/supabase-js';
import { stringify } from 'csv-stringify';
import fs from 'fs/promises'; // Node.jsのファイルシステムモジュール
import path from 'path';     // Node.jsのパス操作モジュール

// Supabaseクライアントの初期化
// データのエクスポートには、通常、読み取り権限のある anon key で十分ですが、
// Row Level Security (RLS) のポリシーに注意してください。
// RLSが有効な場合、ポリシーによってはデータが取得できません。
// 全データをエクスポートしたい場合は、SUPABASE_SERVICE_ROLE_KEY を使うのが確実です。
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL || process.env.SUPABASE_URL;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY; // Service Role Keyを優先

if (!supabaseUrl || !supabaseKey) {
  throw new Error('Supabase URL or API Key is missing in environment variables.');
}

const supabase = createClient(supabaseUrl, supabaseKey as string, {
  auth: {
    persistSession: false,
  },
});

// エクスポート先のフォルダ
const exportDirPath = path.join(process.cwd(), 'export');

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const tableNamesParam = searchParams.get('tables'); // URLパラメータからテーブル名を取得 (例: ?tables=users,products)

  // エクスポートしたいテーブル名の配列
  // URLパラメータで指定されたテーブル名、またはデフォルトのリスト
  let tablesToExport: string[];

  if (tableNamesParam) {
    tablesToExport = tableNamesParam.split(',').map(name => name.trim().toLowerCase());
  } else {
    // デフォルトでエクスポートしたいテーブルのリスト
    // ここにあなたのテーブル名を追加してください。
    // 例: ['users', 'serif', 'tests']
    tablesToExport = ['users', 'serif', 'tests'];
  }

  const results: {
    tableName: string;
    status: string;
    insertedCount?: number;
    error?: string;
    details?: any;
    hint?: any;
    code?: any;
    rowCount?: Number;
    filePath?: string;
  }[] = [];

  try {
    // export フォルダが存在しない場合は作成
    await fs.mkdir(exportDirPath, { recursive: true });

    for (const tableName of tablesToExport) {
      console.log(`Exporting data from table: ${tableName}...`);
      const csvFilePath = path.join(exportDirPath, `${tableName}.csv`);

      try {
        // Supabaseからデータを取得
        // RLSが有効な場合、適切なSELECTポリシーがないと空のデータが返るか、権限エラーになります。
        const { data, error } = await supabase
          .from(tableName)
          .select('*'); // すべてのカラムを選択

        if (error) {
          console.error(`Supabase fetch error for ${tableName}:`, error);
          results.push({
            tableName,
            status: 'failed',
            error: error.message,
            details: (error as any).details || null,
            hint: (error as any).hint || null,
            code: (error as any).code || null,
          });
          continue; // 次のテーブルへ
        }

        if (!data || data.length === 0) {
          console.log(`No data found in table: ${tableName}. Skipping CSV creation.`);
          results.push({ tableName, status: 'skipped', error: 'No data found' });
          continue;
        }

        // CSV文字列に変換
        // `stringify` は、データ配列とオプションを受け取る
        // ヘッダー行を自動で含めるために `header: true` を指定
        const csvString = await new Promise<string>((resolve, reject) => {
          stringify(data, { header: true }, (err, output) => {
            if (err) {
              return reject(err);
            }
            resolve(output as string);
          });
        });

        // CSVファイルを保存
        await fs.writeFile(csvFilePath, csvString, { encoding: 'utf8' });

        console.log(`Successfully exported ${data.length} rows to ${csvFilePath}`);
        results.push({
          tableName,
          status: 'success',
          rowCount: data.length,
          filePath: csvFilePath,
        });

      } catch (tableExportError: any) {
        console.error(`Error exporting table ${tableName}:`, tableExportError);
        results.push({
          tableName,
          status: 'failed',
          error: `Error processing table ${tableName}: ${tableExportError.message}`,
        });
      }
    }

    return NextResponse.json({
      message: 'CSV export process completed.',
      status: 'completed',
      results: results,
    });

  } catch (error) {
    console.error('Unexpected error during batch CSV export:', error);
    return NextResponse.json(
      { message: 'An unexpected error occurred during batch CSV export', status: 'error', error: (error as Error).message },
      { status: 500 }
    );
  }
}