💭

[Flutter]Hono✖️Supabaseでアプリを作る- delete -

に公開1

ぽちぽちのつどいGW Advent(?) Calendar2日目の記事です!
今日は少し前から書いてるHonoとSupabaseをつかった開発の備忘録の最後deleteです。

HonoとSupabaseでアプリを作った話

触れる話
CRUD操作とFlutterの実装
触れない話
デプロイの仕方
Honoの基本

Honoの基本とGet/postについてはこちら
https://zenn.dev/tolto/articles/a1cb22bcdb5eac
https://zenn.dev/pochipochitudoi/articles/25013408f6c9ca

Honoの実装

get編やpost編をみていただいたかたならわかるかと思いますがこれもまたシンプルに記述できます。
今回も私はGet/post同様にSupabaseにデータを追加する専用の関数を作成して成功したかどうかをjson型で返すようにしました。
Honoではdelete型はapp.deleteで実装します。非常にシンプルですね。
今回は名前を削除する関数を作成します。
今回はusersテーブルの該当するnameを探して削除します。
今回はpost同様関数の中でテーブルなどを指定して書いてますがget編同様に変数化しても同じことができると思います。

export async function deleteUser(
  SUPABASE_URL: string,
  SUPABASE_API_KEY: string,
  name: String
) {
  const supabase = createClient(SUPABASE_URL, SUPABASE_API_KEY);

  try {
    const { data, error } = await supabase
      .from('users')
      .delete()
      .eq('username', name);

    if (error) {
      console.error('Supabase error:', error);
      throw error;
    }

    return '成功しました';
  } catch (error) {
    console.error('Error fetching user names:', error);
    return [];
  }
}

流れとしては、fromでテーブル指定 → deleteで削除指示 → eqで条件指定するだけ。
改めて見ると、Supabase用のTypeScriptライブラリがとても直感的に作られていることがわかりますねありがたい🙏
次はHonoの実装です。
SupabasenのURLなどの環境変数の管理もHonoは簡単に実行できてHonoは.dev.varsでdotenvなどを仲介せずにc.で環境変数にアクセスできます。
app.deleteでdelete型のHTTP通信が行えるようになるので次のように記述します。

index.ts
app.delete('/deleteUser', async (c) => {
  const SUPABASE_URL = (c.env as { SUPABASE_URL: string }).SUPABASE_URL;
  const SUPABASE_API_KEY = (c.env as { SUPABASE_API_KEY: string })
    .SUPABASE_API_KEY;
  const { name } = await c.req.json();
  const result = await deleteUser(SUPABASE_URL, SUPABASE_API_KEY, name);
  return c.json({ result: result });
});

最初にURLとAPI_KEYを環境変数から取得して必要な情報を受け取って最初に作った関数に渡して戻り値を変数に定義してjsonで返すと完成です。

Flutter

Flutterでは作成したAPIに名前を渡して削除する処理を記述します。
実際にはdioやRiverpodを使用するかと思いますが今回の趣旨と外れるのでget/post同様今回もフロントから直接通信させます。

削除処理
String res = "";
void deleteData(name) async {
    final response = await http.post(
      Uri.parse("${api}/deleteUser"),
      headers: {'Content-Type': 'application/json'},
      body: json.encode({'name': name}),
    );
    if (response.statusCode == 200) {
      setState(() {
        res = json.decode(response.body)['result'];
      });
    } else {
      setState(() {
        res = 'Failed to load data';
      });
    }
  }

deleteData関数に引数として今回はnameを渡してjsonで通信してさっき作った削除リクエストをAPI側で行ってその結果がresultとしてdecodeすることで見ることができるというものです。
あとはfetchData(Honoの処理は過去記事を参考にしてください)などの関数と組み合わせて以下のようなListViewを作成して端にdeleteアイコンをつけてあげると削除できるようになると思います。

fetch処理
  String msg = "";
  List<dynamic> names = [];

  void fetchData() async {
    final response = await http.get(Uri.parse(api));
    final responseDB = await http.get(Uri.parse('${api}db/read'));
    if (response.statusCode == 200) {
      setState(() {
        names = json.decode(utf8.decode(responseDB.bodyBytes))['users'];
      });
    } else {
      setState(() {
        msg = 'Failed to load data';
      });
    }
  }

UI
ListView.builder(
  itemCount: names.length,
  itemBuilder: (context, index) {
    return Column(
      children: [
        ListTile(
          leading: const Icon(Icons.person),
          title: Text(names[index]),
          /// 今回部分
          trailing: IconButton(
            icon: const Icon(Icons.delete),
            onPressed: () async {
              deleteData(names[index]);
              fetchData();
            },
          ),
        ),
        const Divider(height: 0),
      ],
    );
  },
)

あとは帰ってきたjsonデータに含まれる成功かどうかというデータをsnackbarなどでフロントに表示してやれば完成です。

まとめ

Honoは意外と簡単に実装できることがわかったかと思います。delete型は以上です。
FluterというよりはSupabaseとHonoという実装の話をメインにしました。FlutterでのAPI実装については丁寧な記事が他の方が書かれていると思うのでそちらを参照してください(笑)。
これらを踏まえてFlutterの実装ではテスト実装を想定しています。本番環境ではriverpod等を使用することが多いと思いますのでそちらの実装に合わせてみると良いと思います。
JSONのencode/decodefreezedなどで型安全にする あたりを意識するとさらにレベルアップできるはずです。
一応これで一通りupdate以外は終わったかなと思いますupdateはまたいつかのAdvent(?) Calendarで書こうと思います!

ぽちぽちのつどい

Discussion