Supabaseとは
Supabaseは、オープンソースのデータベースであるPostgreSQLをベースにした、Backend as Service (Bass) プラットフォームです。Firebaseにとって代わるサービスとして活躍しています。データベース、認証、ストレージ、Functionsなど、ほぼすべてのバックエンド機能を提供しています。
Supabase Storegeとは
Supabase Storageは、Supabaseのエコシステムの一部として提供されている「ファイルのアップロードと配信」を簡単に行えるサービスです。
通常はファイルシステム(例.サーバー、ストレージデバイス)が無いと、生成したファイルやアップロードしたファイルを管理することができません。しかしSupabase Storageを使用することで、独自でファイルシステムを用意することなくファイルを簡単に保管・取り出しが出来るようになります。
ファイルシステムとは?
コンピューターがデータ(ファイル)を永続的に保存し、整理し、アクセスするための仕組みのこと。
ファイルのアップロードをしてみよう
1.Supbaseプロジェクトの準備
Supabase Storageを利用するには、まずSupabaseプロジェクトが必要です。
・Supabaseプロジェクトの作成
既にプロジェクトがある場合はスキップしてください。
Supabase公式サイトにアクセスし、指示に従ってプロジェクトを作成します。
・APIキーの確認
ファイルアップロードには、SupabaseプロジェクトのAPIキーが必要になります。
Supabase公式サイトの「Project Settings」➡ 「API」セクションで確認できます。
2.ストレージバケットの作成
ファイルを保存するための「バケット」を作成します。バケットはファイルを整理するためのコンテナのようなものです。
-
Supabaseダッシュボードの左サイドバーから
Storageを選択します -
すると、下記のような画面が表示されるのでバケットの名前を入力します。また、バケットの設定には
PublicとPrivateの2パターンがあります。今回はPublicで設定します。

