Loading...

2023-12-29(金) 15:00

🏖️ Supabase のDatabase FunctionsでCSVデータをインポートする

SupabaseNext.js
SupabaseのDatabase Functionを使って、Next.jsなどブラウザから送信されたCSVデータをSupabaseのテーブルにインポートするための手順を解説します。

目次

前提と注意事項

  • Supabase アカウントを作成済みであること
  • Supabase のプロジェクトを作成済みであること
  • この記事では、Next.js でのプロジェクトを想定し、Next.js でユーザーがアップした CSV データを Supabase の Database Functions に送信します。
  • Next.js のバージョンは 14.0.0 で API route を使用しています。

なお、Next.js で CSV データを受け取る部分については以下の記事の内容を前提にしています。

📊 Next.js で CSVインポート機能を実装する

Next.jsでreact-papaparseというライブラリを使用してCSVインポート機能を実装する手順を解説します。

ritaiz.com

上記の記事の内容と合わせて、Supabase のテーブルとして顧客情報を保存するための以下のようなcustomersテーブルがすでに存在しているものとします。

Supabaseのテーブル
CREATE TABLE public.customers (
    id uuid NOT NULL DEFAULT uuid_generate_v4(),
    name text,
    namekana text,
    age decimal,
    birthday date,
    company text,
    created_at timestamp with time zone NOT NULL,
    updated_at timestamp with time zone NOT NULL,
    CONSTRAINT customers_pkey PRIMARY KEY (id)
);

この記事のゴール

Supabase で Database Functions を用意して、それを Next.js で呼び出して CSV データを渡すことでそのデータを Supabase のテーブルにインポートすることがこの記事のゴールになります。
具体的には、Next.js で CSV ファイルをアップロードして以下のようなデータを Supabase Database Functions に送信し、それを Supabase のテーブルにインポートすることを目指します。

CSVデータ
[
  {
    "name": "John",
    "namekana": "ジョン",
    "age": 20,
    "birthday": "2000-01-01",
    "company": "examplecompany1"
  },
  {
    "name": "Jane",
    "namekana": "ジェーン",
    "age": 30,
    "birthday": "1990-01-01",
    "company": "examplecompany2"
  },
  {
    "name": "Bob",
    "namekana": "ボブ",
    "age": 40,
    "birthday": "1980-01-01",
    "company": "examplecompany3"
  }
  ...
]

CSV データを受け取ってインポートする Database Functions を作成する

以下の Supabase の Database Functions を作成します。

import_customers
DROP FUNCTION IF EXISTS import_customers;
CREATE OR REPLACE FUNCTION import_customers(data jsonb)
RETURNS void AS $$
DECLARE
    record jsonb;
BEGIN
    FOR record IN SELECT * FROM jsonb_array_elements(data)
    LOOP
        INSERT INTO public.customers (
            name,
            namekana,
            age,
            birthday,
            company,
            created_at,
            updated_at
        )
        VALUES (
            (record->>'name')::text,
            (record->>'namekana')::text,
            (record->>'age')::DECIMAL,
            (record->>'company')::text,
            now(),
            now()
        );
    END LOOP;
END;
$$ LANGUAGE plpgsql;

上記は、Next.js などフロント側から CSV のデータをjsonbデータとして受け取って、それらを愚直にLOOPで1つづつテーブルにインサートする関数です。 LOOPを使わずに 1 つのINSERT文でまとめてインサートする方法もありますが、今回は大量のデータではなくかつ使用頻度も少ないため上記のようにLOOPを使ったものにしています。


あとはこのDatabase FunctionsをNext.jsで呼び出します。 なお、上記は受け取ったデータの検証などは行なっていないため、値がnullである場合は何かしらのデフォルト値で置き換えたいなどある場合は、NULLIFなどを使うのが良いと思います。

Supabase の Database Functions を呼び出す API route を作成する

Next.js で Supabase のクライアントは準備できているとし、以下のようにして Supabase の Database Functions を使うための API ルートを作成します。

/app/api/customers/route.ts
import { NextResponse } from 'next/server';
 
// Supabase Client
import { getSupabaseClient } from '@/utils/supabaseClient';
 
// import customer records
export async function POST(request: Request) {
  // アップロードしたCSVデータを取得
  const reqData = await request.json();
  let _error: any = null;
 
  // Supabaseクライアントを準備
  const supabase = getSupabaseClient();
 
  // rpcでDatabase Functionsを呼び出す
  const { error: _error } = await supabase.rpc('import_customers', {
    data: reqData, // csvデータを渡す
  });
 
  if (_error) {
    return NextResponse.json({ message: _error.message }, { status: 500 });
  }
 
  return NextResponse.json({ message: 'Success' }, { status: 200 });
}

上記で@/utils/supabaseClientからgetSupabaseClientを呼び出して使っていますが、この部分については Supabase の公式ドキュメントのものを参考にしています。

Creating a Supabase client for SSR

Creating a Supabase client for SSR

supabase.com

CSV データを API ルートに送信する関数を用意する

後は以下のようにして、CSV データを API ルートに POST するための関数を用意しておきます。

@/utils/api
'use server';
import { headers } from 'next/headers';
 
// POST API
export async function postApi(newData: any) {
  const _apiUrl = '/api/customers';
 
  try {
    const res = await fetch(_apiUrl, {
      method: 'POST',
      headers: {
        Accept: 'application/json, text/plain, */*',
        'Content-Type': 'application/json',
        cookie: headers().get('cookie') as string,
      },
      body: JSON.stringify(newData),
    });
    if (res.status !== 200) {
      const error = await res.text();
      return { data: null, error: error };
    }
 
    const data = await res.json();
    return { data: data.data, error: null };
  } catch (e) {
    console.error(e);
    return { data: null, error: true };
  }
}

あとはインポート実行ボタンを押した場合などに以下のようにpostApiに CSV データを渡して実行すれば Supabase に CSV データをインポートできるはずです。

クライアントサイドでpostApiを実行する
import { postApi } from '@/utils/api';
 
// インポート実行ボタンを押した時の処理
const handleOnImport = async () => {
  // 省略
 
  // uploadedListにはアップロードしたCSVデータが入っているとする。
  const response = await postApi(uploadedList);
 
  // 省略
};

まとめ

Next.js で受け取った CSV データを Supabase のテーブルにインポートするために、Supabase の Database Functions を作成し、それを Next.js から呼び出す方法を解説しました。もし大量のデータをインポートする場合は、今回のように Database Functions を使う方法ではなく、別のサーバーでpgloaderを使うなどの方法もあります。