言語を切り替える
テーマを切り替える

Supabase 入門ガイド:PostgreSQL + Auth + Storage でオールインワン バックエンド

深夜3時、画面上で12回目のデータベース接続エラーを眺めている時、私は気づきました。フロントエンド開発者がフルスタックプロジェクトを作ろうとするのは、本当に大変なんだと。

正直に言うと、以前はバックエンド機能が必要な時、毎回 Node.js や Express を学び、データベース設定やユーザー認証、ファイルストレージをどうにかしなければなりませんでした。どれも大きな落とし穴です。Supabase に出会うまでは。

これはオープンソースの Firebase 代替品ですが、PostgreSQL を使用しており、頭を悩ませる NoSQL ではありません。しかも、データベース、ユーザー認証、ファイルストレージの三つをパッケージ化しています。つまり、オールインワンのバックエンドサービスです。

この記事では、Supabase をゼロから使い始める方法を解説し、Database、Auth、Storage の三大核心機能を重点的に説明します。読み終えた頃には、これを使って完全なバックエンドを素早く構築し、設定ファイルに苦しまれずに済むようになるでしょう。


Supabase とは

まず、Supabase が一体何なのかを説明しましょう。

これは BaaS プラットフォームです。Backend as a Service、つまりバックエンドサービスとしての提供です。どういうことかというと、自分でサーバーを立てたり、データベースを設定したり、API を書いたりする必要がなく、これらを全てやってくれるのです。

Firebase とは異なり、Supabase は完全にオープンソースです。しかも、PostgreSQL を使用しています。これは非常に重要です。PostgreSQL はリレーショナルデータベースで、複雑な SQL クエリを実行でき、データ関係も明確です。Firestore のようなドキュメント型データベースよりもずっと使いやすいです。

Supabase には6つの核心機能があります。

  • Database:PostgreSQL データベースで、SQL クエリをサポートし、REST API も自動生成
  • Auth:ユーザー認証システムで、メールログイン、ソーシャルログイン(Google、GitHub 等)、JWT Token も提供
  • Storage:AWS S3 のようなファイルストレージ、ただしより簡単
  • Realtime:リアルタイムデータ同期、チャットアプリに最適
  • Edge Functions:エッジコンピューティング、AWS Lambda のような機能
  • Vector Database:ベクトルデータベース、AI アプリケーションに最適

ただ、この記事では最初の三つ、Database、Auth、Storage についてのみ説明します。これらが最も核心的な機能であり、他の機能については後日説明します。

ここまで聞くと、オープンソースで、PostgreSQL を使用し、認証とストレージもあるなら、Firebase と比べてどちらが良いのかと思うかもしれません。これについては後で詳しく説明しますが、まず結論を言うと、SQL クエリが必要、データ関係が複雑、またはデータを自分で管理したい場合は Supabase を選びましょう。モバイルアプリを作成し、リアルタイム同期機能が特に重要な場合は Firebase を選びましょう。


クイックスタート

Supabase プロジェクトの作成

まず、supabase.com でアカウントを登録します。登録完了後、「New Project」をクリックして新規プロジェクトを作成します。

プロジェクト作成時にはいくつかの項目を入力する必要があります。

  • プロジェクト名:自由に設定、例えば my-first-app
  • データベースパスワード:これは後で使用するので記録しておいてください
  • リージョン:近い場所を選択、日本国内からなら Singapore または Tokyo

約3分で作成が完了します。作成完了後、プロジェクトダッシュボードが表示され、左側に Table Editor、Authentication、Storage、Edge Functions などの機能メニューが並んでいます。少し多いですが、慌てないでください。この記事で一つずつ説明します。

インストールと初期化

次に、フロントエンドプロジェクトに Supabase をインストールします。まず依存関係をインストールします。

npm install @supabase/supabase-js

インストール完了後、Supabase Dashboard で2つの情報を探します。Project URL と Anon Public Key です。これらは Settings > API で見つかります。

次に、Supabase Client を初期化します。

import { createClient } from '@supabase/supabase-js';

const supabaseUrl = 'https://your-project-id.supabase.co';
const supabaseAnonKey = 'your-anon-public-key';

export const supabase = createClient(supabaseUrl, supabaseAnonKey);

このコードが Supabase との通信の入り口です。データベースクエリ、ユーザーログイン、ファイルアップロードなど、以降の全ての操作はこの supabase オブジェクトを通じて行います。

接続テスト

初期化完了後、まず接続できるかテストしましょう。最も簡単な方法は、データベースにデータがあるかクエリすることです。

