diff --git a/README.md b/README.md index e0d919e..ce0b83d 100644 --- a/README.md +++ b/README.md @@ -1,634 +1,81 @@ # Base44 JavaScript SDK -A modern JavaScript SDK for interacting with the Base44 API. Designed to work with both JavaScript and TypeScript projects. +The Base44 SDK provides a JavaScript interface for building apps on the Base44 platform. When Base44 generates your app, the generated code uses the SDK to authenticate users, manage your app's data, interact with AI agents, and more. You can then use the same SDK to modify and extend your app. -## Installation +## Modules -```bash -npm install @base44/sdk -# or -yarn add @base44/sdk -``` - -## Usage - -### Basic Setup - -```javascript -import { createClient } from '@base44/sdk'; - -// Create a client instance -const base44 = createClient({ - serverUrl: 'https://base44.app', // Optional, defaults to 'https://base44.app' - appId: 'your-app-id', // Required - token: 'your-user-token', // Optional, for user authentication - serviceToken: 'your-service-token', // Optional, for service role authentication - autoInitAuth: true, // Optional, defaults to true - auto-detects tokens from URL or localStorage -}); -``` - -### Working with Entities - -```javascript -// List all products -const products = await base44.entities.Product.list(); - -// Filter products by category -const filteredProducts = await base44.entities.Product.filter({ - category: ['electronics', 'computers'] -}); - -// Get a specific product -const product = await base44.entities.Product.get('product-id'); - -// Create a new product -const newProduct = await base44.entities.Product.create({ - name: 'New Product', - price: 99.99, - category: 'electronics' -}); - -// Update a product -const updatedProduct = await base44.entities.Product.update('product-id', { - price: 89.99 -}); - -// Delete a product -await base44.entities.Product.delete('product-id'); - -// Bulk create products -const newProducts = await base44.entities.Product.bulkCreate([ - { name: 'Product 1', price: 19.99 }, - { name: 'Product 2', price: 29.99 } -]); -``` - -### Service Role Authentication - -Service role authentication allows server-side applications to perform operations with elevated privileges. This is useful for administrative tasks, background jobs, and server-to-server communication. - -```javascript -import { createClient } from '@base44/sdk'; - -// Create a client with service role token -const base44 = createClient({ - appId: 'your-app-id', - token: 'user-token', // For user operations - serviceToken: 'service-token' // For service role operations -}); - -// User operations (uses user token) -const userEntities = await base44.entities.User.list(); - -// Service role operations (uses service token) -const allEntities = await base44.asServiceRole.entities.User.list(); - -// Service role has access to: -// - base44.asServiceRole.entities -// - base44.asServiceRole.integrations -// - base44.asServiceRole.functions -// Note: Service role does NOT have access to auth module for security - -// If no service token is provided, accessing asServiceRole throws an error -const clientWithoutService = createClient({ appId: 'your-app-id' }); -try { - await clientWithoutService.asServiceRole.entities.User.list(); -} catch (error) { - // Error: Service token is required to use asServiceRole -} -``` - -### Server-Side Usage - -For server-side applications, you can create a client from incoming HTTP requests: - -```javascript -import { createClientFromRequest } from '@base44/sdk'; - -// In your server handler (Express, Next.js, etc.) -app.get('/api/data', async (req, res) => { - try { - // Extract client configuration from request headers - const base44 = createClientFromRequest(req); - - // Headers used: - // - Authorization: Bearer - // - Base44-Service-Authorization: Bearer - // - Base44-App-Id: - // - Base44-Api-Url: (optional) - - // Use appropriate authentication based on available tokens - let data; - if (base44.asServiceRole) { - // Service token available - use elevated privileges - data = await base44.asServiceRole.entities.SensitiveData.list(); - } else { - // Only user token available - use user permissions - data = await base44.entities.PublicData.list(); - } - - res.json(data); - } catch (error) { - res.status(500).json({ error: error.message }); - } -}); -``` - -### Working with Integrations - -```javascript -// Send an email using the Core integration -const emailResult = await base44.integrations.Core.SendEmail({ - to: 'user@example.com', - subject: 'Hello from Base44', - body: 'This is a test email sent via the Base44 SDK' -}); - -// Use a custom integration -const result = await base44.integrations.CustomPackage.CustomEndpoint({ - param1: 'value1', - param2: 'value2' -}); - -// Upload a file -const fileInput = document.querySelector('input[type="file"]'); -const file = fileInput.files[0]; -const uploadResult = await base44.integrations.Core.UploadFile({ - file, - metadata: { type: 'profile-picture' } -}); -``` - -## Authentication - -The SDK provides comprehensive authentication capabilities to help you build secure applications. - -### Creating an Authenticated Client - -To create a client with authentication: - -```javascript -import { createClient } from '@base44/sdk'; -import { getAccessToken } from '@base44/sdk/utils/auth-utils'; - -// Create a client with authentication -const base44 = createClient({ - appId: 'your-app-id', - token: getAccessToken() // Automatically retrieves token from localStorage or URL -}); - -// Check authentication status -const isAuthenticated = await base44.auth.isAuthenticated(); -console.log('Authenticated:', isAuthenticated); - -// Get current user information (requires authentication) -if (isAuthenticated) { - const user = await base44.auth.me(); - console.log('Current user:', user); -} -``` - -### Login and Logout - -```javascript -import { createClient } from '@base44/sdk'; -import { getAccessToken, saveAccessToken, removeAccessToken } from '@base44/sdk/utils/auth-utils'; - -const base44 = createClient({ appId: 'your-app-id' }); - -// Redirect to the login page -// This will redirect to: base44.app/login?from_url=http://your-app.com/dashboard&app_id=your-app-id -function handleLogin() { - base44.auth.login('/dashboard'); -} +The SDK provides access to Base44's functionality through the following modules: -// Handle successful login (on return from Base44 login) -function handleLoginReturn() { - const token = getAccessToken(); - if (token) { - console.log('Successfully logged in with token:', token); - // The token is automatically saved to localStorage and removed from URL - } -} +- **[`agents`](https://docs.base44.com/sdk-docs/interfaces/agents)**: Interact with AI agents and manage conversations. +- **[`app-logs`](https://docs.base44.com/sdk-docs/interfaces/app-logs)**: Access and query app logs. +- **[`auth`](https://docs.base44.com/sdk-docs/interfaces/auth)**: Manage user authentication, registration, and session handling. +- **[`connectors`](https://docs.base44.com/sdk-docs/interfaces/connectors)**: Manage OAuth connections and access tokens for third-party services. +- **[`entities`](https://docs.base44.com/sdk-docs/interfaces/entities)**: Work with your app's data entities using CRUD operations. +- **[`functions`](https://docs.base44.com/sdk-docs/interfaces/functions)**: Execute backend functions. +- **[`integrations`](https://docs.base44.com/sdk-docs/type-aliases/integrations)**: Access third-party integrations. -// Logout -function handleLogout() { - removeAccessToken(); - window.location.href = '/login'; -} -``` - -### Real-World Authentication Example (React) - -Here's a complete example of implementing Base44 authentication in a React application: - -```jsx -import React, { createContext, useContext, useEffect, useState } from 'react'; -import { Navigate, Outlet, Route, Routes, useLocation } from 'react-router-dom'; -import { createClient } from '@base44/sdk'; -import { getAccessToken, removeAccessToken } from '@base44/sdk/utils/auth-utils'; - -// Create AuthContext -const AuthContext = createContext(null); - -// Auth Provider Component -function AuthProvider({ children }) { - const [user, setUser] = useState(null); - const [loading, setLoading] = useState(true); - const [client] = useState(() => - createClient({ - appId: 'your-app-id', - token: getAccessToken() - }) - ); - - useEffect(() => { - async function loadUser() { - try { - const isAuth = await client.auth.isAuthenticated(); - if (isAuth) { - const userData = await client.auth.me(); - setUser(userData); - } - } catch (error) { - console.error('Authentication error:', error); - } finally { - setLoading(false); - } - } - - loadUser(); - }, [client]); - - const login = () => { - client.auth.login(window.location.pathname); - }; - - const logout = () => { - removeAccessToken(); - setUser(null); - window.location.href = '/login'; - }; - - return ( - - {children} - - ); -} - -// Custom hook to use auth context -function useAuth() { - const context = useContext(AuthContext); - if (!context) { - throw new Error('useAuth must be used within an AuthProvider'); - } - return context; -} - -// Protected Route Component -function ProtectedRoute() { - const { user, loading, login } = useAuth(); - const location = useLocation(); - - // Check if we're returning from login with a token in URL - useEffect(() => { - const token = getAccessToken(); // This will save token from URL if present - if (token && !user && !loading) { - window.location.reload(); // Reload to apply the new token - } - }, [location, user, loading]); - - if (loading) { - return
Loading...
; - } - - if (!user) { - // If not authenticated, redirect to login - login(); - return
Redirecting to login...
; - } +## Example - // If authenticated, render the child routes - return ; -} - -// Dashboard Component (protected) -function Dashboard() { - const { user, client, logout } = useAuth(); - const [todos, setTodos] = useState([]); - const [loading, setLoading] = useState(true); - - useEffect(() => { - async function loadTodos() { - try { - // Load user-specific data using the SDK - const items = await client.entities.Todo.filter({ - assignee: user.id - }); - setTodos(items); - } catch (error) { - console.error('Failed to load todos:', error); - } finally { - setLoading(false); - } - } - - loadTodos(); - }, [client, user]); - - return ( -
-

