A character management application built with React frontend and tRPC + SurrealDB backend.
- Frontend: React (packages/frontend)
- Backend: tRPC + SurrealDB (packages/backend)
- Authentication: Firebase Auth (frontend only)
- Database: SurrealDB (replacing Firestore)
- Real-time: WebSocket subscriptions via tRPC
- File Uploads: Express server with Multer
# Install root dependencies
bun install
# Install all workspace dependencies
bun run install:all# Start SurrealDB locally
surreal start --user root --pass rootCopy the environment example and fill in your Firebase credentials:
cp packages/backend/.env.example packages/backend/.envUpdate the following variables in packages/backend/.env:
# Firebase Admin Configuration
FIREBASE_PROJECT_ID=your-project-id
FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
FIREBASE_CLIENT_EMAIL=firebase-adminsdk-...@your-project.iam.gserviceaccount.comUpdate the Firebase configuration in packages/frontend/src/App.jsx with your project credentials.
# Development mode (runs both frontend and backend)
bun run dev
# Or run individually:
bun run dev:frontend # React app on http://localhost:3000
bun run dev:backend # tRPC server on http://localhost:3001The backend runs multiple servers:
- tRPC HTTP:
http://localhost:3001- Main API - tRPC WebSocket:
ws://localhost:3002- Real-time subscriptions - Upload Server:
http://localhost:3003- File uploads
me()- Get current user profileupdateProfile(data)- Update user profilegetById(userId)- Get user by ID
list()- Get all characters for current usergetById(id)- Get character by IDcreate(data)- Create new characterupdate(id, data)- Update characterdelete(id)- Delete characteronUpdate()- Real-time subscription for character updates
list(limit, offset)- List user's filesgetById(id)- Get file by IDdelete(id)- Delete file
POST /upload- Upload files (multipart/form-data)GET /uploads/:filename- Serve uploaded files
The application supports real-time updates using SurrealDB's live queries and tRPC subscriptions:
// Subscribe to character updates
const subscription = trpc.character.onUpdate.useSubscription();{
id: "user:firebase_uid",
uid: "firebase_uid",
email: "user@example.com",
displayName?: "Display Name",
photoURL?: "https://...",
createdAt: "2023-...",
updatedAt: "2023-..."
}{
id: "character:uuid",
userId: "user:firebase_uid",
name: "Character Name",
class: "Fighter",
level: 1,
stats: {
strength: 10,
dexterity: 10,
constitution: 10,
intelligence: 10,
wisdom: 10,
charisma: 10
},
createdAt: "2023-...",
updatedAt: "2023-..."
}{
id: "file_upload:uuid",
userId: "user:firebase_uid",
filename: "generated_filename.ext",
originalName: "original_filename.ext",
mimetype: "image/jpeg",
size: 1024000,
url: "/uploads/generated_filename.ext",
createdAt: "2023-..."
}# Build both frontend and backend
bun run build
# Build individually
bun run build --workspace=packages/frontend
bun run build --workspace=packages/backend# Run tests
bun run testThe backend includes TypeScript for full type safety:
cd packages/backend
bun run type-check- Build the application
- Set up SurrealDB in production
- Configure environment variables
- Deploy the built frontend and backend
- Update CORS origins in the backend configuration
This setup keeps Firebase Auth for authentication while replacing Firestore with SurrealDB. The backend automatically creates users in SurrealDB when they first authenticate through Firebase.