One Server

ドキュメント · リファレンス

プロジェクト API

プロジェクトごとに専用のアドレスがあり、supabase-js がそのまま接続します。下のルートはすべて、サーバーのルーターとテストで照合している一覧です。

アドレスとキー

プロジェクトのアドレスは https://<ref>.<zone> です。リクエストごとに apikey ヘッダー(または Authorization: Bearer)でキーを送ります。公開キー(publishable)はブラウザー用で、アクセスルール(RLS)に従います。シークレットキー(secret)はすべてのルールを迂回するため、サーバーでだけ使います。ログインしたユーザーのトークンを Authorization: Bearer で送ると、そのユーザーとして実行されます。キーはAPI キー画面にあります。

curl "https://abcdefghijklmnopqrst.end.blackall.ai/rest/v1/notes?select=id,title" \
  -H "apikey: <publishable key>"

supabase-js との互換性

パス、クエリ、ヘッダー、レスポンスの形が Supabase と同じなので、@supabase/supabase-js をそのまま使えます。変えるのはアドレスとキーだけです。各表の supabase-js 列は、そのルートを呼び出す関数です。

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

const supabase = createClient('https://abcdefghijklmnopqrst.end.blackall.ai', '<publishable key>')

データ(/rest/v1)

PostgREST と同じクエリ言語です。テーブル名がパスで、列名がクエリパラメーターです。すべてのリクエストは、呼び出した人のロールとアクセスルール(RLS)で実行されます。

メソッドパス内容supabase-js
GET/rest/v1/{table}行を読むfrom('t').select()
HEAD/rest/v1/{table}件数だけ読むselect('*', { head: true, count: 'exact' })
POST/rest/v1/{table}行を挿入(Prefer: resolution=… なら upsert)insert() / upsert()
PUT/rest/v1/{table}1 行を upsert
PATCH/rest/v1/{table}フィルターに合う行を更新update()
DELETE/rest/v1/{table}フィルターに合う行を削除delete()
GET/rest/v1/rpc/{function}関数の呼び出し(読み取り)rpc('f', args, { get: true })
POST/rest/v1/rpc/{function}関数の呼び出しrpc('f', args)
POST/graphql/v1GraphQL(pg_graphql、データベース → 拡張機能で有効にしたプロジェクトのみ、/rest/v1 と同じキーとアクセスルール)fetch / Apollo / urql

クエリパラメーター

名前説明
select取得する列。別名(label:title)、型変換(id::text)、JSON パス(data->>name)、外部キーでつながった行(*,author(name)、!inner)
order並び順。created_at.desc.nullslast、複数はカンマ区切り
limit取得する行数。最大 1000
offsetスキップする行数
on_conflictupsert で重複を判定する列
columns挿入する列を指定します
<column>列=演算子.値 のフィルター
or · andor=(a.eq.1,b.gt.2)、and=(…)、入れ子と not.or=(…)

フィルター演算子

次の演算子だけを受け付けます。知らない演算子は黙って無視せず、拒否します。先頭に not. を付けると否定になります。

演算子SQL
eq=
neq<>
gt>
gte>=
lt<
lte<=
likelike
ilikeilike
match~
imatch~*
cs@>
cd<@
ov&&
sl<<
sr>>
nxr&<
nxl&>
adj-|-
isdistinctis distinct from
isis null | not null | true | false | unknown
inin (…)
fts@@ to_tsquery()
plfts@@ plainto_tsquery()
phfts@@ phraseto_tsquery()
wfts@@ websearch_to_tsquery()

ヘッダー

名前説明
Preferreturn=minimal|representation|headers-only、count=exact|planned|estimated、resolution=merge-duplicates|ignore-duplicates、missing=default
AcceptAccept: application/vnd.pgrst.object+json は 1 行をオブジェクトとして返します(.single())。0 行や複数行なら 406
Accept-ProfileAccept-Profile と Content-Profile は public だけを受け付けます
Content-Range総数(count=)はレスポンスの Content-Range ヘッダーで返ります

関数の呼び出し(rpc)

/rest/v1/rpc/<関数> は、プロジェクトのスキーマにある SQL 関数を呼び出した人のロールで実行します。POST では本文が引数です。GET では関数の引数と同じ名前のクエリパラメーターが引数になり、残りは結果へのフィルターです。

