Skip to content

viuex server only architecture

Current State:

  • VibexDataManager runs on both client and server
  • Client: VibexDataManagerDatabaseDataAdapter → API routes → SupabaseDatabaseAdapter → DB
  • Server: VibexDataManagerSupabaseDatabaseAdapter → DB (direct)
  • React hooks directly call VibexDataManager methods

Problems:

  1. DatabaseDataAdapter depends on API routes - creates circular dependency concerns
  2. Complex client/server detection logic - getVibexDataManager() has branching logic
  3. Security concerns - client bundle includes data access logic
  4. Unclear boundaries - where does client logic end and server logic begin?

Core Principle: Vibex = Server-Only, React = Client-Only

Section titled “Core Principle: Vibex = Server-Only, React = Client-Only”
┌─────────────────┐
│ React Client │ (Client Components)
│ - useVibex* │
│ - UI Logic │
└────────┬────────┘
│ HTTP / Server Actions
┌─────────────────┐
│ API Routes / │ (Server Components)
│ Server Actions │
│ - Auth │
│ - Validation │
└────────┬────────┘
┌─────────────────┐
│ Vibex Core │ (Server-Only)
│ - DataManager │
│ - Space │
│ - XAgent │
│ - Storage │
└────────┬────────┘
┌─────────────────┐
│ Database / │
│ File System │
└─────────────────┘
  • Server: Data access, business logic, agent orchestration
  • Client: UI, user interactions, state management
  • No database credentials in client bundle
  • All data access goes through controlled API routes
  • Easier to add authentication, authorization, rate limiting
  • Remove DatabaseDataAdapter entirely
  • Remove client/server detection logic
  • Single code path: API → Vibex → Database
  • Uses Server Actions for mutations
  • Uses API routes for queries
  • Leverages React Server Components
  • Server logic can be tested independently
  • Client logic is pure UI
  • Clear boundaries for mocking

New File: app/src/vibex/server/actions.ts

"use server";
import { getVibexDataManagerServer } from "../data/manager";
export async function getSpace(spaceId: string) {
const manager = getVibexDataManagerServer();
return await manager.getSpace(spaceId);
}
export async function listSpaces(filters?: SpaceFilters) {
const manager = getVibexDataManagerServer();
return await manager.listSpaces(filters);
}
export async function createSpace(space: Partial<Space>) {
const manager = getVibexDataManagerServer();
return await manager.createSpace(space);
}
export async function updateSpace(spaceId: string, updates: Partial<Space>) {
const manager = getVibexDataManagerServer();
return await manager.updateSpace(spaceId, updates);
}
// ... more actions

Phase 2: Update React Hooks to Use Server Actions

Section titled “Phase 2: Update React Hooks to Use Server Actions”

Update: app/src/vibex/react/hooks.ts

"use client";
import { use, useState, useEffect } from "react";
import * as vibexActions from "../server/actions";
export function useVibexSpace(spaceId: string | null | undefined) {
const [space, setSpace] = useState<Space | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
if (!spaceId) {
setSpace(null);
setLoading(false);
return;
}
setLoading(true);
vibexActions.getSpace(spaceId)
.then(setSpace)
.catch(setError)
.finally(() => setLoading(false));
}, [spaceId]);
const updateSpace = async (updates: Partial<Space>) => {
if (!spaceId) throw new Error("No space ID");
setLoading(true);
try {
const updated = await vibexActions.updateSpace(spaceId, updates);
setSpace(updated);
return updated;
} finally {
setLoading(false);
}
};
return { space, loading, error, updateSpace };
}

Remove:

  • DatabaseDataAdapter (no longer needed)
  • getVibexDataManager() client detection logic
  • Client-side VibexDataManager instantiation

Keep:

  • SupabaseDatabaseAdapter (server-only)
  • LocalDataAdapter (for CLI/server scripts)
  • VibexDataManager (server-only)

Current API routes already use server-side adapters - no changes needed!

app/src/app/api/spaces/route.ts
import { getServerDataAdapter } from "@/vibex/data/factory";
export async function GET() {
const adapter = getServerDataAdapter(); // Already server-only!
const spaces = await adapter.getSpaces();
return NextResponse.json({ spaces });
}
  • Create app/src/vibex/server/actions.ts
  • Wrap all VibexDataManager methods
  • Add error handling and validation
  • Replace useVibex() calls with server actions
  • Update all hooks to use vibexActions.*
  • Test with existing components
  • Remove DatabaseDataAdapter
  • Simplify VibexDataManager (remove client detection)
  • Update factory to only create server adapters
  • Use WebSockets or Server-Sent Events
  • Or use Supabase Realtime subscriptions
  • Update hooks to subscribe to changes
app/src/app/api/vibex/stream/route.ts
export async function GET(request: NextRequest) {
const stream = new ReadableStream({
start(controller) {
const manager = getVibexDataManagerServer();
const unsubscribe = manager.subscribeToSpace(spaceId, (space) => {
controller.enqueue(`data: ${JSON.stringify(space)}
`);
});
request.signal.addEventListener('abort', () => {
unsubscribe();
controller.close();
});
},
});
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
},
});
}
// Use Supabase Realtime subscriptions
// VibexDataManager can subscribe to database changes
// React hooks can use SWR with revalidation
  • Simpler: One code path, no client/server branching
  • Secure: No DB access from client
  • Maintainable: Clear boundaries
  • Scalable: Easy to add caching, rate limiting
  • Network overhead: Every action requires HTTP round-trip
  • Less optimistic: Can’t update UI immediately
  • More API routes: Need to expose all operations
  • Real-time complexity: Need WebSockets/SSE for subscriptions
  • Optimistic updates: Use React state + server confirmation
  • Caching: Use SWR/React Query for client-side caching
  • Batching: Combine multiple operations in single request
  • Streaming: Use Server Actions with streaming responses

✅ YES - Make Vibex server-only

This aligns with:

  1. Next.js App Router best practices
  2. Security best practices
  3. Clear separation of concerns
  4. Easier to maintain and test

The trade-offs (network overhead, optimistic updates) can be mitigated with:

  • Server Actions (Next.js built-in)
  • SWR/React Query (client-side caching)
  • Optimistic UI patterns
  • Streaming responses
  1. Create server actions layer
  2. Update React hooks
  3. Remove DatabaseDataAdapter
  4. Test with existing components
  5. Add real-time support if needed