Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

This article is a Private article. Only a writer and users who know the URL can access it.
Please change open range to public in publish setting if you want to share this article with other users.

2025/4/8 から 2025/4/18 にやったこと

Posted at

現状

なんとなくユーザー認証機能から作り始めてしまいまして、だいたい、こんな機能が実装できました

db.kiban の認証に関する仕様

  1. Firebase の匿名認証でお試し利用ができる
  2. ID + パスワード または Google認証で永久ログインユーザーを設定できる
  3. 匿名認証ユーザーは永久ログインユーザーに移行できる

ここまでは Firebase Authentication を利用して実装を進めていたのですが、Firebase を使いたい理由は「匿名認証」を使ってみたかったという点にありました。ID と パスワードの登録って面倒だし、素性もしれないサイトでそんなことしたくない、と誰もがお考えになることだと思います。匿名認証ができれば、気軽に利用いただけるかな、と考えました

Vercel にデプロイ済みで公開も可能なのですが、公開するには少々手直ししたい部分もありまして、こちらは今のところ非公開といたします

2025/4/8 に知ったこと

しかしつい先ほど Supabase で匿名認証が使えるということを知りました

匿名認証は Firebase にしかない機能と思い込んでいたのでかなり驚きました

db.bkiban のデータベースとして Supabase Database (PostgreSQL) を利用する想定でしたので、認証も Supabase Auth で行けるのであれば一気に開発が進みそうです

Firebase は老舗の BaaS であり、その認証機能はとても洗練されていました。日本語ドキュメントも完備されていて、公式ページの手順で迷わず実装できました。一方 Supabase はドキュメントの大半が英語という点と、Firebase と比べるとだいぶマイナー (失礼な言い方で申し訳ありませんが日本語の技術情報が少ないという意味です) なので、その点では開発スピードがマイナスされてしまいそうです

Supabase はまだ公式ドキュメントを眺めた程度の理解なのですが、Firebase で自動で行われる機能が Supabase では手動で実行 (SQL投入!) するなど洗練されていない部分がありました。逆に言うと Firebase を使っている限りブラックボックスだった機能が Supabase では確認SQLや更新SQLを実行するといった「自分が何をしているかわかる」と言う意味ではデメリットばかりでもなさそうです

私は Firebase の課金モデルが好きではないので、そういった面でも Supabase です。Supabase さんチャレンジさせてください! どうぞよろしく!

という訳で 2025/4/1 から 4/7 にやったことは以下の限定共有ページに移動して、改めて開発の記録をはじめたいと思います

github

何はなくとも github です。2つのリポジトリを使っていこうと思います

  • db.bkiban.web (next.js Webブラウザアプリ)
  • db.bkiban.func (Supabase Edge functions Rest API)

アーキテクチャ

さて github のおうちができたところで アーキテクチャ の検討です

こちらを参照していましたが詳しくは Firebase で 匿名認証 を実装する にあります

Web アプリケーション

ローカル開発環境の構築手順と Vercel へデプロイする部分もFirebase で 匿名認証 を実装する にあります

db.kiban の主な機能 (ログインしていない場合)

  1. 公開設定された DB定義 の一覧表示 (カテゴリごとに表示する)
  2. DB定義 のテーブルごとの詳細表示
  3. 公開設定された DB のデータ照会
  4. 公開設定された DB に対するコメント機能

db.kiban の主な機能 (ログインしている場合)

ログインしていない場合の機能に加えて、以下の機能を提供する

  1. DB定義
  2. DBへのデータ投入、メンテナンス
  3. DBデータのファイル出力とダウンロード
  4. DB公開

まずはログインしていない状態で利用可能な機能を実装していきたいと思います (2025/4/1 から 4/7 に行なった開発と逆の順序で進めます)

データモデル

こんなデータモデルを考えてみました

tables

Name Type Default Value Primary Memo
id uuid gen_random_uuid() On
created_at timestampz now() Off
updated_at timestampz Off 注記1
user_id uuid Off auth.users.id
name varchar Off
description varchar NULL Off NULL許可
is_public bool false Off
public_at timestamp Off
num_users int8 0 Off
num_cols int8 0 Off

注記1: moddatetime という Extension で設定できそう

rows

Name Type Default Value Primary Memo
table_id uuid On tables.id
id int8 Is Identity (注記2) On
created_at timestampz now() Off
updated_at timestampz Off 注記1
name varchar Off
description varchar NULL Off NULL許可
is_public bool false Off
is_valid bool true Off

注記2: 列に連続した一意の番号を自動的に割り当てます

cols

Name Type Default Value Primary Memo
table_id uuid On tables.id
id int8 Is Identity (注記2) On
rows_id int8 On rows.id
created_at timestampz now() Off
updated_at timestampz Off 注記1
value varchar Off
is_public bool false Off
is_valid bool true Off

データ型はテキストのみですが、これで表現できそうな気がしています