const { data, error } = await supabase
  .from('notes')
  .select('id, title, author(name)')
  .eq('done', false)
  .order('created_at', { ascending: false })
  .range(0, 19)

await supabase.from('notes').insert({ title: 'Hello' }).select().single()
await supabase.rpc('search_notes', { query: 'hello' })
curl "https://abcdefghijklmnopqrst.end.blackall.ai/rest/v1/notes?select=id,title&done=eq.false&order=created_at.desc&limit=20" \
  -H "apikey: <publishable key>" \
  -H "Prefer: count=exact"

未対応のもの

  • 集計(count()、sum() など)とスプレッド(...rel(*))の select
  • public 以外のスキーマ
  • 一度に 1000 行を超える読み取り(limit は 1000 で切り詰められます)

認証(/auth/v1)

GoTrue と同じパスとレスポンスです。メールとパスワード、マジックリンク、匿名ログイン、ソーシャルログイン(PKCE を含む)、TOTP による二段階認証、シークレットキーによるユーザー管理に対応しています。

メソッドパス内容supabase-js
POST/auth/v1/signupサインアップ。メールとパスワードがなければ匿名ログインauth.signUp() / auth.signInAnonymously()
POST/auth/v1/token?grant_type=passwordメールとパスワードでログインauth.signInWithPassword()
POST/auth/v1/token?grant_type=refresh_tokenセッションの更新auth.refreshSession()
POST/auth/v1/token?grant_type=pkcePKCE のコードをセッションに交換auth.exchangeCodeForSession()
POST/auth/v1/token?grant_type=web3ウォレットが署名した Sign-In with Ethereum メッセージでログインauth.signInWithWeb3()
POST/auth/v1/token?grant_type=id_tokenGoogle や Apple の ID トークンでログインauth.signInWithIdToken()
POST/auth/v1/otpマジックリンクまたは SMS の認証コードを送信auth.signInWithOtp({ email } | { phone })
POST/auth/v1/verifyメールのトークンまたは SMS の認証コードを確認(sms、phone_change、signup、magiclink、recovery、email_change)auth.verifyOtp()
GET/auth/v1/verifyメールのリンクを開いたとき。登録したアドレスへセッションとともに戻します
POST/auth/v1/resend確認メールを再送auth.resend()
POST/auth/v1/recoverパスワード再設定メールauth.resetPasswordForEmail()
POST/auth/v1/reset再設定トークンで新しいパスワードを保存(戻り先がないときの画面が使用)
GET/auth/v1/userログイン中のユーザーと認証要素auth.getUser()
PUT/auth/v1/userメール、パスワード、メタデータの変更auth.updateUser()
GET/auth/v1/reauthenticateパスワード変更の前に確認コードをメールで送信auth.reauthenticate()
POST/auth/v1/logoutログアウト(scope=local|others|global)auth.signOut({ scope })
GET/auth/v1/settings有効なログイン方法
GET/auth/v1/authorizeソーシャルログインの開始auth.signInWithOAuth()
GET/auth/v1/callbackプロバイダーからの戻り先(Apple は POST)
DELETE/auth/v1/user/identities/{id}連携したソーシャルアカウントの解除auth.unlinkIdentity()
POST/auth/v1/factorsTOTP の登録auth.mfa.enroll({ factorType: 'totp' })
POST/auth/v1/factors/{id}/challengeチャレンジの開始auth.mfa.challenge()
POST/auth/v1/factors/{id}/verifyコードの確認(aal2 セッション)auth.mfa.verify()
DELETE/auth/v1/factors/{id}認証要素の削除auth.mfa.unenroll()
GET/auth/v1/admin/usersユーザー一覧(シークレットキー、per_page は最大 1000)auth.admin.listUsers({ page, perPage })
POST/auth/v1/admin/usersユーザーの作成(シークレットキー)auth.admin.createUser()
POST/auth/v1/ssoSAML SSO の開始(IdP のアドレスを返す、PKCE 可)auth.signInWithSSO({ domain } | { providerId })
GET/auth/v1/sso/saml/metadataこのプロジェクトの SP メタデータ(IdP に登録)
POST/auth/v1/sso/saml/acsIdP が SAML レスポンスを送る先(署名・宛先・再利用を確認)
GET/auth/v1/admin/sso/providersSSO プロバイダー一覧(シークレットキー)auth.admin.sso.listProviders()
POST/auth/v1/admin/sso/providersSSO プロバイダーの追加(metadata_url または metadata_xml、domains)auth.admin.sso.createProvider()
GET/auth/v1/admin/sso/providers/{id}SSO プロバイダー 1 つauth.admin.sso.getProvider()
PUT/auth/v1/admin/sso/providers/{id}SSO プロバイダーの変更(証明書の更新後はメタデータを再読込)auth.admin.sso.updateProvider()
DELETE/auth/v1/admin/sso/providers/{id}SSO プロバイダーの削除auth.admin.sso.deleteProvider()
GET/auth/v1/admin/users/{id}ユーザー 1 人(シークレットキー)auth.admin.getUserById()
PUT/auth/v1/admin/users/{id}ユーザーの更新(シークレットキー)auth.admin.updateUserById()
DELETE/auth/v1/admin/users/{id}ユーザーの削除(シークレットキー)auth.admin.deleteUser()
POST/auth/v1/invite招待メールを送信: プロジェクトのSMTPとテンプレートで(シークレットキー)auth.admin.inviteUserByEmail()
POST/auth/v1/admin/generate_linkメールのリンクを作成: signup、invite、magiclink、recovery(シークレットキー)auth.admin.generateLink()
GET/auth/v1/.well-known/jwks.jsonユーザートークンを検証する公開キー(ES256)
GET/auth/v1/.well-known/openid-configurationOpenID 設定(issuer、jwks_uri)
GET/auth/v1/.well-known/oauth-authorization-serverOAuth 認可サーバーのメタデータ(RFC 8414、MCP クライアント向け)
GET/.well-known/oauth-authorization-server/auth/v1同じメタデータ、RFC 8414 が定めるアドレス