const { data, error } = await supabase.from('users').select('*');

if (error) {
  console.error('接続失敗:', error.message);
} else {
  console.log('接続成功!', data);
}

users テーブルが見つからないというエラーが出る場合は正常です。まだテーブルを作成していないからです。次のセクションでテーブルの作成方法を説明します。

一般的なエラーには以下のようなものがあります。

  • URL または Key の入力ミス:完全にコピーしたか確認してください
  • CORS エラー:フロントエンドプロジェクトで CORS 設定が必要かもしれませんが、Supabase はデフォルトで許可しています
  • ネットワーク問題:時々発生します、数分待ってから再試行してください

全て正常であれば、データが返されます。空の配列 [] かもしれず、それはテーブルにまだデータがないことを意味します。


Database:データベース操作

ここが Supabase の核心部分です。以前に PostgreSQL を使用したことがあれば、かなり馴染みがあるでしょう。フロントエンド開発者で SQL に慣れていない場合でも、Supabase は視覚的な Table Editor を提供しており、SQL を書かずにデータベースを操作できます。

データテーブルの作成

テーブル作成には2つの方法があります。Table Editor(視覚的)を使用するか、SQL を書くかです。

まず Table Editor について説明します。Dashboard 左側の「Table Editor」をクリックし、「Create a new table」をクリックします。テーブル名、フィールド名、フィールドタイプを入力する必要があります。

例として、users テーブルを作成します。

フィールド名タイプ制約
idint8Primary Key, Auto Increment
emailtextUnique, Not Null
nametext-
created_attimestamptzDefault: now()

入力完了後、「Save」をクリックするとテーブルが作成されます。

ただ、SQL を書く方が早いと思います。特に後で複雑な操作をする場合です。Supabase は SQL Editor を提供しており、Dashboard 左側で見つかります。

users テーブルと projects テーブルを作成する SQL は以下の通りです。

-- ユーザーテーブル
CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  email TEXT UNIQUE NOT NULL,
  name TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- プロジェクトテーブル
CREATE TABLE projects (
  id SERIAL PRIMARY KEY,
  user_id INTEGER REFERENCES users(id),
  title TEXT NOT NULL,
  description TEXT,
  status TEXT DEFAULT 'active',
  created_at TIMESTAMPTZ DEFAULT NOW()
);

ここに1つの詳細があります。projects テーブルの user_id は外部キーで、users テーブルの id に関連付けられています。これにより、各プロジェクトは特定のユーザーに属します。これがリレーショナルデータベースの利点で、データ関係が非常に明確です。

データ操作 CRUD

テーブルが作成されたので、CRUD 操作を試してみましょう。

データの挿入

// ユーザーを1人挿入
const { data, error } = await supabase
  .from('users')
  .insert([
    { email: 'user@example.com', name: 'John Doe' }
  ]);

if (error) {
  console.error('挿入失敗:', error.message);
} else {
  console.log('挿入成功:', data);
}

データのクエリ

// 全プロジェクトをクエリ、ついでにユーザー情報も取得
const { data, error } = await supabase
  .from('projects')
  .select(`
    *,
    users (
      name,
      email
    )
  `)
  .eq('status', 'active')
  .order('created_at', { ascending: false });

console.log(data);

このクエリは少し面白いです。projects テーブルをクエリするだけでなく、外部キーを通じて関連するユーザー情報も取得しています。1つのクエリで2つのテーブルのデータを取得できました。Firestore でこれを実現するには複数のクエリを書き、手動でデータを結合する必要があります。

データの更新

const { data, error } = await supabase
  .from('projects')
  .update({ status: 'completed' })
  .eq('id', 1);

データの削除

const { data, error } = await supabase
  .from('projects')
  .delete()
  .eq('id', 1);

これらの操作は非常に直感的です。ただ、1つの詳細があります。.eq() は条件フィルタで、「等しい」を意味します。Supabase は他にも多くのフィルタメソッドを提供しており、.gt()(より大きい)、.lt()(より小さい)、.like()(あいまい一致)などがあります。SQL の一般的なフィルタは基本的にサポートされています。

Row Level Security (RLS)

この部分は非常に重要です。特にユーザー関連の機能を作成する場合です。

RLS は Row Level Security の略で、行レベルセキュリティです。どういうことかというと、ユーザーが自分のデータのみを見ることができ、他人のデータは見られないように制御できます。

例えば、projects テーブルには多くのプロジェクトがありますが、ユーザーがログインした後、自分が作成したプロジェクトのみを見られるようにし、他人のを見られないようにしたい場合です。

