知ってはいたもののfirebaseでよくねと思っていましたが、最近触らずに何か言うのもどうなのと思いsupabaseを触ってみようと思いいたりました。
supabaseとは
Supabase is an open source Firebase alternative. Start your project with a Postgres database, Authentication, instant APIs, Edge Functions, Realtime subscriptions, and Storage.
--- supabase公式より
supabaseとは、firebaseに代わるBaaSです。
もっとも、firebaseとは異なりRDBだったりと細かな違いがあり、firebaseとまったく同じように使えるサービスというわけではないです。
オープンソースであるため、ローカル上に環境を作成することも可能です。
使い始める
プロジェクト作成
email/passの他、Githubアカウントからも作成できます。
作成後、新規プロジェクト作成から以下の画面に遷移します。
Regionについては、近くですと日本のTokyoと韓国のsoulがありました。
Create new projectを選択すると、プロビジョニングが始まります。
1分かからずに作成されたので、待つには待ちますがそれほど待たされることはないです。
DBからデータを取得する
クライアントライブラリとして、JS(公式サポート)とDart(コミュニティサポート)のライブラリが用意されています。
(アルファ版ですがPythonもあります)
試しに適当なデータを突っ込んでみます。
create-next-app --ts
で適当なプロジェクトを作成し、ライブラリをインストール
pnpm install @supabase/supabase-js
lib/supabaseClient.ts
を作成し、supabaseとfetchする準備を整えます。
import { createClient } from '@supabase/supabase-js'
const supabaseUrl = process.env.SUPABASE_URL
const supabaseKey = process.env.SUPABASE_KEY
export const supabaseClient = createClient(supabaseUrl, supabaseKey)
declare namespace NodeJS {
interface ProcessEnv {
readonly SUPABASE_URL: string;
readonly SUPABASE_KEY: string;
}
}
RLSを無効にしたテーブルを作成し、なんか適当なスキーマを設定。
その後、Nextjs側に最低限処理を実行して結果を見れるだけのcomponentを作成し、api側でなんか適当な処理を実行します。
import type { NextApiRequest, NextApiResponse } from "next";
import { supabaseClient } from "libs/supabaseClient";
export const handler = async (
req: NextApiRequest,
res: NextApiResponse
) => {
const insertSomethingIntoSupabase = async () => {
const insert = await supabaseClient
.from("foo")
.insert({ title: "吾輩は猫である", auther: "夏目漱石" });
const { data, error } = await supabaseClient.from("foo").select();
return { data, inaseError: insert.error, selectError: error };
};
const resSupabase = await insertSomethingIntoSupabase();
res.status(200).json(resSupabase);
};
export default handler;
import { FC, useState } from "react";
export const Supabase: FC = () => {
const [resSupabaseBody, setResSupabaseBody] = useState("");
const fetchSupabase = async () => {
const res = await fetch("/api/supabase-database");
setResSupabaseBody(JSON.stringify(await res.json(), null, 2));
};
return (
<div>
<button onClick={() => fetchSupabase()}>Run Fetch</button>
<p style={{ whiteSpace: "pre-wrap" }}>{resSupabaseBody}</p>
</div>
);
};
export default Supabase;
その結果がこちら。
supabase側の管理画面で見ても、無事に反映されていることがわかります。
ファイルをアップロードする
バケットを作成し、ファイルをアップロードします。
バケットの作成はJS側で行うことも可能ですが、今回はダッシュボード側で作成してそこにJSからアップロードするという手法をとります。
ダッシュボード側でselectとinsertを許可したPolisyを設定後、APIを追加してそこにファイルを投げられるようにフロントを修正
import type { NextApiRequest, NextApiResponse } from "next";
import { supabaseClient } from "libs/supabaseClient";
import multer from "multer";
export const config = {
api: {
bodyParser: false,
},
};
const upload = multer({
storage: multer.memoryStorage(),
});
export const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const body: any = await new Promise((resolve, reject) => {
upload.single("file")(req, res, (err: any) => {
if (err) return reject(err);
resolve({ file: req.file, path: req.body.path });
});
});
const { data, error } = await supabaseClient.storage
.from("bar")
.upload(
`sample/${body.file.originalname}`,
new Blob([body.file.buffer], { type: body.file.mimetype })
);
console.log(error);
console.log(data);
let status = 200;
if (error) status = 500;
return res.status(status).send("");
};
export default handler;
import { ChangeEvent, FC, useState } from "react";
export const Supabase: FC = () => {
const [resSupabaseBody, setResSupabaseBody] = useState("");
const [imageFile, setImageFile] = useState<Blob | undefined>(undefined);
const fetchSupabase = async () => {
const res = await fetch("/api/supabase-database");
setResSupabaseBody(JSON.stringify(await res.json(), null, 2));
};
const onChangeFileInput = (e: ChangeEvent<HTMLInputElement>) => {
if (!e.target.files) return;
if (e.target.files[0]) setImageFile(e.target.files[0]);
};
const postFile = async () => {
if (!imageFile) return;
const formData = new FormData();
formData.append('file', imageFile);
const res = await fetch("/api/supabase-strage", {method: "POST", body: formData});
};
return (
<div>
<button onClick={() => fetchSupabase()}>Run Fetch</button>
<p style={{ whiteSpace: "pre-wrap" }}>{resSupabaseBody}</p>
<input
type="file"
id="input"
accept="image/*"
onChange={(e) => onChangeFileInput(e)}
/>
<button onClick={() => postFile()}>Post File</button>
</div>
);
};
export default Supabase;
試しに↑のスクショを投げましたが、無事に投げれているみたいですね。
おわりに
というわけでとりあえずsupabaseを触ってみました。
firebaseの無料枠が強いので無料で使う範囲だとsupabaseの方が弱そうですが、オープンソースなのでローカルに構築できるというのは個人的にすごく魅力的に見えます。
時間があったら認証系とかもっといろいろ触ってみたいですね。