React hooks for VAIF - Authentication, Data, Realtime, Storage, and more
npm install @vaiftech/reactReact hooks for VAIF Studio - a Backend-as-a-Service platform.
``bash`
npm install @vaiftech/react @vaiftech/clientor
pnpm add @vaiftech/react @vaiftech/clientor
yarn add @vaiftech/react @vaiftech/client
`tsx
import { VaifProvider } from '@vaiftech/react';
import { createVaifClient } from '@vaiftech/client';
const client = createVaifClient({
baseUrl: 'https://api.myproject.vaif.io',
apiKey: 'vaif_pk_xxx',
});
function App() {
return (
);
}
`
| Category | Hooks |
|----------|-------|
| Auth | useAuth, useUser, useToken, usePasswordReset, useEmailVerification, useMagicLink, useOAuth, useMFA |useQuery
| Query | , useQueryById, useQueryFirst, usePaginatedQuery, useInfiniteQuery, useCount |useMutation
| Mutation | , useCreate, useUpdate, useDelete, useUpsert, useBatchCreate, useBatchUpdate, useBatchDelete, useOptimisticMutation |useSubscription
| Realtime | , useChannel, usePresence, useRealtimeConnection, useBroadcast |useUpload
| Storage | , useDownload, useFile, useFiles, useDropzone, usePublicUrl |useFunction
| Functions | , useRpc, useFunctionList, useBatchInvoke, useScheduledFunction |useMongoFind
| MongoDB | , useMongoFindOne, useMongoAggregate, useMongoInsertOne, useMongoInsertMany, useMongoUpdateOne, useMongoUpdateMany, useMongoDeleteOne, useMongoDeleteMany, useMongoInfiniteFind, useMongoCount, useMongoDistinct, useMongoCollection |useMetrics
| Observability | , useAuditLogs, useIncidents, useSystemHealth, useRealtimeStats, useErrorTracking |
`tsx
import { useAuth, useUser, useSession, useMFA, useOAuth } from '@vaiftech/react';
function AuthComponent() {
const { user, isLoading, signIn, signUp, signOut } = useAuth();
if (isLoading) return
if (!user) {
return (
);
}
return (
Welcome, {user.email}
// Just the user
function Profile() {
const { user, isLoading } = useUser();
return user ?
{user.email}
: null;// OAuth login
function OAuthLogin() {
const { signInWithProvider, isLoading } = useOAuth();
return (
// MFA setup
function MFASetup() {
const { enroll, verify, isEnrolling, qrCode } = useMFA();
const handleSetup = async () => {
await enroll({ type: 'totp' });
};
return (
$3
For auth-only applications, use the standalone auth provider from
@vaiftech/auth:`tsx
import { AuthProvider, useAuth, useSession } from '@vaiftech/react';function App() {
return (
);
}
function LoginPage() {
const { signInWithPassword, signInWithOAuth, isLoading } = useAuth();
return (
);
}
`Data Fetching
`tsx
import { useQuery, useQueryById, usePaginatedQuery, useInfiniteQuery } from '@vaiftech/react';interface Post {
id: string;
title: string;
content: string;
}
// Basic query
function PostList() {
const { data: posts, isLoading, error, refetch } = useQuery('posts', {
filters: [{ field: 'published', operator: 'eq', value: true }],
orderBy: [{ field: 'createdAt', direction: 'desc' }],
limit: 10,
});
if (isLoading) return
Loading...;
if (error) return Error: {error.message}; return (
{posts?.map(post => (
- {post.title}
))}
);
}// Query by ID
function PostDetail({ postId }: { postId: string }) {
const { data: post, isLoading } = useQueryById('posts', postId);
if (isLoading) return
Loading...;
return {post?.title}
;
}// Paginated query
function PaginatedPosts() {
const {
data,
page,
totalPages,
nextPage,
prevPage,
isLoading
} = usePaginatedQuery('posts', {
pageSize: 20,
});
return (
{data?.map(post => {post.title})}
{page} / {totalPages}
);
}// Infinite scroll
function InfinitePosts() {
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage
} = useInfiniteQuery('posts', { limit: 20 });
return (
{data?.map(post => {post.title})}
{hasNextPage && (
)}
);
}
`Mutations
`tsx
import { useCreate, useUpdate, useDelete, useMutation, useOptimisticMutation } from '@vaiftech/react';// Create
function CreatePost() {
const { create, isCreating, error } = useCreate('posts');
const handleSubmit = async (data: Omit) => {
const newPost = await create(data);
console.log('Created:', newPost);
};
return (
);
}
// Update
function UpdatePost({ postId }: { postId: string }) {
const { update, isUpdating } = useUpdate('posts');
return (
onClick={() => update(postId, { title: 'Updated Title' })}
disabled={isUpdating}
>
Update
);
}
// Delete
function DeletePost({ postId }: { postId: string }) {
const { remove, isDeleting } = useDelete('posts');
return (
);
}
// Full mutation hook
function PostActions() {
const { create, update, remove, isLoading } = useMutation('posts');
// Use create, update, remove as needed
}
// Optimistic updates
function OptimisticLike({ postId }: { postId: string }) {
const { mutate } = useOptimisticMutation('posts', {
optimisticUpdate: (cache, postId) => {
return { ...cache[postId], likes: cache[postId].likes + 1 };
},
rollbackOnError: true,
});
return ;
}
`Realtime
`tsx
import { useSubscription, useChannel, usePresence, useBroadcast } from '@vaiftech/react';// Subscribe to table changes
function MessageListener() {
useSubscription('messages', {
event: 'INSERT',
onInsert: (message) => console.log('New message:', message),
onUpdate: (message) => console.log('Updated:', message),
onDelete: (message) => console.log('Deleted:', message.id),
});
return null;
}
// Channel communication
function ChatRoom({ roomId }: { roomId: string }) {
const channel = useChannel(
room-${roomId});
const [messages, setMessages] = useState([]); useEffect(() => {
channel.on('message', (msg) => {
setMessages(prev => [...prev, msg]);
});
return () => channel.off('message');
}, [channel]);
const sendMessage = (text: string) => {
channel.send('message', { text, timestamp: Date.now() });
};
return
{/ Chat UI /};
}// Presence tracking
function OnlineUsers({ roomId }: { roomId: string }) {
const { users, track, leave } = usePresence(
room-${roomId}); useEffect(() => {
track({ status: 'online', name: currentUser.name });
return () => leave();
}, []);
return (
Online ({users.length})
{users.map(u => {u.name})}
);
}// Broadcast events
function TypingIndicator({ roomId }: { roomId: string }) {
const { send, subscribe } = useBroadcast(
room-${roomId});
const [typing, setTyping] = useState([]); useEffect(() => {
subscribe('typing', (event) => {
setTyping(prev => [...prev, event.userId]);
setTimeout(() => {
setTyping(prev => prev.filter(id => id !== event.userId));
}, 3000);
});
}, []);
const onType = () => send('typing', { userId: currentUser.id });
return
{typing.length > 0 && ${typing.join(', ')} typing...};
}
`Storage
`tsx
import { useUpload, useDownload, useFile, useFiles, useDropzone, usePublicUrl } from '@vaiftech/react';// File upload with progress
function FileUpload() {
const { upload, progress, isUploading, error } = useUpload();
const handleUpload = async (file: File) => {
const result = await upload(file,
uploads/${file.name});
console.log('Uploaded:', result?.url);
}; return (
handleUpload(e.target.files![0])} />
{isUploading && }
{error && Error: {error.message}
}
);
}// Drag and drop zone
function DropzoneUpload() {
const { getRootProps, getInputProps, isDragActive, files } = useDropzone({
accept: { 'image/*': ['.png', '.jpg', '.gif'] },
maxFiles: 5,
onDrop: async (files) => {
// Handle uploaded files
},
});
return (
{isDragActive ? 'Drop files here' : 'Drag files or click to upload'}
);
}// Display files
function FileDisplay({ path }: { path: string }) {
const url = usePublicUrl(path);
return url ?
: null;
}
// List files
function FileList({ prefix }: { prefix: string }) {
const { files, isLoading, refetch } = useFiles({ prefix });
return (
{files?.map(file => (
- {file.name} ({file.size} bytes)
))}
);
}
`Edge Functions
`tsx
import { useFunction, useRpc, useBatchInvoke } from '@vaiftech/react';// Invoke function
function SendEmail() {
const { invoke, isInvoking, error, result } = useFunction('send-email');
const handleSend = async () => {
await invoke({
to: 'user@example.com',
subject: 'Hello',
});
};
return (
);
}
// RPC-style calls
function DataProcessor() {
const { call, isLoading, data } = useRpc('process-data');
return (
);
}
// Batch invocations
function BatchProcessor() {
const { invoke, isInvoking, results } = useBatchInvoke();
const processAll = async () => {
await invoke([
{ name: 'resize-image', payload: { url: 'img1.jpg' } },
{ name: 'resize-image', payload: { url: 'img2.jpg' } },
{ name: 'resize-image', payload: { url: 'img3.jpg' } },
]);
};
return ;
}
`MongoDB Hooks
`tsx
import {
useMongoFind,
useMongoFindOne,
useMongoInsertOne,
useMongoUpdateOne,
useMongoDeleteOne,
useMongoAggregate,
useMongoCollection,
useMongoInfiniteFind,
} from '@vaiftech/react';// Find documents
function UserList() {
const { data: users, isLoading, error, refetch } = useMongoFind('users', {
filter: { status: 'active' },
sort: { createdAt: -1 },
limit: 20,
});
if (isLoading) return
Loading...;
if (error) return Error: {error.message}; return (
{users?.map(user => - {user.name}
)}
);
}// Find single document
function UserProfile({ id }: { id: string }) {
const { data: user, isLoading } = useMongoFindOne('users', { _id: id });
return user ? {user.name} : null;
}
// Insert document
function CreateUser() {
const { insertOne, isInserting } = useMongoInsertOne('users');
const handleCreate = async () => {
const result = await insertOne({
name: 'New User',
email: 'new@example.com',
createdAt: new Date(),
});
console.log('Created:', result.insertedId);
};
return (
);
}
// Update document
function UpdateUser({ id }: { id: string }) {
const { updateOne, isUpdating } = useMongoUpdateOne('users');
return (
onClick={() => updateOne({ _id: id }, { $set: { updatedAt: new Date() } })}
disabled={isUpdating}
>
Update
);
}
// Delete document
function DeleteUser({ id }: { id: string }) {
const { deleteOne, isDeleting } = useMongoDeleteOne('users');
return (
);
}
// Aggregation pipeline
function UserStats() {
const { data: stats } = useMongoAggregate<{ _id: string; count: number }>('users', [
{ $match: { status: 'active' } },
{ $group: { _id: '$country', count: { $sum: 1 } } },
{ $sort: { count: -1 } },
]);
return (
{stats?.map(stat => (
- {stat._id}: {stat.count}
))}
);
}// Infinite scroll with MongoDB
function InfiniteUserList() {
const { data, fetchNextPage, hasNextPage } = useMongoInfiniteFind('users', {
filter: { status: 'active' },
sort: { createdAt: -1 },
limit: 20,
});
return (
{data?.map(user => {user.name})}
{hasNextPage && }
);
}// Full collection access
function AdvancedOps() {
const collection = useMongoCollection('users');
const handleBulk = async () => {
const count = await collection.count({ status: 'active' });
const countries = await collection.distinct('country');
await collection.bulkWrite([
{ insertOne: { document: { name: 'Bulk User' } } },
]);
};
return ;
}
`Observability Hooks
`tsx
import {
useMetrics,
useAuditLogs,
useIncidents,
useSystemHealth,
useRealtimeStats,
useErrorTracking,
} from '@vaiftech/react';// Metrics dashboard
function MetricsDashboard() {
const { metrics, isLoading, timeRange, setTimeRange, refresh } = useMetrics({
sources: ['api', 'database', 'storage'],
interval: '1h',
});
return (
{metrics?.map(m => (
{m.name}: {m.value}
))}
);
}// Audit logs
function AuditLogViewer() {
const { logs, hasMore, loadMore, setFilters } = useAuditLogs({ limit: 50 });
return (
{logs?.map(log => (
- {log.timestamp}: {log.action} by {log.userId}
))}
{hasMore && }
);
}// Incident management
function IncidentList() {
const { incidents, activeCount, acknowledge, resolve } = useIncidents();
return (
Active Incidents: {activeCount}
{incidents?.map(incident => (
{incident.title} - {incident.severity}
{incident.status === 'open' && (
)}
{incident.status === 'acknowledged' && (
)}
))}
);
}// System health
function SystemHealth() {
const { isHealthy, services, lastCheck } = useSystemHealth({ pollInterval: 30000 });
return (
Status: {isHealthy ? 'Healthy' : 'Degraded'}
Last Check: {lastCheck?.toLocaleString()}
{services?.map(svc => (
{svc.name}: {svc.status} ({svc.latency}ms)
))}
);
}// Realtime stats
function RealtimeStats() {
const { connections, requestsPerSecond, activeUsers } = useRealtimeStats();
return (
Connections: {connections}
Requests/sec: {requestsPerSecond}
Active Users: {activeUsers}
);
}// Error tracking
function ErrorTracker() {
const { errorRate, topErrors, trackError } = useErrorTracking({ timeRange: '24h' });
return (
Error Rate: {errorRate}%
{topErrors?.map(err => (
{err.message} ({err.count})
))}
);
}
`TypeScript Support
All hooks support TypeScript generics for full type safety:
`tsx
interface User {
id: string;
email: string;
name: string;
}const { data } = useQuery('users');
// data is User[] | undefined
const { data: user } = useMongoFindOne('users', { email: 'test@example.com' });
// user is User | null | undefined
``- @vaiftech/client - Core client SDK
- @vaiftech/auth - Standalone auth client
- @vaiftech/sdk-expo - React Native/Expo SDK
- @vaiftech/cli - CLI tools
MIT