まず RLS を有効にします。

ALTER TABLE projects ENABLE ROW LEVEL SECURITY;

次に Policy を作成します。

-- ユーザーは自分のプロジェクトのみを見られる
CREATE POLICY "Users can view their own projects"
ON projects FOR SELECT
USING (user_id = auth.uid());

ここで auth.uid() は Supabase が提供する関数で、現在ログインしているユーザーの ID を返します。この Policy の意味は、projects テーブルをクエリする際、user_id が現在のユーザー ID と等しいレコードのみを返すということです。

同様に、挿入、更新、削除の Policy も作成できます。

-- ユーザーは自分のプロジェクトのみを挿入できる
CREATE POLICY "Users can insert their own projects"
ON projects FOR INSERT
WITH CHECK (user_id = auth.uid());

-- ユーザーは自分のプロジェクトのみを更新できる
CREATE POLICY "Users can update their own projects"
ON projects FOR UPDATE
USING (user_id = auth.uid())
WITH CHECK (user_id = auth.uid());

-- ユーザーは自分のプロジェクトのみを削除できる
CREATE POLICY "Users can delete their own projects"
ON projects FOR DELETE
USING (user_id = auth.uid());

これにより、誰かが悪意を持って他人のデータをクエリしようとしても、できなくなります。データベースレベルでブロックされます。フロントエンドコードで判断するよりもずっと安全です。


Auth:ユーザー認証システム

認証に関しては、Supabase はかなり充実しています。多くのログイン方式をサポートしています。メールパスワードログイン、パスワードレスログイン(Magic Link)、ワンタイムパスワード(OTP)、ソーシャルログイン(Google、GitHub、Apple 等 20以上のプラットフォーム)、電話番号ログイン、そしてエンタープライズ級 SSO です。

認証機能の概要

まず、Supabase Auth の核心メカニズムについて説明します。

JWT Token を使用しています。JSON Web Token です。ユーザーがログインに成功すると、Supabase は Token を生成し、フロントエンドはこの Token を持ってバックエンド API をリクエストします。バックエンドは Token を検証し、ユーザーの身元を確認します。

また、Supabase Auth は自動的にユーザー情報を PostgreSQL の auth.users テーブルに保存します。このテーブルは自動的に作成されるので、気にする必要はありません。ユーザー ID、メール、作成時間、最終ログイン時間など、これらの情報がすべて含まれています。

さらに、Session Persistence があります。セッションの永続化です。どういうことかというと、ユーザーがログインした後、ブラウザがこのログイン状態を記憶し、次回ページを開いた時に再ログインする必要がありません。Supabase のフロントエンド SDK が自動的に処理するので、追加のコードを書く必要がありません。

Email/Password 認証

これが最も一般的なログイン方式です。まず登録から説明します。

// ユーザー登録
const { data, error } = await supabase.auth.signUp({
  email: 'user@example.com',
  password: 'securepassword123',
});

if (error) {
  console.error('登録失敗:', error.message);
} else {
  console.log('登録成功:', data);
}

登録成功後、Supabase は確認メールをユーザーのメールアドレスに送信します。ユーザーがメール内のリンクをクリックすると、アカウントがアクティベートされます。これはデフォルトの動作で、Dashboard でオフにできますが、お勧めしません。メール確認は悪意ある登録を防げます。

次にログインです。

// ユーザーログイン
const { data, error } = await supabase.auth.signInWithPassword({
  email: 'user@example.com',
  password: 'securepassword123',
});

if (error) {
  console.error('ログイン失敗:', error.message);
} else {
  console.log('ログイン成功:', data);
}

ログイン成功後、ユーザー情報を取得できます。

// 現在ログインしているユーザーを取得
const { data: { user } } = await supabase.auth.getUser();

console.log('現在のユーザー:', user);
// 出力例:{ id: 'abc123', email: 'user@example.com', ... }

最後にログアウトです。

await supabase.auth.signOut();

ログアウト後、Session はクリアされ、ユーザーは再ログインする必要があります。

もう1つの一般的な要件、パスワードリセットがあります。Supabase はすぐに使える機能を提供しています。

// パスワードリセットメールを送信
const { data, error } = await supabase.auth.resetPasswordForEmail(
  'user@example.com'
);

ユーザーはメールを受信し、リンクをクリックするとパスワードをリセットできます。プロセス全体で追加のロジックを書く必要がありません。

Social Auth ソーシャルログイン

この機能は非常に人気があります。ユーザーはフォームを記入する必要がなく、Google や GitHub アカウントで直接ログインでき、体験がずっと良いです。

