快速入门: Svelte
介绍
这个例子提供了建立一个基本用户管理应用程序的步骤。它包括。
- MemFire Cloud Database:一个用于存储用户数据的Postgres数据库。
- MemFire Cloud Auth:用户可以用魔法链接登录(没有密码,只有电子邮件)。
- MemFire Cloud 存储:用户可以上传照片。
- 行级安全:数据受到保护,个人只能访问自己的数据。
- 即时APIs。当你创建你的数据库表时,API将自动生成。
在本指南结束时,你将拥有一个允许用户登录和更新一些基本档案细节的应用程序。
GitHub#
如果你在阅读指南时遇到困难,请参考此版本。
项目设置
在我们开始构建之前,我们要设置我们的数据库和API。这就像在Supabase中启动一个新项目一样简单 然后在数据库中创建一个 "模式"。
创建一个项目
- 进入MemFire Cloud。
- 点击 "新项目"。
- 输入你的项目细节。
- 等待新数据库的启动。
设置数据库模式
现在我们要设置数据库模式。我们可以使用SQL编辑器中的 "用户管理"的模板快速启动。 或者你可以直接复制/粘贴下面的SQL,然后自己运行它。
- 进入仪表版中的SQL编辑器页面。
- 点击 用户管理的模板。
- 点击 运行。
获取API密钥#
现在你已经创建了一些数据库表,你已经准备好使用自动生成的API插入数据。
我们只需要从API设置中获得URL和anon
密钥。
- 进入仪表板中的设置页面。
- 单击侧边栏中的API。
- 在这个页面上找到你的API
URL
、anon
和service_role
键。
构建应用程序
让我们从头开始构建Svelte应用程序。
初始化一个Svelte应用程序#
我们可以使用Vite Svelte TypeScript模板来初始化一个名为supabase-svelte
的应用程序。
npm create vite@latest supabase-svelte -- --template svelte-ts cd supabase-svelte npm install
然后让我们安装唯一的额外依赖:supabase-js
npm install @supabase/supabase-js
最后,我们要把环境变量保存在.env
中。
我们所需要的是API URL和你[早些时候]复制的anon
密钥(#get-theapi-keys)。
.envVITE_SUPABASE_URL=YOUR_SUPABASE_URL VITE_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
现在我们已经有了API凭证,让我们创建一个辅助文件来初始化Supabase客户端。这些变量将被暴露在 在浏览器上,这完全没有问题,因为我们的数据库已经启用了行级安全。
src/supabaseClient.ts1import { createClient } from '@supabase/supabase-js' 2 3const supabaseUrl = import.meta.env.VITE_SUPABASE_URL 4const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY 5 6export const supabase = createClient(supabaseUrl, supabaseAnonKey)
还有一个可选的步骤是更新CSS文件src/app.css
以使应用程序看起来漂亮。
你可以找到这个文件的全部内容这里。
设置一个登录组件
让我们建立一个Svelte组件来管理登录和注册。我们将使用Magic Links,这样用户就可以用他们的电子邮件登录,而无需使用密码。
src/lib/Auth.svelte1<script lang="ts"> 2 import { supabase } from 'src/supabaseClient' 3 4 let loading = false 5 let email = '' 6 7 const handleLogin = async () => { 8 try { 9 loading = true 10 const { error } = await supabase.auth.signInWithOtp({ email }) 11 if (error) throw error 12 alert('Check your email for login link!') 13 } catch (error) { 14 if (error instanceof Error) { 15 alert(error.message) 16 } 17 } finally { 18 loading = false 19 } 20 } 21</script> 22 23<div class="row flex-center flex"> 24 <div class="col-6 form-widget" aria-live="polite"> 25 <h1 class="header">Supabase + Svelte</h1> 26 <p class="description">Sign in via magic link with your email below</p> 27 <form class="form-widget" on:submit|preventDefault="{handleLogin}"> 28 <div> 29 <label for="email">Email</label> 30 <input 31 id="email" 32 class="inputField" 33 type="email" 34 placeholder="Your email" 35 bind:value="{email}" 36 /> 37 </div> 38 <div> 39 <button type="submit" class="button block" aria-live="polite" disabled="{loading}"> 40 <span>{loading ? 'Loading' : 'Send magic link'}</span> 41 </button> 42 </div> 43 </form> 44 </div> 45</div>
账号页面
在用户登录后,我们可以让他们编辑他们的个人资料细节和管理他们的账户。
让我们为其创建一个新的组件,名为Account.svelte
。
src/lib/Account.svelte1<script lang="ts"> 2 import { onMount } from "svelte"; 3 import type { AuthSession } from "@supabase/supabase-js"; 4 import { supabase } from "../supabaseClient"; 5 6 export let session: AuthSession; 7 8 let loading = false 9 let username: string | null = null 10 let website: string | null = null 11 let avatarUrl: string | null = null 12 13 onMount(() => { 14 getProfile() 15 }) 16 17 const getProfile = async () => { 18 try { 19 loading = true 20 const { user } = session 21 22 const { data, error, status } = await supabase 23 .from('profiles') 24 .select('username, website, avatar_url') 25 .eq('id', user.id) 26 .single() 27 28 if (error && status !== 406) throw error 29 30 if (data) { 31 username = data.username 32 website = data.website 33 avatarUrl = data.avatar_url 34 } 35 } catch (error) { 36 if (error instanceof Error) { 37 alert(error.message) 38 } 39 } finally { 40 loading = false 41 } 42 } 43 44 const updateProfile = async () => { 45 try { 46 loading = true 47 const { user } = session 48 49 const updates = { 50 id: user.id, 51 username, 52 website, 53 avatar_url: avatarUrl, 54 updated_at: new Date().toISOString(), 55 } 56 57 let { error } = await supabase.from('profiles').upsert(updates) 58 59 if (error) { 60 throw error 61 } 62 } catch (error) { 63 if (error instanceof Error) { 64 alert(error.message) 65 } 66 } finally { 67 loading = false 68 } 69 } 70</script> 71 72<form on:submit|preventDefault={updateProfile} class="form-widget"> 73 <div>Email: {session.user.email}</div> 74 <div> 75 <label for="username">Name</label> 76 <input id="username" type="text" bind:value={username} /> 77 </div> 78 <div> 79 <label for="website">Website</label> 80 <input id="website" type="text" bind:value={website} /> 81 </div> 82 <div> 83 <button type="submit" class="button primary block" disabled={loading}> 84 {loading ? 'Saving ...' : 'Update profile'} 85 </button> 86 </div> 87 <button type="button" class="button block" on:click={() => supabase.auth.signOut()}> 88 Sign Out 89 </button> 90</form>
启动
现在我们已经有了所有的组件,让我们来更新App.svelte
。
src/App.svelte1<script lang="ts"> 2 import { onMount } from 'svelte' 3 import { supabase } from './supabaseClient' 4 import type { AuthSession } from '@supabase/supabase-js' 5 import Account from './lib/Account.svelte' 6 import Auth from './lib/Auth.svelte' 7 8 let session: AuthSession 9 10 onMount(() => { 11 supabase.auth.getSession().then(({ data }) => { 12 session = data.session 13 }) 14 15 supabase.auth.onAuthStateChange((_event, _session) => { 16 session = _session 17 }) 18 }) 19</script> 20 21<div class="container" style="padding: 50px 0 100px 0"> 22 {#if !session} 23 <Auth /> 24 {:else} 25 <Account {session} /> 26 {/if} 27</div>
一旦完成,在终端窗口运行这个程序。
npm run dev
然后打开浏览器到localhost:5173,你应该看到完成的应用程序。
⚠️ 警告:Svelte使用Vite,默认端口为
5173
,Supabase使用端口3000
。要改变supabase的重定向端口,请进入。认证 > 设置
并将网站网址
改为localhost:5173
。
个人照片
每个Supabase项目都配置了存储,用于管理照片和视频等大文件。
创建一个上传小组件
让我们为用户创建一个头像,以便他们可以上传个人资料照片。我们可以从创建一个新的组件开始。
src/lib/Avatar.svelte1<script lang="ts"> 2 import { createEventDispatcher } from 'svelte' 3 import { supabase } from '../supabaseClient' 4 5 export let size: number 6 export let url: string 7 8 let avatarUrl: string = null 9 let uploading = false 10 let files: FileList 11 12 const dispatch = createEventDispatcher() 13 14 const downloadImage = async (path: string) => { 15 try { 16 const { data, error } = await supabase.storage.from('avatars').download(path) 17 18 if (error) { 19 throw error 20 } 21 22 const url = URL.createObjectURL(data) 23 avatarUrl = url 24 } catch (error) { 25 if (error instanceof Error) { 26 console.log('Error downloading image: ', error.message) 27 } 28 } 29 } 30 31 const uploadAvatar = async () => { 32 try { 33 uploading = true 34 35 if (!files || files.length === 0) { 36 throw new Error('You must select an image to upload.') 37 } 38 39 const file = files[0] 40 const fileExt = file.name.split('.').pop() 41 const filePath = `${Math.random()}.${fileExt}` 42 43 let { error } = await supabase.storage.from('avatars').upload(filePath, file) 44 45 if (error) { 46 throw error 47 } 48 49 url = filePath 50 dispatch('upload') 51 } catch (error) { 52 if (error instanceof Error) { 53 alert(error.message) 54 } 55 } finally { 56 uploading = false 57 } 58 } 59 60 $: if (url) downloadImage(url) 61</script> 62 63<div style="width: {size}px" aria-live="polite"> 64 {#if avatarUrl} <img src={avatarUrl} alt={avatarUrl ? 'Avatar' : 'No image'} class="avatar image" 65 style="height: {size}px, width: {size}px" /> {:else} 66 <div class="avatar no-image" style="height: {size}px, width: {size}px" /> 67 {/if} 68 <div style="width: {size}px"> 69 <label class="button primary block" for="single"> 70 {uploading ? 'Uploading ...' : 'Upload avatar'} 71 </label> 72 <span style="display:none"> 73 <input 74 type="file" 75 id="single" 76 accept="image/*" 77 bind:files 78 on:change="{uploadAvatar}" 79 disabled="{uploading}" 80 /> 81 </span> 82 </div> 83</div>
添加新的小组件
然后我们就可以把这个小部件添加到账号页面:
src/lib/Account.svelte1<script lang="ts"> 2 // Import the new component 3 import Avatar from './Avatar.svelte' 4</script> 5 6<form on:submit|preventDefault="{updateProfile}" class="form-widget"> 7 <!-- Add to body --> 8 <Avatar bind:url="{avatarUrl}" size="{150}" on:upload="{updateProfile}" /> 9 10 <!-- Other form elements --> 11</form>
下一步
在这个阶段,你已经有了一个功能完备的应用程序!
- 有问题吗?在此提问.
- 请登录MemFire Cloud