トークンの発行(grant_type): password、refresh_token、pkce、id_token(Google と Apple の ID トークン)。

ソーシャルログインのプロバイダー: google, kakao, naver, apple, github, gitlab, microsoft, facebook, line, discord, x, bitbucket, figma, linkedin_oidc, notion, twitch, slack_oidc, spotify, zoom, azure, keycloak, workos, twitter, slack。設定はコンソールか、管理 API の set_auth_provider で行います。

await supabase.auth.signUp({ email: '[email protected]', password: 'a-long-passphrase' })
await supabase.auth.signInWithPassword({ email: '[email protected]', password: 'a-long-passphrase' })
await supabase.auth.signInWithOAuth({ provider: 'google', options: { redirectTo: 'https://app.example.com' } })
const { data: { user } } = await supabase.auth.getUser()

未対応のもの

  • TOTP 以外の二段階認証(WebAuthn、電話)
  • 再認証(reauthenticate)と ID の連携(linkIdentity)。連携の解除だけに対応しています
  • auth.admin.inviteUserByEmail。代わりに generateLink({ type: 'invite' }) を使います

ストレージ(/storage/v1)

Supabase Storage と同じパスです。ファイルの記録はプロジェクトのテーブルにあるため、誰がファイルを読み書きできるかは、そのテーブルのアクセスルールが決めます。

ファイル 1 つは既定で 50MB まで受け付けます(バケットごとにより低く設定できます)。署名付き URL は最長 7 日、アップロード用の署名付き URL は 2 時間有効です。バケット名には小文字、数字、.、_、- を使います。