Welcome, {user.name}!

- - -

Your Todos

- {loading ? ( -
Loading todos...
- ) : ( -
    - {todos.map(todo => ( -
  • {todo.title}
  • - ))} -
- )} -
- ); -} - -// Login Page -function LoginPage() { - const { login, user } = useAuth(); - - if (user) { - return ; - } - - return ( -
-

Login Required

- -
- ); -} - -// App Component -function App() { - return ( - - - } /> - }> - } /> - } /> - {/* Add more protected routes here */} - - } /> - - - ); -} -``` - -## TypeScript Support - -This SDK includes TypeScript definitions out of the box: +Here's a quick look at working with data in the SDK, using the `entities` module to create, update, and list records. In this example, we're working with a custom `Task` entity: ```typescript -import { createClient, Base44Error } from '@base44/sdk'; -import type { Entity, Base44Client, AuthModule } from '@base44/sdk'; +import { base44 } from "@/api/base44Client"; -// Create a typed client -const base44: Base44Client = createClient({ - appId: 'your-app-id' +// Create a new task +const newTask = await base44.entities.Task.create({ + title: "Complete project documentation", + status: "pending", + dueDate: "2024-12-31", }); -// Using the entities module with type safety -async function fetchProducts() { - try { - const products: Entity[] = await base44.entities.Product.list(); - console.log(products.map(p => p.name)); - - const product: Entity = await base44.entities.Product.get('product-id'); - console.log(product.name); - } catch (error) { - if (error instanceof Base44Error) { - console.error(`Error ${error.status}: ${error.message}`); - } - } -} - -// Service role operations with TypeScript -async function adminOperations() { - const base44 = createClient({ - appId: 'your-app-id', - serviceToken: 'service-token' - }); - - // TypeScript knows asServiceRole requires a service token - try { - const allUsers: Entity[] = await base44.asServiceRole.entities.User.list(); - console.log(`Total users: ${allUsers.length}`); - } catch (error) { - if (error instanceof Error) { - console.error(error.message); // Service token is required to use asServiceRole - } - } -} - -// Authentication with TypeScript -async function handleAuth(auth: AuthModule) { - // Check authentication - const isAuthenticated: boolean = await auth.isAuthenticated(); - - if (isAuthenticated) { - // Get user info - const user: Entity = await auth.me(); - console.log(`Logged in as: ${user.name}, Role: ${user.role}`); - - // Update user - const updatedUser: Entity = await auth.updateMe({ - preferences: { theme: 'dark' } - }); - } else { - // Redirect to login - auth.login('/dashboard'); - } -} - -// Execute with proper typing -handleAuth(base44.auth); -``` - -### Advanced TypeScript Usage - -You can define your own entity interfaces for better type safety: - -```typescript -// Define custom entity interfaces -interface User extends Entity { - name: string; - email: string; - role: 'admin' | 'editor' | 'viewer'; - preferences?: { - theme: 'light' | 'dark'; - notifications: boolean; - }; -} - -interface Product extends Entity { - name: string; - price: number; - category: string; - inStock: boolean; -} - -// Use your custom interfaces with the SDK -async function getLoggedInUser(): Promise { - const base44 = createClient({ appId: 'your-app-id' }); - - try { - const user = await base44.auth.me() as User; - return user; - } catch (error) { - console.error('Failed to get user:', error); - return null; - } -} - -// Use with React hooks -function useBase44User() { - const [user, setUser] = useState(null); - const [loading, setLoading] = useState(true); - - useEffect(() => { - const base44 = createClient({ appId: 'your-app-id' }); - - async function fetchUser() { - try { - const userData = await base44.auth.me() as User; - setUser(userData); - } catch (error) { - console.error('Auth error:', error); - } finally { - setLoading(false); - } - } - - fetchUser(); - }, []); - - return { user, loading }; -} -``` - -## Error Handling - -The SDK provides a custom `Base44Error` class for error handling: - -```javascript -import { createClient, Base44Error } from '@base44/sdk'; - -const base44 = createClient({ appId: 'your-app-id' }); +// Update the task +await base44.entities.Task.update(newTask.id, { + status: "in-progress", +}); -try { - const result = await base44.entities.NonExistentEntity.list(); -} catch (error) { - if (error instanceof Base44Error) { - console.error(`Status: ${error.status}`); - console.error(`Message: ${error.message}`); - console.error(`Code: ${error.code}`); - console.error(`Data: ${JSON.stringify(error.data)}`); - } else { - console.error('Unexpected error:', error); - } -} +// List all tasks +const tasks = await base44.entities.Task.list(); ``` -## Functions +## Learn more -The SDK supports invoking custom functions: +For complete documentation, guides, and API reference, visit the **[Base44 SDK Documentation](https://docs.base44.com/sdk-getting-started/overview)**. -```javascript -// Invoke a function without parameters -const result = await base44.functions.myFunction(); +## Development -// Invoke a function with parameters -const result = await base44.functions.calculateTotal({ - items: ['item1', 'item2'], - discount: 0.1 -}); +### Build the SDK -// Functions are automatically authenticated with the user token -// Service role can also invoke functions -const serviceResult = await base44.asServiceRole.functions.adminFunction(); +```bash +npm install +npm run build ``` -## Testing - -The SDK includes comprehensive tests to ensure reliability. - -### Running Tests +### Run tests ```bash # Run all tests npm test -# Run unit tests only (no API calls) +# Run unit tests only npm run test:unit -# Run end-to-end tests (requires API access) -npm run test:e2e - -# Run tests with coverage report +# Run with coverage npm run test:coverage ``` -### Setting Up E2E Tests - -E2E tests require access to a Base44 API. To run these tests: - -1. Copy `tests/.env.example` to `tests/.env` -2. Fill in your Base44 API credentials in the `.env` file: - ``` - BASE44_SERVER_URL=https://base44.app - BASE44_APP_ID=your_app_id_here - BASE44_AUTH_TOKEN=your_auth_token_here - ``` - -3. Optionally, set `SKIP_E2E_TESTS=true` to skip E2E tests. - -### Writing Your Own Tests - -You can use the provided test utilities for writing your own tests: - -```javascript -const { createClient } = require('@base44/sdk'); -const { getTestConfig } = require('@base44/sdk/tests/utils/test-config'); - -describe('My Tests', () => { - let base44; - - beforeAll(() => { - const config = getTestConfig(); - base44 = createClient({ - serverUrl: config.serverUrl, - appId: config.appId, - }); - - if (config.token) { - base44.setToken(config.token); - } - }); - - test('My test', async () => { - const todos = await base44.entities.Todo.filter({}, 10); - expect(Array.isArray(todos)).toBe(true); - expect(todos.length).toBeGreaterThan(0); - }); -}); +For E2E tests, create a `tests/.env` file with: ``` +BASE44_APP_ID=your_app_id +BASE44_AUTH_TOKEN=your_auth_token +``` + +### Generate documentation -## License +Generate API documentation locally: -MIT \ No newline at end of file +```bash +# Process and preview locally +npm run create-docs +cd docs +mintlify dev +``` \ No newline at end of file