From 796416a36e545210a8b74389c892a4cb17341392 Mon Sep 17 00:00:00 2001 From: thorwebdev Date: Tue, 7 Jan 2025 12:26:17 +0800 Subject: [PATCH 1/2] feat: add Supabase Auth example. --- README.md | 1 + supabase-auth/.env.example | 3 + supabase-auth/.gitignore | 1 + supabase-auth/README.md | 27 +++++ supabase-auth/package.json | 20 +++ supabase-auth/src/client.tsx | 114 ++++++++++++++++++ supabase-auth/src/index.tsx | 64 ++++++++++ .../src/middleware/auth.middleware.ts | 56 +++++++++ supabase-auth/tsconfig.json | 12 ++ supabase-auth/vite.config.ts | 33 +++++ 10 files changed, 331 insertions(+) create mode 100644 supabase-auth/.env.example create mode 100644 supabase-auth/.gitignore create mode 100644 supabase-auth/README.md create mode 100644 supabase-auth/package.json create mode 100644 supabase-auth/src/client.tsx create mode 100644 supabase-auth/src/index.tsx create mode 100644 supabase-auth/src/middleware/auth.middleware.ts create mode 100644 supabase-auth/tsconfig.json create mode 100644 supabase-auth/vite.config.ts diff --git a/README.md b/README.md index 3d0ede5..0525a6c 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ This repository contains examples that use [Hono](https://hono.dev). - [deno](./deno/) - Deno example - [bun](./bun/) - Bun example - [pages-stack](./pages-stack/) - Zod + Zod Validator + `hc` + React on Cloudflare Pages +- [Supabase](./supabase-auth/) - Example of using Supabase Auth and database on both server and client side (built on [hono-vite-jsx](./hono-vite-jsx/)). ## Running Examples diff --git a/supabase-auth/.env.example b/supabase-auth/.env.example new file mode 100644 index 0000000..671edd5 --- /dev/null +++ b/supabase-auth/.env.example @@ -0,0 +1,3 @@ +# Get your keys at https://supabase.com/dashboard/project/_/settings/api +VITE_SUPABASE_URL=your_supabase_url +VITE_SUPABASE_ANON_KEY=your_supabase_anon_key \ No newline at end of file diff --git a/supabase-auth/.gitignore b/supabase-auth/.gitignore new file mode 100644 index 0000000..2eea525 --- /dev/null +++ b/supabase-auth/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/supabase-auth/README.md b/supabase-auth/README.md new file mode 100644 index 0000000..2286f07 --- /dev/null +++ b/supabase-auth/README.md @@ -0,0 +1,27 @@ +# Hono Supabase Auth Example! + +Based on the Hono/JSX + Vite example by [@MathurAditya724](https://github.com/MathurAditya724) \o/ + +This example shows how to use Supabase Auth both on the client and server side with Hono. + +## Setup + +- Run `npm install` to install the dependencies. +- Run `cp .env.example .env`. +- Set the required environment vairables in your `.env` file. + +## Commands + +Run the `vite` dev server + +```bash +npm run dev +``` + +Building + +```bash +npm run build +``` + +This project is configured to use `node` runtime, you can change it to your desired runtime in the `vite.config.js` file. We are using [@hono/vite-build](https://www.npmjs.com/package/@hono/vite-build) package for building the project and [@hono/vite-dev-server](https://www.npmjs.com/package/@hono/vite-dev-server) for running the dev server. diff --git a/supabase-auth/package.json b/supabase-auth/package.json new file mode 100644 index 0000000..a4673fa --- /dev/null +++ b/supabase-auth/package.json @@ -0,0 +1,20 @@ +{ + "name": "hono-vite-jsx", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build --mode client && vite build" + }, + "dependencies": { + "@hono/node-server": "^1.12.2", + "@supabase/ssr": "^0.5.2", + "@supabase/supabase-js": "^2.47.10", + "hono": "^4.5.10" + }, + "devDependencies": { + "@hono/vite-build": "^1.1.0", + "@hono/vite-dev-server": "^0.17.0", + "@types/node": "^20.11.17", + "vite": "^5.4.2" + } +} diff --git a/supabase-auth/src/client.tsx b/supabase-auth/src/client.tsx new file mode 100644 index 0000000..1c2a0d9 --- /dev/null +++ b/supabase-auth/src/client.tsx @@ -0,0 +1,114 @@ +import { createBrowserClient } from '@supabase/ssr'; +import { hc } from 'hono/client'; +import { useEffect, useState } from 'hono/jsx'; +import { render } from 'hono/jsx/dom'; +import type { AppType } from '.'; + +const client = hc('/'); + +const supabase = createBrowserClient( + import.meta.env.VITE_SUPABASE_URL!, + import.meta.env.VITE_SUPABASE_ANON_KEY! +); + +function App() { + const [user, setUser] = useState(null); + // Check client-side if user is logged in: + useEffect(() => { + supabase.auth.onAuthStateChange((event, session) => { + console.log('Auth event:', event); + if (event === 'SIGNED_OUT') { + setUser(null); + } else { + setUser(session?.user!); + } + }); + }, []); + + return ( + <> +

Hono Supabase Auth Example!

+

Sign in

+ {!user ? ( + + ) : ( + + )} +

Example of API fetch()

+ +

Example of database read

+

+ Note that only authenticated users are able to read from the database! +

+ Get countries + + ); +} + +function SignIn() { + return ( + <> +

+ Ready about and enable{' '} + + anonymous signins here! + +

+ + + ); +} + +const UserDetailsButton = () => { + const [response, setResponse] = useState(null); + + const handleClick = async () => { + const response = await client.api.user.$get(); + const data = await response.json(); + const headers = Array.from(response.headers.entries()).reduce< + Record + >((acc, [key, value]) => { + acc[key] = value; + return acc; + }, {}); + const fullResponse = { + url: response.url, + status: response.status, + headers, + body: data, + }; + setResponse(JSON.stringify(fullResponse, null, 2)); + }; + + return ( +
+ + {response &&
{response}
} +
+ ); +}; + +const root = document.getElementById('root')!; +render(, root); diff --git a/supabase-auth/src/index.tsx b/supabase-auth/src/index.tsx new file mode 100644 index 0000000..5acaf48 --- /dev/null +++ b/supabase-auth/src/index.tsx @@ -0,0 +1,64 @@ +import { Hono } from 'hono'; +import { getSupabase, supabaseMiddleware } from './middleware/auth.middleware'; + +const app = new Hono(); +app.use('*', supabaseMiddleware()); + +const routes = app.get('/api/user', async (c) => { + const supabase = getSupabase(c); + const { data, error } = await supabase.auth.getUser(); + + if (error) console.log('error', error); + + if (!data?.user) { + return c.json({ + message: 'You are not logged in.', + }); + } + + return c.json({ + message: 'You are logged in!', + userId: data.user, + }); +}); + +app.get('/signout', async (c) => { + const supabase = getSupabase(c); + await supabase.auth.signOut(); + console.log('Signed out server-side!'); + return c.redirect('/'); +}); + +app.get('/countries', async (c) => { + const supabase = getSupabase(c); + const { data, error } = await supabase.from('countries').select('*'); + if (error) console.log(error); + return c.json(data); +}); + +export type AppType = typeof routes; + +app.get('/', (c) => { + return c.html( + + + + + + {import.meta.env.PROD ? ( +