まずソーシャルログインを設定します。Dashboard で Authentication > Providers をクリックし、希望のプラットフォームを有効にします。例えば Google や GitHub です。

各プラットフォームの設定は異なりますが、大まかな手順は似ています。

  1. Google/GitHub で OAuth App を作成
  2. Client ID と Client Secret を Supabase にコピー
  3. コールバック URL(Redirect URL)を設定

設定完了後、フロントエンドコードは非常に簡単です。

// Google ログイン
const { data, error } = await supabase.auth.signInWithOAuth({
  provider: 'google',
});

// GitHub ログイン
const { data, error } = await supabase.auth.signInWithOAuth({
  provider: 'github',
});

呼び出し後、ページは Google/GitHub の認可ページにリダイレクトされ、ユーザーが「同意」をクリックすると、アプリケーションに戻ります。この時点でユーザーはログインに成功しています。

また、コールバック URL や追加パラメータをカスタマイズできます。

const { data, error } = await supabase.auth.signInWithOAuth({
  provider: 'google',
  options: {
    redirectTo: 'https://your-app.com/dashboard',
    queryParams: {
      access_type: 'offline',
      prompt: 'consent',
    }
  }
});

redirectTo はログイン成功後にリダイレクトされるページです。queryParams は追加の OAuth パラメータで、例えばオフラインアクセスの要求や、毎回認可を尋ねるなどです。

RLS と Auth の連携

RLS の説明で auth.uid() について触れましたが、ここで繋がります。

ユーザーがログインした後、auth.uid() はユーザーの ID を返します。データベース Policy でこの ID を使用することで、「ユーザーは自分のデータのみにアクセスできる」を実現できます。

例えば、ユーザーがログインした後にプロジェクトを作成し、プロジェクトが自動的に現在のユーザーに関連付けられるようにしたい場合です。

// プロジェクトを作成、自動的に現在のユーザーに関連付け
const { data: { user } } = await supabase.auth.getUser();

const { data, error } = await supabase
  .from('projects')
  .insert([
    {
      title: 'New Project',
      user_id: user.id  // Auth からユーザー ID を取得
    }
  ]);

そして RLS Policy が以下を保証します。

  • クエリ時、user_id = auth.uid() のプロジェクトのみを見られる
  • 挿入時、user_idauth.uid() と等しい必要がある
  • 更新と削除も同様

これにより、認証と権限管理が完全に繋がります。ユーザーログイン、データ関連付け、権限制限が1つのラインで貫通します。


Storage:ファイルストレージ

Supabase Storage はオブジェクトストレージサービスで、AWS S3 に似ていますが、使用がずっと簡単です。ユーザーアバター、画像、ドキュメントなどのファイルを保存するのに適しています。

Storage 機能の概要

核心的な概念は2つあります。Bucket と Object です。

  • Bucket:バケット、フォルダのような概念。複数の Bucket を作成できます。例えば avatars にアバターを保存し、documents にドキュメントを保存
  • Object:具体的なファイル、例えば avatar.jpgreport.pdf

Bucket には2つの権限モードがあります。

  • Public:パブリックバケット、誰でもファイルにアクセス可能(公開画像に適している)
  • Private:プライベートバケット、許可されたユーザーのみがアクセス可能(プライベートドキュメントに適している)

Bucket の作成

Dashboard で Storage をクリックし、「New Bucket」をクリックしてバケットを作成します。

例として、avatars バケットを作成してユーザーアバターを保存します。

  • 名前:avatars
  • Public bucket:チェック(アバターは一般的に公開)
  • File size limit:最大ファイルサイズを設定、例えば 2MB

作成完了後、このバケットにファイルをアップロードできます。

ファイルのアップロードとダウンロード

ファイルのアップロード:

// ユーザーアバターをアップロード
const file = document.getElementById('avatar-input').files[0];

const { data, error } = await supabase.storage
  .from('avatars')
  .upload('user-id/avatar.jpg', file, {
    cacheControl: '3600',
    upsert: false
  });

if (error) {
  console.error('アップロード失敗:', error.message);
} else {
  console.log('アップロード成功:', data);
}

ここにいくつかの詳細があります。

  • upload() の最初のパラメータはファイルパス:'user-id/avatar.jpg'
  • cacheControl:キャッシュ時間、3600 秒
  • upsert:ファイルが既に存在する場合、上書きするか。false は上書きせず、エラーになる。true は上書き

ファイルのダウンロード:

