(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/examples/auth/hono/src/index.tsx b/examples/auth/hono/src/index.tsx
new file mode 100644
index 00000000000..5acaf4801f8
--- /dev/null
+++ b/examples/auth/hono/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 ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+ );
+});
+
+export default app;
diff --git a/examples/auth/hono/src/middleware/auth.middleware.ts b/examples/auth/hono/src/middleware/auth.middleware.ts
new file mode 100644
index 00000000000..24d2888a071
--- /dev/null
+++ b/examples/auth/hono/src/middleware/auth.middleware.ts
@@ -0,0 +1,56 @@
+import { createServerClient, parseCookieHeader } from '@supabase/ssr';
+import { SupabaseClient } from '@supabase/supabase-js';
+import type { Context, MiddlewareHandler } from 'hono';
+import { env } from 'hono/adapter';
+import { setCookie } from 'hono/cookie';
+
+declare module 'hono' {
+ interface ContextVariableMap {
+ supabase: SupabaseClient;
+ }
+}
+
+export const getSupabase = (c: Context) => {
+ return c.get('supabase');
+};
+
+type SupabaseEnv = {
+ VITE_SUPABASE_URL: string;
+ VITE_SUPABASE_ANON_KEY: string;
+};
+
+export const supabaseMiddleware = (): MiddlewareHandler => {
+ return async (c, next) => {
+ const supabaseEnv = env(c);
+ const supabaseUrl =
+ supabaseEnv.VITE_SUPABASE_URL ?? import.meta.env.VITE_SUPABASE_URL;
+ const supabaseAnonKey =
+ supabaseEnv.VITE_SUPABASE_ANON_KEY ??
+ import.meta.env.VITE_SUPABASE_ANON_KEY;
+
+ if (!supabaseUrl) {
+ throw new Error('SUPABASE_URL missing!');
+ }
+
+ if (!supabaseAnonKey) {
+ throw new Error('SUPABASE_ANON_KEY missing!');
+ }
+
+ const supabase = createServerClient(supabaseUrl, supabaseAnonKey, {
+ cookies: {
+ getAll() {
+ return parseCookieHeader(c.req.header('Cookie') ?? '');
+ },
+ setAll(cookiesToSet) {
+ cookiesToSet.forEach(({ name, value, options }) =>
+ setCookie(c, name, value, options)
+ );
+ },
+ },
+ });
+
+ c.set('supabase', supabase);
+
+ await next();
+ };
+};
diff --git a/examples/auth/hono/tsconfig.json b/examples/auth/hono/tsconfig.json
new file mode 100644
index 00000000000..af70760b695
--- /dev/null
+++ b/examples/auth/hono/tsconfig.json
@@ -0,0 +1,12 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "strict": true,
+ "lib": ["ESNext", "DOM", "DOM.Iterable"],
+ "types": ["@cloudflare/workers-types", "vite/client"],
+ "jsx": "react-jsx",
+ "jsxImportSource": "hono/jsx"
+ }
+}
diff --git a/examples/auth/hono/vite.config.ts b/examples/auth/hono/vite.config.ts
new file mode 100644
index 00000000000..d6e59e72631
--- /dev/null
+++ b/examples/auth/hono/vite.config.ts
@@ -0,0 +1,33 @@
+import devServer from "@hono/vite-dev-server";
+import { defineConfig } from "vite";
+
+// Change the import to use your runtime specific build
+import build from "@hono/vite-build/node";
+
+export default defineConfig(({ mode }) => {
+ if (mode === "client")
+ return {
+ esbuild: {
+ jsxImportSource: "hono/jsx/dom", // Optimized for hono/jsx/dom
+ },
+ build: {
+ rollupOptions: {
+ input: "./src/client.tsx",
+ output: {
+ entryFileNames: "static/client.js",
+ },
+ },
+ },
+ };
+
+ return {
+ plugins: [
+ build({
+ entry: "src/index.tsx",
+ }),
+ devServer({
+ entry: "src/index.tsx",
+ }),
+ ],
+ };
+});