LoginSignup
5
2

More than 1 year has passed since last update.

知ってはいたものの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アカウントからも作成できます。

作成後、新規プロジェクト作成から以下の画面に遷移します。

supabase-create-new-project.png

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する準備を整えます。

lib/spabaseClient.ts
import { createClient } from '@supabase/supabase-js'

const supabaseUrl = process.env.SUPABASE_URL
const supabaseKey = process.env.SUPABASE_KEY
export const supabaseClient = createClient(supabaseUrl, supabaseKey)
global.ts
declare namespace NodeJS {
  interface ProcessEnv {
    readonly SUPABASE_URL: string;
    readonly SUPABASE_KEY: string;
  }
}

RLSを無効にしたテーブルを作成し、なんか適当なスキーマを設定。
その後、Nextjs側に最低限処理を実行して結果を見れるだけのcomponentを作成し、api側でなんか適当な処理を実行します。

api/supabase-database.ts
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;
components/Supabase.tsx
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-insert.png

supabase側の管理画面で見ても、無事に反映されていることがわかります。

supabase-inserted.png

ファイルをアップロードする

バケットを作成し、ファイルをアップロードします。
バケットの作成はJS側で行うことも可能ですが、今回はダッシュボード側で作成してそこにJSからアップロードするという手法をとります。

ダッシュボード側でselectとinsertを許可したPolisyを設定後、APIを追加してそこにファイルを投げられるようにフロントを修正

api/supabase-strage.ts
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;
components/Supabase.tsx
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;

試しに↑のスクショを投げましたが、無事に投げれているみたいですね。

image.png

おわりに

というわけでとりあえずsupabaseを触ってみました。
firebaseの無料枠が強いので無料で使う範囲だとsupabaseの方が弱そうですが、オープンソースなのでローカルに構築できるというのは個人的にすごく魅力的に見えます。
時間があったら認証系とかもっといろいろ触ってみたいですね。

5
2
2

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
5
2