Core Concepts
Comparisons with Other Libraries
If you're coming from React Query, Prisma, or other data management libraries, this guide helps you understand how @wayvo-ai/core compares and what makes it different.
DataSource + Store vs React Query
Conceptual Mapping
| React Query | @wayvo-ai/core | Notes |
|---|---|---|
useQuery | useStore + useDBRows | Store handles querying automatically |
useMutation | store.save() | Save handles both insert and update |
queryClient.invalidateQueries | Auto-refresh after save | No manual invalidation needed |
queryClient.setQueryData | store.setValue() / store.updateRow() | Direct state updates |
isLoading | useIsStoreLoading() | Reactive loading state |
isError | useStoreError() | Error state management |
refetch | store.executeQuery() | Manual refresh when needed |
Key Differences
1. Automatic Cache Management
React Query:
// Manual cache invalidation
const mutation = useMutation({
mutationFn: updateUser,
onSuccess: () => {
queryClient.invalidateQueries(['users']);
},
});
@wayvo-ai/core:
// Automatic refresh after save
await store.save({ feedback: 'User updated' });
// Store automatically refreshes - no manual invalidation needed
2. Type Safety
React Query:
// Manual type definitions
const { data } = useQuery<User[]>({
queryKey: ['users'],
queryFn: () => fetchUsers(),
});
@wayvo-ai/core:
// Types inferred from DataSource
const store = useStore<User>({
datasourceId: 'Users',
// TypeScript knows User type from DataSource
});
const rows = useDBRows(store); // rows: ReadonlyArray<DBRow<User>>
3. CRUD Operations
React Query:
// Separate mutations for each operation
const createMutation = useMutation({ mutationFn: createUser });
const updateMutation = useMutation({ mutationFn: updateUser });
const deleteMutation = useMutation({ mutationFn: deleteUser });
// Manual optimistic updates
queryClient.setQueryData(['users'], (old) => [...old, newUser]);
@wayvo-ai/core:
// Single save method handles insert/update
store.createNew({ partialRecord: { name: 'New User' } });
await store.save(); // Automatically detects insert vs update
// Delete is built-in
store.deleteRow(rowId);
await store.save();
4. Form State Management
React Query:
// Manual form state + mutation
const [formData, setFormData] = useState({});
const mutation = useMutation({ mutationFn: saveUser });
const handleSubmit = () => {
mutation.mutate(formData);
};
@wayvo-ai/core:
// Store tracks form state automatically
const row = useCurrentRowSync(store);
const isDirty = useIsStoreDirty(store);
// Direct updates
store.setValue('name', 'New Name');
// Save with dirty tracking
await store.save();
When to Use What
Use React Query when:
- ✅ You need fine-grained control over caching strategies
- ✅ You're building a public API or external-facing app
- ✅ You need to work with multiple external APIs
- ✅ You want to use React Query's ecosystem (devtools, etc.)
Use @wayvo-ai/core when:
- ✅ You're building an internal/admin application
- ✅ You want automatic CRUD operations
- ✅ You need built-in form state management
- ✅ You want type-safe database operations
- ✅ You need role-based access control built-in
DataSource vs Prisma
Conceptual Mapping
| Prisma | @wayvo-ai/core | Notes |
|---|---|---|
schema.prisma | DataSource definition | TypeScript-based schema |
prisma.user.findMany() | useStore + query | Reactive queries via Store |
prisma.user.create() | store.createNew() + store.save() | Two-step process |
prisma.user.update() | store.setValue() + store.save() | Direct updates |
| Middleware | Lifecycle hooks | beforeInsert, afterUpdate, etc. |
@relation | Joins array | Explicit join definitions |
Key Differences
1. Schema Definition
Prisma:
// schema.prisma
model User {
id String @id @default(uuid())
email String @unique
name String?
posts Post[]
createdAt DateTime @default(now())
}
@wayvo-ai/core:
// TypeScript-based schema
export const UserDataSource: DataSource<User> = {
...DefaultDataSource,
id: 'User',
tableName: 'users',
attributes: [
{
...DefaultAttribute,
code: 'id',
type: 'Text',
column: 'id',
},
{
...DefaultAttribute,
code: 'email',
type: 'Text',
column: 'email',
},
// ...
],
};
2. Querying
Prisma:
// Server-side only
const users = await prisma.user.findMany({
where: { isActive: true },
include: { posts: true },
});
@wayvo-ai/core:
// Client-side reactive queries
const store = useStore<User>({
datasourceId: 'Users',
autoQuery: true,
query: {
filters: [{ isActive: { is: true } }],
},
});
const rows = useDBRows(store); // Reactive, updates automatically
3. Mutations
Prisma:
// Server-side only
await prisma.user.create({
data: { email: 'user@example.com', name: 'John' },
});
await prisma.user.update({
where: { id: userId },
data: { name: 'Jane' },
});
@wayvo-ai/core:
// Client-side mutations with automatic sync
store.createNew({
partialRecord: { email: 'user@example.com', name: 'John' },
});
await store.save();
// Or update existing
store.setValue('name', 'Jane', rowId);
await store.save();
4. Access Control
Prisma:
// Manual access control in application code
if (!session.user.roles.includes('admin')) {
throw new Error('Unauthorized');
}
const users = await prisma.user.findMany();
@wayvo-ai/core:
// Built-in role-based access control
access: [
{
...DefaultFullAccess,
roleCode: 'admin',
},
{
...DefaultReadOnlyAccess,
roleCode: 'viewer',
},
],
// Access control enforced automatically
5. Lifecycle Hooks
Prisma:
// Middleware (limited)
prisma.$use(async (params, next) => {
if (params.action === 'create') {
// Pre-processing
}
return next(params);
});
@wayvo-ai/core:
// Rich lifecycle hooks
beforeInsert: async ({ rows, session, client }) => {
// Validation, transformation
return { rows };
},
afterInsert: async ({ rows, session, client }) => {
// Post-processing, notifications
return rows;
},
beforeUpdate: async ({ rows, session, client }) => {
// Validation, audit logging
return { rows };
},
When to Use What
Use Prisma when:
- ✅ You need a standalone ORM for any framework
- ✅ You want database migrations managed by Prisma
- ✅ You're building a REST API or GraphQL API
- ✅ You need Prisma's ecosystem (Studio, etc.)
Use @wayvo-ai/core when:
- ✅ You're building a Next.js application
- ✅ You want client-side reactive state management
- ✅ You need built-in access control
- ✅ You want automatic form state tracking
- ✅ You need calculated fields and complex joins
- ✅ You want type-safe operations end-to-end
DataSource + Store vs Redux/Zustand
Conceptual Mapping
| Redux/Zustand | @wayvo-ai/core | Notes |
|---|---|---|
useSelector | useDBRows(), useCurrentRowSync() | Reactive selectors |
dispatch(action) | store.setValue(), store.save() | Direct state updates |
| Reducers | DataSource hooks | Business logic in hooks |
| Middleware | Lifecycle hooks | beforeInsert, afterUpdate |
createSlice | DataSource definition | Schema + logic together |
Key Differences
1. State Management
Redux:
// Manual state management
const userSlice = createSlice({
name: 'users',
initialState: { users: [], loading: false },
reducers: {
setUsers: (state, action) => {
state.users = action.payload;
},
},
});
@wayvo-ai/core:
// Automatic state management
const store = useStore<User>({
datasourceId: 'Users',
autoQuery: true,
});
// State managed automatically, synced with server
2. Server Sync
Redux:
// Manual API calls and state updates
const fetchUsers = async (dispatch) => {
dispatch(setLoading(true));
const users = await api.getUsers();
dispatch(setUsers(users));
dispatch(setLoading(false));
};
@wayvo-ai/core:
// Automatic server sync
const store = useStore<User>({
datasourceId: 'Users',
autoQuery: true, // Automatically queries on mount
});
// Store handles loading, error states, and server sync
3. Optimistic Updates
Redux:
// Manual optimistic updates
dispatch(addUserOptimistic(newUser));
try {
await api.createUser(newUser);
dispatch(confirmUser(newUser));
} catch (error) {
dispatch(rollbackUser(newUser.id));
}
@wayvo-ai/core:
// Built-in optimistic updates
store.createNew({ partialRecord: newUser });
// UI updates immediately
await store.save(); // Syncs with server
// Automatically handles rollback on error
Summary: What Makes @wayvo-ai/core Different
Unique Features
Unified Client-Server Model
- Same DataSource definition works on client and server
- Automatic type inference end-to-end
- No manual API layer needed
Built-in Access Control
- Role-based permissions in DataSource definition
- Automatic enforcement on all operations
- No manual authorization checks needed
Automatic State Management
- No manual cache invalidation
- Automatic optimistic updates
- Built-in dirty tracking for forms
Reactive by Default
- All queries are reactive
- UI updates automatically when data changes
- No manual subscriptions needed
Form Integration
- Stores track form state automatically
- Built-in dirty state tracking
- Field-level error management
When @wayvo-ai/core Shines
- ✅ Internal/admin applications
- ✅ CRUD-heavy applications
- ✅ Applications requiring role-based access control
- ✅ Forms with complex validation
- ✅ Real-time data updates
- ✅ Type-safe database operations
When to Consider Alternatives
- ❌ Public APIs or external-facing applications
- ❌ Applications with complex caching requirements
- ❌ Applications that need framework-agnostic solutions
- ❌ Applications with minimal database interaction
Migration Guide
From React Query
- Replace
useQuerywithuseStore+useDBRows - Replace
useMutationwithstore.save() - Remove manual
invalidateQueriescalls - Use
store.setValue()instead ofsetQueryData
From Prisma
- Convert Prisma schema to DataSource definition
- Replace server-side queries with client-side Stores
- Move access control to DataSource
accessarray - Convert Prisma middleware to lifecycle hooks
From Redux/Zustand
- Replace state slices with DataSource definitions
- Replace actions with Store methods (
setValue,save) - Remove manual API calls - Store handles them
- Use Store hooks instead of selectors
Next Steps
- DataSources - Learn about DataSource definitions
- Stores - Understand reactive state management
- Getting Started - Build your first app