// ファイルをダウンロード
const { data, error } = await supabase.storage
  .from('avatars')
  .download('user-id/avatar.jpg');

if (error) {
  console.error('ダウンロード失敗:', error.message);
} else {
  // data は Blob オブジェクト、URL に変換して表示
  const url = URL.createObjectURL(data);
  document.getElementById('avatar-img').src = url;
}

バケットが公開されている場合、公開 URL を直接取得できます。

// 公開 URL を取得
const { data } = supabase.storage
  .from('avatars')
  .getPublicUrl('user-id/avatar.jpg');

console.log('公開 URL:', data.publicUrl);
// 出力例:https://your-project.supabase.co/storage/v1/object/public/avatars/user-id/avatar.jpg

プライベートバケットのファイルは一時アクセス URL を生成する必要があります。

// 一時アクセス URL を生成(有効期限 1 時間)
const { data, error } = await supabase.storage
  .from('documents')
  .createSignedUrl('private-file.pdf', 3600);

console.log('一時 URL:', data.signedUrl);

ファイルの削除:

const { data, error } = await supabase.storage
  .from('avatars')
  .remove(['user-id/avatar.jpg']);

アクセス制御

Storage も RLS Policy をサポートし、ユーザーがどのファイルにアクセスできるかを制御します。

例えば、ユーザーは自分のフォルダ内のファイルのみをアップロードおよびアクセスできるようにします。

-- ユーザーは自分のフォルダにのみアップロードできる
CREATE POLICY 'Users can upload to their own folder'
ON storage.objects FOR INSERT
WITH CHECK (
  bucket_id = 'avatars' AND
  (storage.foldername(name))[1] = auth.uid()::text
);

-- ユーザーは自分のフォルダ内のファイルのみにアクセスできる
CREATE POLICY 'Users can access their own files'
ON storage.objects FOR SELECT
USING (
  bucket_id = 'avatars' AND
  (storage.foldername(name))[1] = auth.uid()::text
);

ここで storage.foldername(name) はファイルパスからフォルダ部分を抽出します。[1] は最初のフォルダ名を取得、つまりユーザー ID です。

これにより、ユーザーがファイルをアップロードする際、パスは user-id/filename でなければなりません。RLS は user-id が現在のユーザー ID と等しいかを検証します。そうでない場合、アップロードは拒否されます。


実践ケース:タスク管理アプリ

ここまで説明してきましたが、完全なケースでまとめてみましょう。

シンプルなタスク管理アプリケーションを作成すると仮定します。機能は以下の通りです。

  • ユーザー登録とログイン
  • タスクの作成、タスクリストの表示
  • タスク添付ファイルのアップロード

データベース設計

まずテーブル構造を設計します。

