2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Supabase を基本から一歩づつ理解する(Database編)

Posted at

はじめに

新しいサービスを生み出していく際に、はじめから重厚長大な作りをすることは少なくなってきているかと思います。はじめはスピーディにサクッと作りたい、でも大きくなってきたらキッチリ作りたい。「サクッと」から「キッチリ」へ切り替える際に、データ構造がガラッと変わってしまうとサービスを育てていく時の課題になるのではないでしょうか。

do-the-right-thing-1024x458.png

(出典)正しいものを正しくつくる

「サクッと」React をはじめとしたフロントエンドを開発する際に、バックエンドには FirebaseAmplify などを選択することが多いかと思います。私も両方とも好きでよく使っています。しかしながら FirebaseAmplify は基本的には 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('*')          

ダッシュボードサンプルキャプチャ

image.png

image.png

image.png

SQL Editor で JOIN のクエリもかけます。

image.png

単純なクエリを書きたい

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;

image.png

単純なテーブル検索で
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"
      }
    ]
  }
]

便利ですね。サクッとアプリが作れそうです

複雑なクエリを書きたい

複雑なクエリを書く時には VIEWRPC の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 ができました。

image.png

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 は以下の記事を参照してください。

注意点

.env
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 を使うと便利です。

image.png

レスポンスが返ってきたら 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'

image.png

おわりに

今回は shops, books テーブル両方とも SELECT Enable read access for all users という公開権限の Row Level Security(RLS) にして検証をしました。

次回は RLS の深掘りと Auth 機能を触り、一歩づつ理解していきたいと思います。

image.png

(参考)

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?