「Puclic」と「Private」の違い
・Public
URLを知っていれば誰でも直接アクセスできる形式。認証は不要。
・Private
認証されたユーザーまたは署名付きURLを介してのみ、アクセスが可能。
これでバケットの作成は完了です!
3.RLSを設定する
Storageに保存された写真をブラウザ上でアップロードしたり閲覧したりするには、Policyの作成による認可が必要です。
※これをしないと、Supbase Storage APIから404エラーや400エラーが返ってきます
RLS(Low Level Security)とは?
「行レベルのセキュリティ」の略で、PostgreSQLの強力なセキュリティ機能を利用して、データベースの行単位でデータへのアクセスを制御する仕組み
基本的な「RLS」の考え方としては、
通常、データベースのアクセス制御はテーブル全体に対して行われます。しかし、RLSを有効にすると、ユーザーがクエリを実行する際に、データベースが自動的に追加の条件を適用し、そのユーザーが見るべきデータ、または操作できるデータだけをフィルタリングします。
これによって、同じテーブルに対しても、ユーザーの役割や属性に応じて表示される行が異なる、といったきめ細かいアクセス制御が可能になります。
Policyを作成する
Storageにアクセスするには、この「Policy」を Public, Private に関わらず設定する必要があります。
- CONFIGURATION ➡「Policies」を選択します
- 右上の
New Policyを選択すると、下記のような画面が出てきます。今回は、 Supabase側がある程度のテンプレートを用意してくれているGet staterd quicklyを選択します。
- 選択すると、作成する
Policyの種類を選ぶ画面が表示されます。ここでアクセスに必要な条件を設定していきます。今回は、いちばん上のAllow access to JPG images in a public folder to anonymous usersを選択します。
設定した Policy を自分なりに変更しました。内容を説明すると、
-- "policy_name"という名前の新しいRLSポリシーを作成します。
CREATE POLICY "policy_name"
-- storage.objectsテーブルの特定操作({operation})に対して、条件({USING | WITH CHECK}以下の内容)を適用します。
ON storage.objects FOR {operation} {USING | WITH CHECK} (
-- ポリシーを適用するバケットを{bucket_name}に限定します。
bucket_id = {bucket_name}
-- ファイルの拡張子が'jpg'または'png'であるもののみにアクセスを許可します。
AND storage."extension"(name) = 'jpg' OR 'png'
-- ファイルが'public'というフォルダ内にあるもののみにアクセスを許可します。
AND LOWER((storage.foldername(name))[1]) = 'public'
-- 認証されていない匿名ユーザーにのみこのポリシーを適用します。
AND auth.role() = 'anon'
);
anon という権限は下記を参照すると、詳細が載っています
4.いよいよ画像をアップロード
export const uploadImage = async (filename: string, file: File) => {
const { data, error } = await supabase.storage
.from("qiita")
.upload(`public/${filename}`, file);
if (error) {
console.error("Error uploading user card:", error);
throw new Error("Failed to upload user card");
}
console.log("Upload successful:", data);
};
export const getImage = (filename: string) => {
const { data } = supabase.storage
.from("qiita")
.getPublicUrl(`public/${filename}`);
return data;
};
import { useCallback, useState } from "react";
import "./App.css";
import { getImage, uploadImage } from "../api/supabaseFunctions";
function App() {
const [memberImage, setMemberImage] = useState<string | ArrayBuffer | null>(
null,
);
// 画像ファイルのinputのchangeイベントハンドラ
const handlerChangeImageFileInput = useCallback(
async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target?.files?.[0];
if (!file) {
return;
}
if (file.type !== "image/jpeg" && file.type !== "image/png") {
console.error("jpeg/pngファイルを選択してください");
return;
}
await uploadImage(`${file.name}`, file);
// 画像の表示
const data = getImage(file.name);
setMemberImage(data.publicUrl);
},
[],
);
return (
<>
<input id="file-upload" name="file-upload" type="file" onChange={handlerChangeImageFileInput}/>
<img src={(memberImage as string) || ""} alt="Image"></img>
</>
);
}
export default App;
コードを上から順に説明していきます!
【画像のアップロード】
export const uploadImage = async (filename: string, file: File) => {
const { data, error } = await supabase.storage
.from("qiita")
.upload(`public/${filename}`, file);
if (error) {
console.error("Error uploading user card:", error);
throw new Error("Failed to upload user card");
}
console.log("Upload successful:", data);
};
supabaseのクライアント設定が済んだ状態を想定しています。
クライアント設定はこちら↓
バケット名の指定
.from("qiita") これでバケット名を指定します。
ファイルパスの指定
.upload(`public/${filename}`, file); ではPolicyで設定した通り、Public フォルダ配下にファイルパスを指定してあげる必要があります。
【画像の取得】
export const getImage = (filename: string) => {
const { data } = supabase.storage
.from("qiita")
.getPublicUrl(`public/${filename}`);
return data;
};
.getPublicUrl(`public/${filename}`); でStorageにアップロードした画像URLを取得します。
fromの引数には保存先のバケット名、getPublicUrl()の引数にはuploadImageによって設定されたフォルダー以下のパスを設定します。
getPublicUrl() の使い方は下記を参照して下さい!
【画像の表示】
const [memberImage, setMemberImage] = useState<string | ArrayBuffer | null>(
null,
);
...
// 画像の表示
const handlerChangeImageFileInput = useCallback(
...
const data = getCardImage(file.name);
setMemberImage(data.publicUrl);
...
)
return (
...
<img src={(memberImage as string) || ""} alt="Image"></img>
...
);
memberImage のstateに、supabaseから取得した画像URLをセットし、img タグの src 属性に設定すると画像を表示できます!
最後に
これで実装は完了です!
お疲れさまでした!
今回は、セキュリティ甘めのPolicyを作成しましたが実際にはユーザーごとにしかアクセスできないよう厳格なPolicyが必要です。
テンプレートで作成したSQLは後で、編集できるので好きなようにカスタムしましょう!
このアカウントでは、主にReact開発におけるエラー記事や便利なツールを紹介していくので、気になる方はフォローをよろしくお願いいたします✨
