はじめに
Baas(Backend As A Service)で何かサービスを構築してみたいと思い、
firebaseを触ってみようと思ったところ、Firebase の代替と謳われているsupabaseを発見。
こちらの方が面白そうだと感じたため、構築してみることにしました。
今回はプロフィール画面を作成した際に、supabaseのstorage機能について触れたのでメモ。
使ったもの
-
Vite + SvelteKit + Typescript
- @supabase/supabase-js
- Flowbite-svelte
- tailwind css
-
supabase
- auth
- db
- storage
完成品
マイページ(アイコンアップロード前)
アップロード画面
マイページ(アイコンアップロード後)
実装方法
- Supabase管理画面より、
storage
>bucket
を作成する - Supabase管理画面から、
profiles
テーブルを作成する - フロント側を整備する
1. Supabase管理画面より、storage
> bucket
を作成する
アイコンを保存するためのバケットを作成していきます。
- サイドメニューから、
Storage
を選択する - 画面左上の
New bucket
を選択し、任意のタイトルで保存をする。
今回は公式のチュートリアル通りにavatars
としました。
2. Supabase管理画面から、profiles
テーブルを作成する
ユーザーに対してアイコンのURLを紐づけるためにprofiles
テーブルを作成します。
- サイドメニューから、
SQL Editor
を選択する - 画面左上の
Welcome
を選択。 -
Quick start
の欄のUser Management Starter
を選び、実行する
(RUN or Ctrl + Enter)
3. フロント側を整備する
1と2でsupabase側の用意は完了なので、今度はフロント側を整備していく。
まずはファイルのアップロードフォームを作成する。
<script lang="ts">
import { Label } from 'flowbite-svelte'
let files: FileList
</script>
<div class="flex">
<Label class="w-1/5 pb-2">アイコンアップロード</Label>
<input
class="w-4/5 text-sm text-gray-900 border border-gray-300 rounded-lg cursor-pointer bg-gray-50 dark:text-gray-400 focus:outline-none dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400"
type="file"
accept="image/*"
bind:value={files}
/>
</div>
こんな感じになりました。
次に、ファイルがアップロードされたら着火するイベントを作成する
<script lang="ts">
let files: FileList
const uploadIcon = async () => {
if (!files || files.length === 0) {
return
}
const file = files[0]
const fileExt = file.name.split('.').pop()
const filePath = `${session.user.id}/${Math.random()}.${fileExt}`
await supabase.storage.from('avatars').upload(filePath, file)
}
</script>
<input
class="w-4/5 block w-full text-sm text-gray-900 border border-gray-300 rounded-lg cursor-pointer bg-gray-50 dark:text-gray-400 focus:outline-none dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400"
type="file"
accept="image/*"
bind:files
on:change={uploadIcon} // 追加
/>
session
には現在のsession情報を格納しています。
avatar storage
にユーザーごとのファイルを作成し、そこに画像を格納するようにしています。
次に、profiles
テーブルに画像URLを保存します。
<script lang="ts">
import { Label, Button } from 'flowbite-svelte'
let files: FileList
let url: string // 追加
const updateUser = async () => {
update_data = {
id: $session.user.id,
avatar_url: url
}
await supabase
.from('profiles')
.update(update_data)
}
const uploadIcon = async () => {
if (!files || files.length === 0) {
return
}
const file = files[0]
const fileExt = file.name.split('.').pop()
const filePath = `${session.user.id}/${Math.random()}.${fileExt}`
await supabase.storage.from('avatars').upload(filePath, file)
url = filePath // 追加
}
</script>
<form class="w-full" on:submit|preventDefault="{updateUser}">
// ------------------------------
// 先ほどのファイルフォーム
// ------------------------------
<Button class="mt-10" type="submit" color="yellow" disabled={updating}>プロフィールを変更する</Button>
</form>
先ほど作ったuploadIcon
メソッドの最後にfilePath
を変数に格納します。
また、form
タグのサブミットイベントにupdateUser
を定義し、サブミットボタンを押されたら着火するようにしました。
ニックネームや出身地など、一緒のフォームで入力できるようにすればプロフィール画面が作れますね