Skip to content
This repository was archived by the owner on Oct 22, 2025. It is now read-only.

Commit 3903be8

Browse files
committed
chore: add more platform demos
1 parent 6cdbb14 commit 3903be8

File tree

33 files changed

+1931
-910
lines changed

33 files changed

+1931
-910
lines changed

examples/better-auth/README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Better Auth Integration for RivetKit
2+
3+
Example project demonstrating authentication integration with [RivetKit](https://rivetkit.org) using Better Auth.
4+
5+
[Learn More →](https://github.com/rivet-gg/rivetkit)
6+
7+
[Discord](https://rivet.gg/discord)[Documentation](https://rivetkit.org)[Issues](https://github.com/rivet-gg/rivetkit/issues)
8+
9+
## Getting Started
10+
11+
### Prerequisites
12+
13+
- Node.js 18+
14+
- npm or pnpm
15+
16+
### Installation
17+
18+
```sh
19+
git clone https://github.com/rivet-gg/rivetkit
20+
cd rivetkit/examples/better-auth
21+
npm install
22+
```
23+
24+
### Development
25+
26+
```sh
27+
npm run dev
28+
```
29+
30+
Open your browser to `http://localhost:5173` to see the frontend and the backend will be running on `http://localhost:6420`.
31+
32+
## Features
33+
34+
- **Authentication**: Email/password authentication using Better Auth
35+
- **Protected Workers**: RivetKit workers with authentication via `onAuth` hook
36+
- **Real-time Chat**: Authenticated chat room with real-time messaging
37+
- **SQLite Database**: Persistent user data and session storage
38+
39+
## How It Works
40+
41+
1. **Better Auth Setup**: Configured with SQLite adapter for user storage
42+
2. **Protected Worker**: The `chatRoom` worker uses the `onAuth` hook to verify user sessions
43+
3. **Frontend Integration**: React components handle authentication flow and chat interface
44+
4. **Session Management**: Better Auth handles session creation, validation, and cleanup
45+
46+
## Key Files
47+
48+
- `src/backend/auth.ts` - Better Auth configuration with SQLite
49+
- `src/backend/registry.ts` - RivetKit worker with authentication
50+
- `src/frontend/components/AuthForm.tsx` - Login/signup form
51+
- `src/frontend/components/ChatRoom.tsx` - Authenticated chat interface
52+
53+
## License
54+
55+
Apache 2.0

examples/better-auth/package.json

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"name": "example-better-auth",
3+
"version": "0.9.0-rc.1",
4+
"private": true,
5+
"type": "module",
6+
"scripts": {
7+
"dev": "concurrently \"npm run dev:backend\" \"npm run dev:frontend\"",
8+
"dev:backend": "tsx --watch src/backend/server.ts",
9+
"dev:frontend": "vite",
10+
"build": "vite build",
11+
"check-types": "tsc --noEmit",
12+
"test": "vitest run"
13+
},
14+
"devDependencies": {
15+
"@types/node": "^22.13.9",
16+
"@types/react": "^18.2.0",
17+
"@types/react-dom": "^18.2.0",
18+
"@vitejs/plugin-react": "^4.2.0",
19+
"concurrently": "^8.2.2",
20+
"rivetkit": "workspace:*",
21+
"tsx": "^3.12.7",
22+
"typescript": "^5.5.2",
23+
"vite": "^5.0.0",
24+
"vitest": "^3.1.1"
25+
},
26+
"dependencies": {
27+
"@hono/node-server": "^1.14.4",
28+
"@rivetkit/memory": "workspace:0.9.0-rc.1",
29+
"@rivetkit/react": "workspace:0.9.0-rc.1",
30+
"better-auth": "^1.0.1",
31+
"hono": "^4.7.0",
32+
"react": "^18.2.0",
33+
"react-dom": "^18.2.0"
34+
},
35+
"example": {
36+
"platforms": [
37+
"*"
38+
]
39+
},
40+
"stableVersion": "0.8.0"
41+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { betterAuth } from "better-auth";
2+
import { sqliteAdapter } from "@better-auth/sqlite";
3+
import Database from "better-sqlite3";
4+
5+
const db = new Database("./auth.db");
6+
7+
export const auth = betterAuth({
8+
database: sqliteAdapter(db),
9+
emailAndPassword: {
10+
enabled: true,
11+
},
12+
session: {
13+
expiresIn: 60 * 60 * 24 * 7, // 7 days
14+
updateAge: 60 * 60 * 24, // 1 day (every day the session expiry is updated)
15+
},
16+
plugins: [],
17+
});
18+
19+
export type Session = typeof auth.$Infer.Session;
20+
export type User = typeof auth.$Infer.User;
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { worker, setup } from "rivetkit";
2+
import { auth, type Session, type User } from "./auth";
3+
4+
export const chatRoom = worker({
5+
onAuth: async (c) => {
6+
const authResult = await auth.api.getSession({
7+
headers: c.req.headers,
8+
});
9+
10+
if (!authResult?.session || !authResult?.user) {
11+
throw new Error("Unauthorized");
12+
}
13+
14+
return {
15+
userId: authResult.user.id,
16+
user: authResult.user,
17+
session: authResult.session,
18+
};
19+
},
20+
state: {
21+
messages: [] as Array<{ id: string; userId: string; username: string; message: string; timestamp: number }>
22+
},
23+
actions: {
24+
sendMessage: (c, message: string) => {
25+
const newMessage = {
26+
id: crypto.randomUUID(),
27+
userId: c.auth.userId,
28+
username: c.auth.user.email,
29+
message,
30+
timestamp: Date.now(),
31+
};
32+
33+
c.state.messages.push(newMessage);
34+
c.broadcast("newMessage", newMessage);
35+
36+
return newMessage;
37+
},
38+
getMessages: (c) => {
39+
return c.state.messages;
40+
},
41+
},
42+
});
43+
44+
export const registry = setup({
45+
workers: { chatRoom },
46+
});
47+
48+
export type Registry = typeof registry;
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { registry } from "./registry";
2+
import { auth } from "./auth";
3+
import { Hono } from "hono";
4+
import { serve } from "@hono/node-server";
5+
import { createMemoryDriver } from "@rivetkit/memory";
6+
7+
// Setup router
8+
const app = new Hono();
9+
10+
// Start RivetKit
11+
const { client, hono } = registry.run({
12+
driver: createMemoryDriver(),
13+
cors: {
14+
// IMPORTANT: Configure origins in production
15+
origin: "*",
16+
},
17+
});
18+
19+
// Mount Better Auth routes
20+
app.on(["GET", "POST"], "/api/auth/**", (c) => auth.handler(c.req.raw));
21+
22+
// Expose RivetKit to the frontend
23+
app.route("/registry", hono);
24+
25+
// Example HTTP endpoint to join chat room
26+
app.post("/api/join-room/:roomId", async (c) => {
27+
const roomId = c.req.param("roomId");
28+
29+
// Verify authentication
30+
const authResult = await auth.api.getSession({
31+
headers: c.req.header(),
32+
});
33+
34+
if (!authResult?.session || !authResult?.user) {
35+
return c.json({ error: "Unauthorized" }, 401);
36+
}
37+
38+
try {
39+
const room = client.chatRoom.getOrCreate(roomId);
40+
const messages = await room.getMessages();
41+
42+
return c.json({
43+
success: true,
44+
roomId,
45+
messages,
46+
user: authResult.user
47+
});
48+
} catch (error) {
49+
return c.json({ error: "Failed to join room" }, 500);
50+
}
51+
});
52+
53+
serve({ fetch: app.fetch, port: 6420 }, () =>
54+
console.log("Listening at http://localhost:6420"),
55+
);
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { useState, useEffect } from "react";
2+
import { authClient } from "./auth-client";
3+
import { AuthForm } from "./components/AuthForm";
4+
import { ChatRoom } from "./components/ChatRoom";
5+
6+
function App() {
7+
const [user, setUser] = useState<{ id: string; email: string } | null>(null);
8+
const [loading, setLoading] = useState(true);
9+
10+
useEffect(() => {
11+
// Check if user is already authenticated
12+
const checkAuth = async () => {
13+
try {
14+
const session = await authClient.getSession();
15+
if (session.data?.user) {
16+
setUser(session.data.user);
17+
}
18+
} catch (error) {
19+
console.error("Auth check failed:", error);
20+
} finally {
21+
setLoading(false);
22+
}
23+
};
24+
25+
checkAuth();
26+
}, []);
27+
28+
const handleAuthSuccess = async () => {
29+
try {
30+
const session = await authClient.getSession();
31+
if (session.data?.user) {
32+
setUser(session.data.user);
33+
}
34+
} catch (error) {
35+
console.error("Failed to get user after auth:", error);
36+
}
37+
};
38+
39+
const handleSignOut = () => {
40+
setUser(null);
41+
};
42+
43+
if (loading) {
44+
return (
45+
<div style={{
46+
display: "flex",
47+
justifyContent: "center",
48+
alignItems: "center",
49+
height: "100vh"
50+
}}>
51+
Loading...
52+
</div>
53+
);
54+
}
55+
56+
return (
57+
<div style={{ minHeight: "100vh", backgroundColor: "#f0f0f0" }}>
58+
<div style={{ padding: "20px 0" }}>
59+
<h1 style={{ textAlign: "center", marginBottom: "30px" }}>
60+
RivetKit with Better Auth
61+
</h1>
62+
63+
{user ? (
64+
<ChatRoom user={user} onSignOut={handleSignOut} />
65+
) : (
66+
<AuthForm onAuthSuccess={handleAuthSuccess} />
67+
)}
68+
</div>
69+
</div>
70+
);
71+
}
72+
73+
export default App;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { createAuthClient } from "better-auth/react";
2+
3+
export const authClient = createAuthClient({
4+
baseURL: "http://localhost:6420",
5+
});

0 commit comments

Comments
 (0)