メソッドパス内容supabase-js
GET/storage/v1/bucketバケット一覧storage.listBuckets()
POST/storage/v1/bucketバケットの作成(public、file_size_limit、allowed_mime_types)storage.createBucket()
GET/storage/v1/bucket/{bucket}バケット 1 つstorage.getBucket()
PUT/storage/v1/bucket/{bucket}バケットの設定変更storage.updateBucket()
DELETE/storage/v1/bucket/{bucket}空のバケットを削除storage.deleteBucket()
POST/storage/v1/bucket/{bucket}/emptyバケットを空にするstorage.emptyBucket()
POST/storage/v1/object/{bucket}/{path}アップロード(x-upsert)from('b').upload()
PUT/storage/v1/object/{bucket}/{path}上書きfrom('b').update()
GET/storage/v1/object/{bucket}/{path}ダウンロード(呼び出した人の権限、HEAD も可)from('b').download()
DELETE/storage/v1/object/{bucket}/{path}ファイル 1 つを削除
DELETE/storage/v1/object/{bucket}複数ファイルの削除(prefixes、最大 1000)from('b').remove([paths])
GET/storage/v1/object/public/{bucket}/{path}公開バケットのファイルfrom('b').getPublicUrl()
GET/storage/v1/object/authenticated/{bucket}/{path}ログインユーザーとしてダウンロード
POST/storage/v1/object/list/{bucket}フォルダー 1 階層の一覧(limit は最大 1000)from('b').list(prefix, { limit, offset, sortBy, search })
GET/storage/v1/object/info/{bucket}/{path}ファイルの情報from('b').info()
POST/storage/v1/object/move移動from('b').move()
POST/storage/v1/object/copyコピーfrom('b').copy()
POST/storage/v1/object/sign/{bucket}/{path}署名付き URL の作成from('b').createSignedUrl(path, expiresIn, { transform })
POST/storage/v1/object/sign/{bucket}複数の署名付き URL(最大 100)from('b').createSignedUrls(paths, expiresIn)
GET/storage/v1/object/sign/{bucket}/{path}署名付き URL でダウンロード(?token=)
POST/storage/v1/object/upload/sign/{bucket}/{path}アップロード用の署名付き URL の作成from('b').createSignedUploadUrl()
PUT/storage/v1/object/upload/sign/{bucket}/{path}署名付き URL でアップロード(?token=)from('b').uploadToSignedUrl()
GET/storage/v1/render/image/public/{bucket}/{path}公開画像の変換from('b').getPublicUrl(path, { transform })
GET/storage/v1/render/image/authenticated/{bucket}/{path}ログインユーザーとして画像を変換from('b').download(path, { transform })
GET/storage/v1/render/image/sign/{bucket}/{path}署名付き URL で画像を変換
POST/storage/v1/upload/resumable再開可能なアップロードの開始(TUS 1.0、Upload-Length、Upload-Metadata に bucketName と objectName)tus-js-client / Uppy
PATCH/storage/v1/upload/resumable/{id}続きを送る(Upload-Offset、application/offset+octet-stream)
HEAD/storage/v1/upload/resumable/{id}これまでに受け取ったサイズの確認
DELETE/storage/v1/upload/resumable/{id}再開可能なアップロードの取り消し
GET/storage/v1/s3/{bucket}/{key}S3 互換 API(コンソールで作った S3 キーによる SigV4 署名、path-style、既定のリージョン ap-northeast-2)AWS SDK / rclone

アップロードは、本文そのまま(Content-Type がファイルの形式)か multipart/form-data で送ります。POST は同じ名前があれば拒否し(x-upsert: true なら上書き)、PUT は上書きします。

画像の変換

/render/image/{public|authenticated|sign}/<バケット>/<パス> にクエリで指定します。画像変換サーバーがない環境では元の画像をそのまま返し、x-blackwall-transform ヘッダーで知らせます。

名前説明
width1〜2500 ピクセル
height1〜2500 ピクセル
resizecover(既定)、contain、fill
quality20〜100(既定 80)
formatorigin(既定)、webp、avif、png、jpeg
const bucket = supabase.storage.from('avatars')
await bucket.upload('me.png', file, { upsert: true })
const { data } = bucket.getPublicUrl('me.png', { transform: { width: 200, height: 200 } })
const { data: signed } = await bucket.createSignedUrl('me.png', 3600)

未対応のもの

  • ファイルごとのキャッシュ設定(cacheControl オプション)は保存しません

リアルタイム(/realtime/v1)

wss://<ref>.<zone>/realtime/v1/websocket?apikey=<キー>&vsn=1.0.0 は Phoenix チャネル(supabase-js が使うもの)です。vsn=2.0.0 は配列形式のメッセージとバイナリのブロードキャストです。/realtime/v1(websocket なし)は同じ機能のシンプルな JSON プロトコルです。

チャネルのトピックは realtime:<名前> です。同じ名前に参加したクライアント同士で、ブロードキャストとプレゼンスを共有します。

クライアントが送るイベント

イベント説明
phx_joinチャネルに参加します。config.broadcast.self/ack、config.presence.key、config.postgres_changes、access_token
phx_leaveチャネルから退出します
heartbeatphoenix トピックに送ると、サーバーがすぐに phx_reply で応答します。接続が生きているかの確認に使います
access_tokenログインしたユーザーの新しいトークンに切り替えます
broadcastチャネルのほかの参加者に送ります(データベースを経由しません)
presencetrack で自分の状態を知らせ、untrack で消します