Supabase

create table

慣れるまではできるだけ Supabase コンソールからアクセスした方が無難だと思い Table Editor を使ってみたのですが、エラーが出まくってしまい、ものすごいストレスを感じたので SQL で定義しました

create table public.tables (
  id uuid not null default gen_random_uuid(),
  created_at timestamp with time zone not null default now(),
  updated_at timestamp with time zone not null default now(),
  user_id uuid not null ,
  name varchar(128) not null ,
  description varchar(2048),
  is_public boolean not null default false,
  public_at timestamp with time zone,
  num_users int8 not null default 0,
  num_cols int8 not null default 0,
  constraint tables_pkey primary key (id)
) tablespace pg_default;

psql が今のところ使えませんので SQL でテーブル定義を確認してみました

確認用 SQL

select
isc.table_name
, isc.ordinal_position
, isc.column_name
, isc.data_type
, isc.character_maximum_length
, isc.column_default
, isc.is_nullable
from
information_schema.columns isc
where
isc.table_schema = 'public'    /* ここは可変 */
and isc.table_name = 'tables'  /* ここも可変 */
order by isc.ordinal_position;

実行結果

[
  {
    "table_name": "tables",
    "ordinal_position": 1,
    "column_name": "id",
    "data_type": "uuid",
    "character_maximum_length": null,
    "column_default": "gen_random_uuid()",
    "is_nullable": "NO"
  },
  {
    "table_name": "tables",
    "ordinal_position": 2,
    "column_name": "created_at",
    "data_type": "timestamp with time zone",
    "character_maximum_length": null,
    "column_default": "now()",
    "is_nullable": "NO"
  },
  {
    "table_name": "tables",
    "ordinal_position": 3,
    "column_name": "updated_at",
    "data_type": "timestamp with time zone",
    "character_maximum_length": null,
    "column_default": "now()",
    "is_nullable": "NO"
  },
  {
    "table_name": "tables",
    "ordinal_position": 4,
    "column_name": "user_id",
    "data_type": "uuid",
    "character_maximum_length": null,
    "column_default": null,
    "is_nullable": "NO"
  },
  {
    "table_name": "tables",
    "ordinal_position": 5,
    "column_name": "name",
    "data_type": "character varying",
    "character_maximum_length": 128,
    "column_default": null,
    "is_nullable": "NO"
  },
  {
    "table_name": "tables",
    "ordinal_position": 6,
    "column_name": "description",
    "data_type": "character varying",
    "character_maximum_length": 2048,
    "column_default": null,
    "is_nullable": "YES"
  },
  {
    "table_name": "tables",
    "ordinal_position": 7,
    "column_name": "is_public",
    "data_type": "boolean",
    "character_maximum_length": null,
    "column_default": "false",
    "is_nullable": "NO"
  },
  {
    "table_name": "tables",
    "ordinal_position": 8,
    "column_name": "public_at",
    "data_type": "timestamp with time zone",
    "character_maximum_length": null,
    "column_default": null,
    "is_nullable": "YES"
  },
  {
    "table_name": "tables",
    "ordinal_position": 9,
    "column_name": "num_users",
    "data_type": "bigint",
    "character_maximum_length": null,
    "column_default": "0",
    "is_nullable": "NO"
  },
  {
    "table_name": "tables",
    "ordinal_position": 10,
    "column_name": "num_cols",
    "data_type": "bigint",
    "character_maximum_length": null,
    "column_default": "0",
    "is_nullable": "NO"
  }
]

問題ないようです。Supabase コンソールから Table Editor で確認してみましたが、こちらもダイジョウブそうでした

updated_at は更新の都度更新したい (おかしな日本語な) のですが以下の手順で設定できました

Supabase の時間関係のカラムに関して

こちらの記事には postgres のトリガーを Supabase GUI からトリガーを登録する手順と SQL で登録する手順が記載されていましたが、GUI からはうまく登録できず、SQL による登録を行いました

Edge Functions からのデータアクセス

こんな機能が必要になると思っています

  • addTable
  • getTables
  • getTable
  • modTable
  • delTable
  • isPublicTable

rows と cols についても概ね同じ構成の Rest API を用意していきたいと思います

Rest API は Supabase Edge Functions で実装しようと思っています。当初は aws Lambda function を使うつもりでしたが、以下の検討、設定、実装が必要となり、課金の対象にもなるため最初の段階では Supabase Edge Functions に丸投げしてみます

  • エンドポイント (API Gateway)
  • エンドポイントでの認証 (API Gateway authorizer)
  • 認証基盤 (Cognito)
  • 認証情報管理機能 (Rest API による)
  • データベースアクセス機能API (Lambda function)
  • Supabase Database へのアクセス情報管理 (Secrets Manager)

Supabase ローカル環境