-- ユーザーテーブル(Auth が自動作成、手動作成不要)
-- tasks テーブル
CREATE TABLE tasks (
  id SERIAL PRIMARY KEY,
  user_id UUID REFERENCES auth.users(id) NOT NULL,
  title TEXT NOT NULL,
  description TEXT,
  status TEXT DEFAULT 'pending' CHECK (status IN ('pending', 'completed')),
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- RLS を有効化
ALTER TABLE tasks ENABLE ROW LEVEL SECURITY;

-- Policy:ユーザーは自分のタスクのみにアクセスできる
CREATE POLICY 'Users can manage their own tasks'
ON tasks FOR ALL
USING (user_id = auth.uid())
WITH CHECK (user_id = auth.uid());

-- attachments テーブル(タスクに関連付け)
CREATE TABLE task_attachments (
  id SERIAL PRIMARY KEY,
  task_id INTEGER REFERENCES tasks(id) ON DELETE CASCADE,
  file_name TEXT NOT NULL,
  file_path TEXT NOT NULL,
  file_size INTEGER,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- RLS を有効化
ALTER TABLE task_attachments ENABLE ROW LEVEL SECURITY;

-- Policy:タスクを通じて権限を判断
CREATE POLICY 'Users can manage attachments of their tasks'
ON task_attachments FOR ALL
USING (
  EXISTS (
    SELECT 1 FROM tasks
    WHERE tasks.id = task_attachments.task_id
    AND tasks.user_id = auth.uid()
  )
);

ここに1つの詳細があります。task_attachments テーブルの Policy は直接 user_id を判断するのではなく、task_id を通じて tasks テーブルに関連付け、その後 tasks.user_id を判断します。これにより、タスクのオーナーのみが添付ファイルを管理できます。

Storage Bucket の作成

task-files バケットを作成し、プライベートに設定します。

-- Dashboard で作成、または SQL を使用
INSERT INTO storage.buckets (name, public)
VALUES ('task-files', false);

-- Policy:ユーザーは自分のフォルダにのみアップロードできる
CREATE POLICY 'Users can upload task files'
ON storage.objects FOR INSERT
WITH CHECK (
  bucket_id = 'task-files' AND
  (storage.foldername(name))[1] = auth.uid()::text
);

CREATE POLICY 'Users can access task files'
ON storage.objects FOR SELECT
USING (
  bucket_id = 'task-files' AND
  (storage.foldername(name))[1] = auth.uid()::text
);

核心コードの実装

全ての機能を繋げるコードです。

import { createClient } from '@supabase/supabase-js';

const supabase = createClient(
  'https://your-project.supabase.co',
  'your-anon-key'
);

// 1. ユーザー登録
async function register(email: string, password: string) {
  const { data, error } = await supabase.auth.signUp({
    email,
    password,
  });

  if (error) throw error;
  return data;
}

// 2. ユーザーログイン
async function login(email: string, password: string) {
  const { data, error } = await supabase.auth.signInWithPassword({
    email,
    password,
  });

  if (error) throw error;
  return data;
}

// 3. タスク作成
async function createTask(title: string, description?: string) {
  const { data: { user } } = await supabase.auth.getUser();

  if (!user) throw new Error('未ログイン');

  const { data, error } = await supabase
    .from('tasks')
    .insert([
      {
        title,
        description,
        user_id: user.id,
      }
    ])
    .select();

  if (error) throw error;
  return data[0];
}

// 4. タスクリストを取得
async function getTasks() {
  const { data, error } = await supabase
    .from('tasks')
    .select('*')
    .order('created_at', { ascending: false });

  if (error) throw error;
  return data;
}

// 5. 添付ファイルをアップロード
async function uploadAttachment(taskId: number, file: File) {
  const { data: { user } } = await supabase.auth.getUser();

  if (!user) throw new Error('未ログイン');

  const filePath = user.id + '/' + taskId + '/' + file.name;

  const { data, error } = await supabase.storage
    .from('task-files')
    .upload(filePath, file);

  if (error) throw error;

  // task_attachments テーブルに記録
  const { data: attachment, error: dbError } = await supabase
    .from('task_attachments')
    .insert([
      {
        task_id: taskId,
        file_name: file.name,
        file_path: filePath,
        file_size: file.size,
      }
    ])
    .select();

  if (dbError) throw dbError;

  return attachment[0];
}

// 6. タスク添付ファイルを取得
async function getTaskAttachments(taskId: number) {
  const { data, error } = await supabase
    .from('task_attachments')
    .select('*')
    .eq('task_id', taskId);

  if (error) throw error;

  // 一時アクセス URL を生成
  const attachmentsWithURLs = data.map(async (attachment) => {
    const { data: urlData } = await supabase.storage
      .from('task-files')
      .createSignedUrl(attachment.file_path, 3600);

    return {
      ...attachment,
      url: urlData.signedUrl,
    };
  });

  return Promise.all(attachmentsWithURLs);
}

// 7. タスクを完了
async function completeTask(taskId: number) {
  const { data, error } = await supabase
    .from('tasks')
    .update({ status: 'completed', updated_at: new Date() })
    .eq('id', taskId)
    .select();

  if (error) throw error;
  return data[0];
}

// 8. ログアウト
async function logout() {
  await supabase.auth.signOut();
}

このコードは、ユーザー登録ログイン、タスク CRUD、ファイルアップロードをカバーしています。基本的に完全なアプリケーションの骨格があります。これを基にさらに機能を追加できます。例えば、タスク分類、タグ、コメント、通知など。


Supabase vs Firebase 比較

ここまで説明してきましたが、Supabase と Firebase のどちらを選ぶべきか疑問に思うかもしれません。ここで詳しく比較してみましょう。

核心的な違い

比較軸SupabaseFirebase
データベースタイプPostgreSQL(リレーショナル)Firestore(ドキュメント型 NoSQL)
オープンソース完全にオープンソースGoogle のクローズド製品
料金モデル固定 $25/月(Pro Plan)従量課金制(読み取り/書き込み/ストレージ別)
データ所有権100% 所有、いつでもエクスポート可能データは Google に、エクスポートが困難
クエリ能力SQL の強力なクエリ、複雑な結合クエリに制限、複雑な結合は複数回のクエリが必要
リアルタイム機能WebSocket、手動購読が必要Firestore ネイティブリアルタイム、自動同期
オフラインサポート自分でキャッシュを実装ネイティブオフラインサポート、自動同期
移行難易度標準 PostgreSQL、移行が容易独自形式、移行コストが高い

適用シナリオの提案

Supabase を選ぶシナリオ

  • データ関係が複雑、SQL クエリと結合が必要
  • 長期プロジェクト、データを完全に管理したい
  • チームが SQL に精通、PostgreSQL を使用したい
  • コスト重視、予算が限られている(固定費用の方が安定)
  • オープンソース優先、技術スタックを管理したい

Firebase を選ぶシナリオ

  • 迅速なプロトタイプ開発、時間が切迫
  • モバイルアプリ優先、オフライン機能が重要
  • リアルタイム同期が核心機能(例:チャットアプリ)
  • 既に Google Cloud エコシステムを使用
  • データ構造が単純、クエリが複雑でない

正直に言うと、以前 Firebase を使っていくつかのプロジェクトを作成しましたが、後で請求書を見て驚きました。1ヶ月で800ドル以上でした。その後、Supabase に移行したところ、同じ機能で、コストは約25ドルで安定しました。この差はかなり大きいです。

さらに、Firebase からのデータエクスポートは面倒です。Firestore のデータ形式は独自のもので、エクスポート後、変換しないと使えません。Supabase は簡単で、SQL ファイルを直接エクスポートするか、PostgreSQL ツールを使って CSV にエクスポートすれば、いつでも他のデータベースに移行できます。

ただし、Firebase はリアルタイム機能とモバイルアプリに関しては確かに優れています。Firestore のリアルタイム同期、オフラインサポートは非常によくできています。チャットアプリやコラボレーションツールのような強いリアルタイム要件がある場合、Firebase の方が使いやすいかもしれません。


まとめと提案

ここまで説明してきましたが、最後にまとめましょう。

Supabase の核心的な価値は以下の通りです。

  • PostgreSQL データベース——強力なクエリ、明確な関係
  • エンタープライズ級認証——複数のログイン方式、JWT Token、RLS 権限
  • オブジェクトストレージ——簡単で使いやすい、アクセス制御をサポート
  • リアルタイム同期、エッジコンピューティング——後で探索可能

さらに、オープンソースで、データを完全に管理でき、コストが安定しています。これらは長期プロジェクトにとって非常に重要です。

フロントエンド開発者で、素早くフルスタックアプリケーションを構築したいが、バックエンド設定に悩みたくない場合、Supabase は良い選択です。基本的に数時間でデータベース、認証、ストレージの三大機能を完了できます。従来のバックエンド開発よりもずっと速いです。

学習の提案:

  • まず Database、Auth、Storage の三つを理解する、これが核心です
  • 次に RLS Policy を試す、これは認証と権限管理をどう繋げるかを理解するのに役立ちます
  • 実践プロジェクトが最良の学習方法——要件を見つけ、ゼロからアプリケーションを構築する
  • 後で Realtime、Edge Functions、Vector Database などの高度な機能を探索する

ここまでで、ほぼ説明が終わりました。Supabase の公式ドキュメントは非常に明確に書かれているので、分からないことがあれば多く見てみてください。supabase.com/docs。また、コミュニティには多くのチュートリアルやケースがあり、例えば GitHub に awesome-supabase リストがあり、多くのリソースがまとめられています。

一言でまとめると、PostgreSQL でバックエンドを作りたいが、設定に悩みたくない場合、Supabase が道を敷いてくれます。そのまま進めばいいです。


Supabase 三大核心機能クイックスタート

ゼロから Supabase プロジェクトを構築し、Database、Auth、Storage の三大核心機能をマスター

⏱️ 目安時間: 2 時間

  1. 1

    ステップ1: Supabase プロジェクトの作成

    supabase.com でアカウントを登録し、新規プロジェクトを作成:

    • プロジェクト名:my-first-app
    • データベースパスワード:必ず記録
    • リージョン:最も近い場所を選択(日本国内からなら Singapore または Tokyo)

    作成完了後、Settings > API で Project URL と Anon Public Key を取得
  2. 2

    ステップ2: クライアントのインストールと初期化

    フロントエンドプロジェクトに依存関係をインストール:

    npm install @supabase/supabase-js

    Supabase Client を初期化:

    import { createClient } from '@supabase/supabase-js';

    const supabase = createClient(
    'https://your-project.supabase.co',
    'your-anon-key'
    );
  3. 3

    ステップ3: データテーブルの作成と RLS の設定

    SQL Editor を使用してテーブルを作成:

    CREATE TABLE tasks (
    id SERIAL PRIMARY KEY,
    user_id UUID REFERENCES auth.users(id),
    title TEXT NOT NULL,
    status TEXT DEFAULT 'pending'
    );

    ALTER TABLE tasks ENABLE ROW LEVEL SECURITY;

    CREATE POLICY 'Users can manage their own tasks'
    ON tasks FOR ALL
    USING (user_id = auth.uid());

    RLS Policy はユーザーが自分のデータのみにアクセスできることを保証
  4. 4

    ステップ4: ユーザー認証の実装

    Email/Password 認証のコード例:

    // 登録
    await supabase.auth.signUp({
    email: 'user@example.com',
    password: 'password123'
    });

    // ログイン
    await supabase.auth.signInWithPassword({
    email: 'user@example.com',
    password: 'password123'
    });

    // 現在のユーザーを取得
    const { data: { user } } = await supabase.auth.getUser();

    Google、GitHub 等 20以上のプラットフォームのソーシャルログインをサポート
  5. 5

    ステップ5: ファイルストレージの設定

    Storage Bucket を作成(パブリックまたはプライベート):

    // ファイルをアップロード
    await supabase.storage
    .from('avatars')
    .upload('user-id/avatar.jpg', file);

    // 公開 URL を取得
    const { data } = supabase.storage
    .from('avatars')
    .getPublicUrl('user-id/avatar.jpg');

    プライベートファイルは createSignedUrl を使用して一時アクセスリンクを生成

FAQ

Supabase と Firebase の核心的な違いは何ですか?
Supabase は PostgreSQL リレーショナルデータベースを使用し、複雑な SQL クエリとデータ結合をサポートします。Firebase は Firestore ドキュメント型 NoSQL を使用し、クエリ能力に制限があります。Supabase は完全にオープンソースで、固定料金制月額25ドル、データを100%管理可能。Firebase はクローズドソース、従量課金制(月800ドル以上になる可能性)、データエクスポートが困難です。
Supabase はどのようなプロジェクトに適していますか?
以下のシナリオに適しています:

• データ関係が複雑で、SQL クエリと結合が必要
• 長期プロジェクトで、データを完全に管理したい
• コスト重視で、予算が限られている(固定費用の方が安定)
• フロントエンド開発者が素早くフルスタックアプリを構築
• オープンソース優先で、技術スタックを管理したい
Row Level Security (RLS) とは何ですか?何のためにありますか?
RLS は PostgreSQL の行レベルセキュリティ機能で、データベースレベルでユーザーが自分のデータのみにアクセスできるように制御できます。Supabase Auth の auth.uid() 関数と組み合わせて、ユーザーがログイン後に自分が作成したレコードのみを見られる/変更できるように実現し、フロントエンドコードで判断するよりも安全です。
Supabase Auth はどのようなログイン方式をサポートしていますか?
複数のログイン方式をサポート:

• Email/Password(メールパスワード)
• Magic Link(パスワードレスログイン)
• OTP(ワンタイムパスワード)
• Social Auth(Google、GitHub、Apple 等 20以上のプラットフォーム)
• Phone Auth(Twilio、MessageBird)
• SSO(エンタープライズ級シングルサインオン)
Supabase Storage のパブリックバケットとプライベートバケットの違いは何ですか?
パブリックバケット(Public Bucket)のファイルは誰でもアクセスでき、公開画像やアバターに適しています。プライベートバケット(Private Bucket)のファイルは許可が必要で、createSignedUrl を使用して一時アクセスリンクを生成(有効期限を設定可能)、プライベートドキュメントやユーザーアップロードファイルに適しています。
Firebase から Supabase への移行にはどのくらいの時間がかかりますか?
シンプルなアプリケーションなら2〜3日で移行が完了します。Firestore データを PostgreSQL テーブル構造に変換、Firebase Auth ユーザーを Supabase Auth にエクスポート、Cloud Storage ファイルを Supabase Storage に移行。複雑なアプリケーションは1〜2週間かかる可能性があり、主な作業はデータモデル変換とクエリロジックの書き直しです。
Supabase の無料プランには何が含まれていますか?
無料プランには、無制限の API リクエスト、月間50,000アクティブユーザー、500MB データベースストレージ、1GB ファイルストレージ、5GB 帯域幅が含まれます。個人 MVP 開発に適しており、制限を超えた場合は Pro Plan(月額25ドル)にアップグレードが必要です。

8 min read · 公開日: 2026年4月3日 · 更新日: 2026年4月5日

コメント

GitHubアカウントでログインしてコメントできます

関連記事