はじめに
新しいサービスを生み出していく際に、はじめから重厚長大な作りをすることは少なくなってきているかと思います。はじめはスピーディにサクッと作りたい、でも大きくなってきたらキッチリ作りたい。「サクッと」から「キッチリ」へ切り替える際に、データ構造がガラッと変わってしまうとサービスを育てていく時の課題になるのではないでしょうか。
(出典)正しいものを正しくつくる
「サクッと」React をはじめとしたフロントエンドを開発する際に、バックエンドには Firebase
や Amplify
などを選択することが多いかと思います。私も両方とも好きでよく使っています。しかしながら Firebase
や Amplify
は基本的には NoSQL
です。RDB
で「サクッと」バックエンドを提供してくれるものが Supabase
です。
Supabase
は「The Open Source Firebase Alternative / オープンソースの Firebase の代替」と自らを謳っており、慣れ親しんだリレーショナル・データベース(PostgreSQL)でありながら使いやすい BaaS(Backend as a Service)なのです。
本稿では、手を動かしながら Supabase
を基本から一歩づつ理解していきたいと思います。第一弾は Database 編です。
Supabase とは
読み方は「スーパーベース」と読みます。
主要な機能は以下です。
- Database
- Auth
- Storage
- Realtime
- Edge Functions
Database
shops と books というテーブルを作ります。GUI でも SQL でもどちらでも作ることが出来ます。Supabase が便利なのはテーブルを作ると自動的に RESTful API と GraphQL を生成してくれることです。
curl 'http://localhost:8000/rest/v1/books?select=*' \
-H "apikey: SUPABASE_CLIENT_ANON_KEY" \
-H "Authorization: Bearer SUPABASE_CLIENT_ANON_KEY"
let { data: books, error } = await supabase
.from('books')
.select('*')
ダッシュボードサンプルキャプチャ
SQL Editor で JOIN のクエリもかけます。
単純なクエリを書きたい
JavaScript(React)から Supabase のクエリを発行してみます。 @supabase/supabase-js
を使います。
import { useEffect, useState } from "react";
import { createClient } from "@supabase/supabase-js";
const supabase = createClient("http://localhost:8000", "your-anon-key");
function App() {
const [shops, setShops] = useState([]);
const [books, setBooks] = useState([]);
const [shopsJoinBooks, setShopsJoinBooks] = useState([]);
useEffect(() => {
getShops();
getBooks();
getShopsJoinBooks();
}, []);
async function getShops() {
const { data } = await supabase.from("shops").select();
setShops(data);
}
async function getBooks() {
const { data } = await supabase.from("books").select();
setBooks(data);
}
async function getShopsJoinBooks() {
const { data } = await supabase.from('shops')
.select(`
id, name,
books (
id, name
)
`);
setShopsJoinBooks(data);
}
return (
<>
<ul>
{shops.map((shop) => (
<li key={shop.id}>{shop.name}</li>
))}
</ul>
<ul>
{books.map((book) => (
<li key={book.id}>{book.name}</li>
))}
</ul>
<ul>
{shopsJoinBooks.map((shop) => (
<li key={shop.id}>
<h3>{shop.name}</h3>
<ul>
{shop.books.map((book) => (
<li key={book.id}>{book.name}</li>
))}
</ul>
</li>
))}
</ul>
</>
);
}
export default App;
単純なテーブル検索で
const { data } = await supabase.from("shops").select();
は
GET http://localhost:8000/rest/v1/shops?select=*
となります。返却値は以下となります。
[
{
"id": "7294ad9a-535a-406e-87c9-2c33aee1e629",
"created_at": "2025-02-28T07:31:04+00:00",
"name": "shop1"
}
]
次に、
const { data } = await supabase
.from('shops')
.select(`
id, name,
books (
id, name
)
`);
というJOINのクエリは、
GET http://localhost:8000/rest/v1/shops?select=id,name,books(id,name)
となります。返却値は以下となります。
[
{
"id": "7294ad9a-535a-406e-87c9-2c33aee1e629",
"name": "shop1",
"books": [
{
"id": "6b086feb-bc6b-461b-b5ec-6f09ae94d8e2",
"name": "book1"
},
{
"id": "41c9bc66-c2cc-4537-b50b-2ebecb6d7673",
"name": "book2"
}
]
}
]
便利ですね。サクッとアプリが作れそうです
複雑なクエリを書きたい
複雑なクエリを書く時には VIEW
か RPC
の2つの方法があります。
今回は VIEW
を作成してみます。
CREATE VIEW public.shops_books_view AS
SELECT
shops.id AS shop_id,
shops.name AS shop_name,
books.id AS book_id,
books.name AS book_name
FROM
shops
JOIN
books ON shops.id = books.shop_id;
VIEW
ができました。
React / JavaScript から参照してみます。
const { data } = await supabase.from("shops_books_view").select();
は
GET http://localhost:8000/rest/v1/shops_books_view?select=*
になります。返却値は以下となります。
[
{
"shop_id": "7294ad9a-535a-406e-87c9-2c33aee1e629",
"shop_name": "shop1",
"book_id": "6b086feb-bc6b-461b-b5ec-6f09ae94d8e2",
"book_name": "book1"
},
{
"shop_id": "7294ad9a-535a-406e-87c9-2c33aee1e629",
"shop_name": "shop1",
"book_id": "41c9bc66-c2cc-4537-b50b-2ebecb6d7673",
"book_name": "book2"
}
]
RPC は以下の記事を参照してください。
注意点
SUPABASE_PUBLIC_URL=http://localhost:8000
ANON_KEY=your-anon-key
ブラウザで動く React / CSR で Supabase を使う場合、アクセスキー(ANON_KEYとSUPABASE_PUBLIC_URL)を隠す必要はありません。
テーブルごとに Row Level Security(RLS) / 行レベルセキュリティ
を適切に設定することでアクセスコントロールをします。そのため、各種アクセスキーを隠す必要が無いという考え方になります。
(出典)開発者を魅了するオープンソースソフトウェア Supabase とは | AWS Dev Day 2022 Japan #AWSDevDay
番外編(環境構築)
Supabase は公式で Docker Componse の構成を提供しています。
ローカル環境の構築手順のメモを記載します。
環境構築
# Supabase のリポジトリを取得
git clone https://github.com/supabase/supabase.git
# フォルダを移動
cd supabase/docker
# .env ファイルの設定
cp .env.example .env
# Supabase を起動
docker compose up -d
動作確認
curl --location 'http://localhost:8000/rest/v1/' \
--header 'apikey: your-anon-key'
Postman を使うと便利です。
レスポンスが返ってきたら Supabase はローカルで動いています。
管理画面は http://localhost:8000/
で確認できます。
番外編(API I/F 仕様書)
以下のAPIを叩くと Swagger
が返却されます。Swagger Editor に入力すると API I/F 仕様書を表示することが出来ます。
たまたま気がついたのですが、便利ですねぇ。
curl --location 'http://localhost:8000/rest/v1/' \
--header 'apikey: your-anon-key'
おわりに
今回は shops, books テーブル両方とも SELECT Enable read access for all users
という公開権限の Row Level Security(RLS) にして検証をしました。
次回は RLS の深掘りと Auth 機能を触り、一歩づつ理解していきたいと思います。
(参考)