データベースと連携する Edge Functions の開発を進めていきたいのですが、サーバサイドで開発する情報は見つかりませんでした。Supabase CLI というローカルの Supabase 開発環境で開発し、Cloud の Edge サーバに Deploy する手順はいくつか見つかりましたのでこれで進めてみようとしています

このページがローカルPC に Supabase 開発環境をつくる手順を示す公式ページです

ローカル開発環境が起動するとローカルの Supabase にアクセスできます

スクリーンショット 2025-04-09 18.21.14.png

  • ローカル Supabase の停止
supabase stop
  • ローカル Supabase の起動
supabase start

supabase start ローカルの Supabase を起動すると以下の Key が表示されます

         API URL: http://127.0.0.1:54321
     GraphQL URL: http://127.0.0.1:54321/graphql/v1
  S3 Storage URL: http://127.0.0.1:54321/storage/v1/s3
          DB URL: postgresql://postgres:postgres@127.0.0.1:54322/postgres
      Studio URL: http://127.0.0.1:54323
    Inbucket URL: http://127.0.0.1:54324
      JWT secret: super-secret-jwt-token-with-at-least-32-characters-long
        anon key: eyJhbGciOiJI......Tn_I0
service_role key: eyJhbGciOiJI......N81IU
   S3 Access Key: 625729a08b95......a23c
   S3 Secret Key: 850181e4652d......25907
       S3 Region: local

Local で Hello World

いろいろと調べてみたのですが、公式ページ以外の情報は古くて使える手順はほぼありませんでした。英語なのでちょっと大変ですが公式ページと公式からの動画を頼りに進めていきます

以下のページの手順に従うとローカル環境で Hello World できました

Developing Edge Functions locally

% pwd
/Users/m/Dev/supabase
%
% supabase init
Generate VS Code settings for Deno? [y/N] y
Generated VS Code settings in .vscode/settings.json. Please install the recommended extension!
Finished supabase init.
/Users/m/Dev/supabase
%
% ls -la
total 0
drwxr-xr-x  4 m  staff  128  4  9 21:33 .
drwxr-xr-x@ 8 m  staff  256  4  9 21:28 ..
drwxr-xr-x@ 4 m  staff  128  4  9 21:33 .vscode
drwxr-xr-x@ 5 m  staff  160  4  9 21:33 supabase
%

hello function をつくります

% pwd
/Users/m/Dev/supabase
% supabase functions new hello
Created new Function at supabase/functions/hello
%

supabase フォルダの下に functions フォルダができ、その下に hello フォルダがつくられます。そのなかにいくつかファイルが生成されており index.ts を編集していきます

スクリーンショット 2025-04-10 9.45.00.png

実行してみます

% curl --request POST 'http://localhost:54321/functions/v1/hello' \
  --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsIn......' \
  --header 'Content-Type: application/json' \
  --data '{ "name":"Functions" }'
{"message":"Hello Functions!"}%
%

Cloud で Hello World

以下のページに従います

Deploy to Production

% supabase login
Using workdir /Users/m/Dev/supabase
Hello from Supabase! Press Enter to open browser and login automatically.

Here is your login link in case browser did not open https://supabase.com/dashboard/cli/login?session_id=12c6714d-db0b-4939-be0f-dd4eff5882f5&token_name=cli_m@mopnoMac-mini.local_1744247012&public_key=0434e8adec......

Enter your verification code: edaa0e1d
Token cli_m@mopnoMac-mini.local_1744247012 created successfully.

You are now logged in. Happy coding!
% supabase projects list
Using workdir /Users/m/Dev/supabase
Cannot find project ref. Have you run supabase link?


   LINKED | ORG ID               | REFERENCE ID         | NAME       | REGION                 | CREATED AT (UTC)
  --------|----------------------|----------------------|------------|------------------------|---------------------
          | sflwwkgnxfcjqphbllcx | wpxn................ | bkiban_dev | Northeast Asia (Tokyo) | 2024-05-06 05:38:35
          | sflwwkgnxfcjqphbllcx | jkon................ | db.bkibam  | Northeast Asia (Tokyo) | 2025-04-07 22:08:11

%
% supabase link --project-ref jkon......

(snip)

% supabase functions deploy hello

(snip)

%
% curl --request POST 'https://jkon.......supabase.co/functions/v1/hello' \
  --header 'Authorization: Bearer eyJhbG......' \
  --header 'Content-Type: application/json' \
  --data '{ "name":"Functions" }'
{"message":"Hello Functions!"}%
%
% curl --request POST 'https://jkon.......supabase.co/functions/v1/hello' \
  --header 'Authorization: Bearer eyJhbG......' \
  --header 'Content-Type: application/json' \
  --data '{ "name":"db.bkiban!" }'
{"message":"Hello db.bkiban!!"}%
%

Edge Functions で CRUD

ここからはデータベースアクセス機能を提供する Rest API の開発を進めてみます

このコードがサンプルになりそうです

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?