UNPKG

@srfoster/one-backend-react

Version:

React components and hooks for One Backend authentication and data management

376 lines (291 loc) 9.14 kB
# TODO * Security: CORS per app id? Distinct auth/JWT per app login. Prevent one frontend from CRUDing another's data NOTE: This is a WIP, not recommended for use unless you're me... # One Backend React React components and hooks for seamless integration with One Backend - a generalized serverless backend for authentication, CRUD operations, and file management. ## Installation ```bash npm install @srfoster/one-backend-react ``` ## Quick Start ### 1. Get Your App ID Your App ID is a unique identifier for your application in the One Backend system. You can use any string that uniquely identifies your app, such as: - `my-todo-app` - `acme-crm` - `blog-platform` - `ecommerce-store` **Important:** Choose a unique App ID as this isolates your app's data from other applications using the same backend. All users, data records, and files will be scoped to your App ID. ### 2. Setup the Provider Wrap your app with the `AuthProvider`: ```tsx import React from 'react'; import { AuthProvider } from '@srfoster/one-backend-react'; function App() { return ( <AuthProvider config={{ appId: 'your-app-id' }}> <YourAppContent /> </AuthProvider> ); } ``` ### 2. Use Authentication ```tsx import React from 'react'; import { useAuth, LoginForm, RegisterForm } from '@srfoster/one-backend-react'; function AuthPage() { const { user, logout, isAuthenticated } = useAuth(); if (isAuthenticated) { return ( <div> <h1>Welcome, {user?.email}!</h1> <button onClick={logout}>Logout</button> </div> ); } return ( <div> <h2>Login</h2> <LoginForm onSuccess={() => console.log('Logged in!')} onError={(error) => console.error(error)} /> <h2>Register</h2> <RegisterForm onSuccess={() => console.log('Registered!')} onError={(error) => console.error(error)} /> </div> ); } ``` ### 3. Manage Data ```tsx import React from 'react'; import { useData } from '@srfoster/one-backend-react'; function PostsList() { const { data: posts, loading, error, createRecord, updateRecord, deleteRecord } = useData('posts'); const handleCreatePost = async () => { await createRecord({ data: { title: 'New Post', content: 'This is a new post', status: 'published' } }); }; if (loading) return <div>Loading...</div>; if (error) return <div>Error: {error}</div>; return ( <div> <button onClick={handleCreatePost}>Create Post</button> {posts.map(post => ( <div key={post.id}> <h3>{post.data.title}</h3> <p>{post.data.content}</p> <button onClick={() => deleteRecord(post.id)}>Delete</button> </div> ))} </div> ); } ``` #### Batch Delete Records ```tsx import React, { useState } from 'react'; import { useData } from '@srfoster/one-backend-react'; function PostsManager() { const { data: posts, loading, error, deleteRecords } = useData('posts'); const [selectedPosts, setSelectedPosts] = useState<string[]>([]); const handleBatchDelete = async () => { if (selectedPosts.length === 0) return; const result = await deleteRecords(selectedPosts); if (result.success) { console.log(`Deleted ${result.deleted} posts`); if (result.failed > 0) { console.log(`Failed to delete ${result.failed} posts`); } setSelectedPosts([]); // Clear selection } }; const toggleSelection = (postId: string) => { setSelectedPosts(prev => prev.includes(postId) ? prev.filter(id => id !== postId) : [...prev, postId] ); }; if (loading) return <div>Loading...</div>; if (error) return <div>Error: {error}</div>; return ( <div> <div> <button onClick={handleBatchDelete} disabled={selectedPosts.length === 0} > Delete Selected ({selectedPosts.length}) </button> </div> {posts.map(post => ( <div key={post.id}> <input type="checkbox" checked={selectedPosts.includes(post.id)} onChange={() => toggleSelection(post.id)} /> <h3>{post.data.title}</h3> <p>{post.data.content}</p> </div> ))} </div> ); } ``` ### 4. Handle File Uploads ```tsx import React from 'react'; import { useFileUpload } from '@srfoster/one-backend-react'; function FileUploader() { const { uploadFile, uploading, error } = useFileUpload(); const handleFileUpload = async (event: React.ChangeEvent<HTMLInputElement>) => { const file = event.target.files?.[0]; if (!file) return; const result = await uploadFile(file); if (result) { console.log('File uploaded:', result.fileId); } }; return ( <div> <input type="file" onChange={handleFileUpload} disabled={uploading} /> {uploading && <p>Uploading...</p>} {error && <p>Error: {error}</p>} </div> ); } ``` ## API Reference ### Components #### `AuthProvider` Provides authentication context to your app. **Props:** - `config`: Object with `appId` - `children`: React nodes #### `LoginForm` Pre-built login form component. **Props:** - `onSuccess?`: Callback when login succeeds - `onError?`: Callback when login fails - `className?`: CSS class name #### `RegisterForm` Pre-built registration form component. **Props:** - `onSuccess?`: Callback when registration succeeds - `onError?`: Callback when registration fails - `className?`: CSS class name - `showNameFields?`: Whether to show first/last name fields (default: true) ### Hooks #### `useAuth()` Provides authentication state and methods. **Returns:** - `user`: Current user object or null - `token`: JWT token or null - `isLoading`: Boolean indicating loading state - `isAuthenticated`: Boolean indicating if user is logged in - `login(email, password)`: Login function - `register(email, password, firstName?, lastName?)`: Registration function - `logout()`: Logout function - `client`: OneBackendClient instance #### `useData(resourceType)` Manages CRUD operations for a specific resource type. **Returns:** - `data`: Array of records - `loading`: Boolean indicating loading state - `error`: Error message or null - `pagination`: Pagination info - `fetchData(page?, limit?, filters?)`: Fetch data function - `createRecord(request)`: Create new record - `updateRecord(id, request)`: Update existing record - `deleteRecord(id)`: Delete single record - `deleteRecords(ids)`: Batch delete multiple records - `ids`: Array of record IDs (max 100) - Returns: `{ success: boolean, deleted: number, failed: number, details: any[] }` - `refresh()`: Refresh data #### `useFileUpload()` Handles file uploads to S3. **Returns:** - `uploadFile(file)`: Upload file function - `getDownloadUrl(fileId)`: Get download URL - `deleteFile(fileId)`: Delete file - `uploading`: Boolean indicating upload state - `error`: Error message or null ### Client #### `OneBackendClient` Direct API client for advanced usage. ```tsx import { useAuth } from '@srfoster/one-backend-react'; function MyComponent() { const { client } = useAuth(); // Use client methods directly const handleCustomRequest = async () => { const response = await client.getRecords('custom-resource'); // ... }; } ``` ## Configuration ### Environment Setup You just need: 1. **App ID**: A unique identifier for your application (e.g., `my-app-name`) The API connection is handled automatically by the package - no configuration needed! ### TypeScript Support This package is written in TypeScript and provides full type definitions. --- ## For One Backend Maintainers ### Updating the API URL When the One Backend is deployed to a new AWS region or account, the API Gateway URL will change. To update the React package automatically: 1. **Run the automated update script**: ```bash cd react-package npm run update-api-url ``` This script will: - Fetch the current API URL from your serverless deployment - Automatically update `src/api/client.ts` with the new endpoint - Confirm the update was successful 2. **Build and publish the updated package**: ```bash npm run build npm version patch # or minor/major as appropriate npm publish --access public ``` ### Manual API URL Update (if needed) If the automated script fails, you can manually update the API URL: 1. **Get the API URL** from your serverless deployment: ```bash cd /path/to/one-backend npx serverless info ``` Look for any endpoint in the output (e.g., `https://abc123xyz.execute-api.us-west-2.amazonaws.com/dev`) 2. **Update the client configuration**: Edit `src/api/client.ts` and update the hardcoded URL: ```typescript private static readonly API_URL = 'https://your-new-endpoint.execute-api.region.amazonaws.com/dev'; ``` This ensures all React package users automatically connect to the correct backend without needing to configure URLs. ## License MIT