サーバーが送るイベント

イベント説明
phx_reply送ったメッセージへの返答(ok または error)
presence_state参加したときにすでにいる人たち
presence_diff参加した人と退出した人
broadcastチャネルのほかの参加者に送ります(データベースを経由しません)
postgres_changesテーブルの行が変わった(INSERT、UPDATE、DELETE)
phx_closeチャネルが閉じた
systemトークン切り替えの失敗などの通知

postgres_changes は、コンソールか set_realtime でリアルタイムを有効にしたテーブルからだけ届きます。各行は受け取る人のアクセスルールで確認してから送られます。フィルターは 列=eq|neq|lt|lte|gt|gte|in.値 です。

supabase
  .channel('room-1')
  .on('broadcast', { event: 'cursor' }, ({ payload }) => console.log(payload))
  .on('presence', { event: 'sync' }, () => console.log('online'))
  .on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'notes', filter: 'done=eq.false' }, (change) => console.log(change.new))
  .subscribe()
wss://abcdefghijklmnopqrst.end.blackall.ai/realtime/v1/websocket?apikey=<publishable key>&vsn=1.0.0

{"topic":"realtime:room-1","event":"phx_join","payload":{"config":{"broadcast":{"self":false},"presence":{"key":"me"},"postgres_changes":[{"event":"*","schema":"public","table":"notes"}]}},"ref":"1","join_ref":"1"}
{"topic":"realtime:room-1","event":"broadcast","payload":{"type":"broadcast","event":"cursor","payload":{"x":1}},"ref":"2","join_ref":"1"}
{"topic":"phoenix","event":"heartbeat","payload":{},"ref":"3"}

未対応のもの

  • 非公開チャネル(config.private: true)
  • UPDATE の変更前の値(old は空です)

関数(/functions/v1)

/functions/v1/<名前> はデプロイした関数を呼び出します。すべての HTTP メソッドをそのまま渡し、本文は 2MB までです。非公開(既定)の関数には、このプロジェクトのキーが必要です: 公開キー(anon)、ログインしたユーザーのトークン(authenticated)、シークレットキー(service_role)。公開としてデプロイした関数はキーなしで呼び出せます。関数にはトークンではなく、検証済みのクレームが渡されます。名前は小文字で始まり、小文字、数字、- を使います。

メソッドパス内容supabase-js
*/functions/v1/{name}関数の呼び出し(すべてのメソッド)functions.invoke('name', { body })
const { data, error } = await supabase.functions.invoke('hello', { body: { name: 'world' } })

curl -X POST "https://abcdefghijklmnopqrst.end.blackall.ai/functions/v1/hello" \
  -H "Authorization: Bearer <user token or secret key>" \
  -H "content-type: application/json" \
  -d '{"name":"world"}'

そのほかのルート

メソッドパス内容supabase-js
POST/kv/v1KV コマンド(Upstash 形式 ["SET", "k", "v"]、/kv/v1/pipeline、/kv/v1/keys、/kv/v1/usage も、シークレットキー)@upstash/redis
*/kv/v1/{key}KV キー 1 つの読み書き・削除(GET・PUT・DELETE、/incr、/expire)
GET/config/v1エッジ設定の全体(シークレットキー)@vercel/edge-config
GET/config/v1/{key}エッジ設定の値 1 つget('key')
POST/ai/v1/{endpoint}AI ゲートウェイ、OpenAI 互換(chat/completions、embeddings、images/generations、audio/speech、audio/transcriptions、models)openai SDK (baseURL)
GET/healthプロジェクトが応答するか({ project, ok })
GET/realtime/v1/websocketPhoenix チャネル(websocket)channel('name').subscribe()
GET/realtime/v1シンプルな JSON プロトコル(websocket)

エラー

エラーは、ルートごとに Supabase のクライアントが読む形のままです。データは PostgREST と同じく code(PGRST…)、message、details、hint、認証は GoTrue と同じく code、error_code、msg(トークン発行では OAuth の error、error_description も)、ストレージは storage-js と同じく statusCode、error、message です。メッセージは Accept-Language(韓国語、英語、日本語)に従います。

{ "code": "PGRST116", "message": "…", "hint": null, "details": "…" }