JavaScript Client
The Lumenize JavaScript client provides a clean, type-safe way to interact with your Lumenize backend from any JavaScript or TypeScript environment.
Installation
npm install @lumenize/client
Quick Start
import { LumenizeClient } from '@lumenize/client';
const client = new LumenizeClient({ projectId: 'proj_abc123', apiKey: 'lum_live_xyz789', environment: 'production' // 'development', 'staging', or 'production'});
// Create a new userconst user = await client.users.create({ name: 'John Doe', email: 'john@example.com'});
// Fetch all usersconst users = await client.users.findMany();
// Update a userconst updatedUser = await client.users.update(user.id, { name: 'John Smith'});
Configuration
Basic Configuration
const client = new LumenizeClient({ projectId: 'proj_abc123', // Your project ID apiKey: 'lum_live_xyz789', // Your API key environment: 'production', // Target environment baseUrl: 'https://api.lumenize.dev', // Optional: custom base URL timeout: 5000, // Request timeout in ms retries: 3, // Number of retry attempts retryDelay: 1000 // Delay between retries in ms});
TypeScript Configuration
import { LumenizeClient, User, Task } from '@lumenize/client';
// Fully typed clientconst client = new LumenizeClient({ projectId: 'proj_abc123', apiKey: 'lum_live_xyz789'});
// Type-safe operationsconst user: User = await client.users.create({ name: 'John Doe', email: 'john@example.com'});
const tasks: Task[] = await client.tasks.findMany({ where: { assignedTo: user.id }});
CRUD Operations
Create
// Create a single entityconst user = await client.users.create({ name: 'Alice Johnson', email: 'alice@example.com', role: 'admin'});
// Create multiple entitiesconst users = await client.users.createMany([ { name: 'Bob Wilson', email: 'bob@example.com' }, { name: 'Carol Brown', email: 'carol@example.com' }]);
Read
// Find by IDconst user = await client.users.findById('user_123');
// Find with filtersconst adminUsers = await client.users.findMany({ where: { role: 'admin' }});
// Find with complex queriesconst recentTasks = await client.tasks.findMany({ where: { createdAt: { gte: new Date('2024-01-01') }, status: { in: ['pending', 'in_progress'] } }, orderBy: { createdAt: 'desc' }, limit: 10, offset: 0});
// Find with relationshipsconst userWithTasks = await client.users.findById('user_123', { include: { tasks: true }});
Update
// Update by IDconst updatedUser = await client.users.update('user_123', { name: 'New Name', email: 'newemail@example.com'});
// Update multiple entitiesconst result = await client.users.updateMany( { where: { role: 'guest' } }, { role: 'user' });
// Partial updatesconst task = await client.tasks.update('task_123', { status: 'completed', completedAt: new Date()});
Delete
// Delete by IDawait client.users.delete('user_123');
// Soft delete (if enabled in schema)await client.users.delete('user_123', { soft: true });
// Delete multiple entitiesconst result = await client.users.deleteMany({ where: { lastLoginAt: { lt: new Date('2023-01-01') } }});
// Restore soft-deleted entityawait client.users.restore('user_123');
Query Builder
Advanced Filtering
// Complex where conditionsconst tasks = await client.tasks.findMany({ where: { AND: [ { status: 'pending' }, { priority: { in: ['high', 'urgent'] } }, { OR: [ { dueDate: { lt: new Date() } }, { assignedTo: 'user_123' } ] } ] }});
// Text searchconst users = await client.users.findMany({ where: { name: { contains: 'john' } }});
// Numeric comparisonsconst expensiveTasks = await client.tasks.findMany({ where: { budget: { gte: 1000, lte: 5000 } }});
Sorting and Pagination
// Multiple sort fieldsconst tasks = await client.tasks.findMany({ orderBy: [ { priority: 'desc' }, { createdAt: 'asc' } ], limit: 20, offset: 40});
// Cursor-based paginationconst tasks = await client.tasks.findMany({ orderBy: { createdAt: 'desc' }, limit: 10, cursor: 'task_456'});
Field Selection
// Select specific fieldsconst users = await client.users.findMany({ select: ['id', 'name', 'email']});
// Include relationshipsconst userWithDetails = await client.users.findById('user_123', { include: { tasks: { where: { status: 'pending' }, orderBy: { dueDate: 'asc' } }, profile: true }});
Real-time Subscriptions
WebSocket Connection
// Establish WebSocket connectionawait client.connect();
// Subscribe to entity changesconst unsubscribe = client.tasks.subscribe('*', (event) => { console.log(`Task ${event.id} was ${event.type}d:`, event.data);});
// Subscribe to specific entityclient.users.subscribe('user_123', (event) => { console.log('User updated:', event.data);});
// Subscribe with filtersclient.tasks.subscribe({ where: { assignedTo: 'user_123' }}, (tasks) => { console.log('My tasks updated:', tasks);});
// Cleanupunsubscribe();await client.disconnect();
Event Types
client.tasks.subscribe('*', (event) => { switch (event.type) { case 'created': console.log('New task:', event.data); break; case 'updated': console.log('Task updated:', event.data); break; case 'deleted': console.log('Task deleted:', event.id); break; }});
Authentication
User Authentication
// Login with email/passwordconst session = await client.auth.login({ email: 'user@example.com', password: 'password123'});
// Login with OAuth providerconst session = await client.auth.loginWithProvider('google');
// Get current userconst currentUser = await client.auth.getCurrentUser();
// Logoutawait client.auth.logout();
Session Management
// Check authentication statusconst isAuthenticated = client.auth.isAuthenticated();
// Refresh tokenawait client.auth.refreshToken();
// Handle session eventsclient.auth.onSessionChange((session) => { if (session) { console.log('User logged in:', session.user); } else { console.log('User logged out'); }});
Error Handling
Try-Catch Pattern
try { const user = await client.users.create({ name: 'John Doe', email: 'invalid-email' });} catch (error) { if (error.code === 'VALIDATION_ERROR') { console.log('Validation failed:', error.details); } else if (error.code === 'NETWORK_ERROR') { console.log('Network error:', error.message); } else { console.log('Unknown error:', error); }}
Global Error Handler
client.onError((error) => { console.error('Lumenize error:', error);
// Send to error tracking service errorTracker.captureException(error);});
Caching
Automatic Caching
const client = new LumenizeClient({ projectId: 'proj_abc123', apiKey: 'lum_live_xyz789', cache: { enabled: true, ttl: 300000, // 5 minutes maxSize: 1000 // Maximum cached items }});
// Subsequent calls use cacheconst user1 = await client.users.findById('user_123'); // Fetches from APIconst user2 = await client.users.findById('user_123'); // Returns from cache
Cache Control
// Bypass cache for fresh dataconst freshUser = await client.users.findById('user_123', { cache: false});
// Invalidate cacheclient.cache.invalidate('users', 'user_123');
// Clear all cacheclient.cache.clear();
Middleware
Request Middleware
// Add request interceptorclient.interceptors.request.use((request) => { // Add custom headers request.headers['X-Custom-Header'] = 'value';
// Log requests console.log('Request:', request.method, request.url);
return request;});
// Add response interceptorclient.interceptors.response.use( (response) => { console.log('Response:', response.status, response.data); return response; }, (error) => { console.error('Response error:', error); throw error; });
Custom Middleware
// Create custom middlewareconst loggingMiddleware = (next) => async (request) => { const start = Date.now(); const response = await next(request); const duration = Date.now() - start;
console.log(`${request.method} ${request.url} - ${duration}ms`); return response;};
// Apply middlewareclient.use(loggingMiddleware);
Advanced Features
Batch Operations
// Batch multiple operationsconst batch = client.batch();
batch.users.create({ name: 'User 1', email: 'user1@example.com' });batch.users.create({ name: 'User 2', email: 'user2@example.com' });batch.tasks.update('task_123', { status: 'completed' });
// Execute batchconst results = await batch.execute();
Transactions
// Perform operations in a transactionconst result = await client.transaction(async (tx) => { const user = await tx.users.create({ name: 'John Doe', email: 'john@example.com' });
const task = await tx.tasks.create({ title: 'Welcome task', assignedTo: user.id });
return { user, task };});
File Uploads
// Upload fileconst file = document.getElementById('fileInput').files[0];
const uploadResult = await client.files.upload(file, { folder: 'avatars', public: true});
// Update user with avatar URLawait client.users.update('user_123', { avatar: uploadResult.url});
Node.js Specific Features
Server-Side Usage
// Node.js server usageimport { LumenizeClient } from '@lumenize/node';
const client = new LumenizeClient({ projectId: process.env.LUMENIZE_PROJECT_ID, apiKey: process.env.LUMENIZE_API_KEY});
// Server-side operationsapp.get('/api/users', async (req, res) => { try { const users = await client.users.findMany(); res.json(users); } catch (error) { res.status(500).json({ error: error.message }); }});
Environment Variables
# .env fileLUMENIZE_PROJECT_ID=proj_abc123LUMENIZE_API_KEY=lum_live_xyz789LUMENIZE_ENVIRONMENT=production
Best Practices
Error Handling
// âś… Good: Specific error handlingtry { await client.users.create(userData);} catch (error) { switch (error.code) { case 'VALIDATION_ERROR': return handleValidationError(error); case 'PERMISSION_DENIED': return handlePermissionError(error); default: return handleGenericError(error); }}
Performance
// âś… Good: Use field selection for large objectsconst users = await client.users.findMany({ select: ['id', 'name', 'email'], // Only fetch needed fields limit: 100});
// âś… Good: Use pagination for large datasetsconst getAllUsers = async () => { const users = []; let cursor = null;
do { const page = await client.users.findMany({ limit: 100, cursor });
users.push(...page.data); cursor = page.nextCursor; } while (cursor);
return users;};
Security
// âś… Good: Never expose API keys in client-side codeconst client = new LumenizeClient({ projectId: 'proj_abc123', // Use public key for client-side apiKey: 'lum_pub_xyz789'});
// âś… Good: Validate data before sendingconst createUser = async (userData) => { if (!userData.email || !isValidEmail(userData.email)) { throw new Error('Valid email is required'); }
return client.users.create(userData);};
Self-Hosted Usage
For self-hosted Lumenize instances, you can use the client directly with your Durable Objects:
Basic Setup
import { LumenizeClient } from "lumenize";
const client = new LumenizeClient({ agent: "mylumenizeserver", // Your server class name in kebab-case name: "instance-name", // Specific Durable Object instance host: window.location.host, // Or your server host timeout: 5000, // Request timeout in ms (default: 5000) route: "jsonrpc" // Endpoint route (default: 'jsonrpc')});
Making Method Calls
The client provides multiple ways to call your server methods:
// Generic call methodconst result = await client.call("subtract", [42, 23]);
// Direct method calls (auto-generated based on your server methods)const result = await client.subtract(42, 23);
// Named parametersconst result = await client.subtract({ minuend: 42, subtrahend: 23 });
// Mixed parameter order (named params)const result = await client.subtract({ subtrahend: 23, minuend: 42 });
console.log(result); // 19
HTTP Requests (Manual)
For environments where you can’t use the client library:
Single Request
const response = await fetch("https://your-worker.your-subdomain.workers.dev/agents/mylumenizeserver/instance-name/jsonrpc", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ jsonrpc: "2.0", method: "subtract", params: [42, 23], id: 1 })});
const result = await response.json();console.log(result); // { jsonrpc: '2.0', result: 19, id: 1 }
Batch Requests
JSON-RPC 2.0 supports batch operations:
const response = await fetch("https://your-worker.your-subdomain.workers.dev/agents/mylumenizeserver/instance-name/jsonrpc", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify([ { jsonrpc: "2.0", method: "subtract", params: [42, 23], id: 1 }, { jsonrpc: "2.0", method: "subtract", params: [23, 42], id: 2 }, { jsonrpc: "2.0", method: "getUserProfile", params: ["user123"], id: 3 } ])});
const results = await response.json();// Array of responses with matching IDs
WebSocket Connections
Using LumenizeClient
const client = new LumenizeClient({ agent: "mylumenizeserver", name: "instance-name", host: "your-worker.your-subdomain.workers.dev", transport: "websocket" // Use WebSocket instead of HTTP});
// Same API as HTTP clientconst result = await client.subtract(42, 23);
// Subscribe to real-time updatesawait client.subscribe("team-updates", (data) => { console.log("Team updated:", data);});
Manual WebSocket
const ws = new WebSocket("wss://your-worker.your-subdomain.workers.dev/agents/mylumenizeserver/instance-name");
ws.onopen = () => { // Send JSON-RPC request wrapped in envelope ws.send(JSON.stringify({ type: "jsonrpc", payload: { jsonrpc: "2.0", method: "subtract", params: [42, 23], id: 1 } }));};
ws.onmessage = (event) => { const envelope = JSON.parse(event.data); if (envelope.type === "jsonrpc") { console.log("Response:", envelope.payload); }};
Framework Integration
Svelte Integration
<script> import { useLumenize } from "lumenize/svelte";
const { data, loading, error, call } = useLumenize({ agent: "mylumenizeserver", name: "instance-name", host: "your-worker.your-subdomain.workers.dev" });
async function handleCalculation() { const result = await call("subtract", [42, 23]); console.log("Result:", result); }</script>
{#if loading} <p>Connecting...</p>{:else if error} <p>Error: {error.message}</p>{:else} <button on:click={handleCalculation}>Calculate</button>
<!-- Reactive data updates automatically --> {#if $data.teamMetrics} <div>Team Score: {$data.teamMetrics.score}</div> {/if}{/if}
React Integration (Coming Soon)
import { useLumenize } from "lumenize/react";
function MyComponent() { const { data, loading, error, call } = useLumenize({ agent: "mylumenizeserver", name: "instance-name", host: "your-worker.your-subdomain.workers.dev" });
const handleCalculation = async () => { const result = await call("subtract", [42, 23]); console.log("Result:", result); };
if (loading) return <div>Connecting...</div>; if (error) return <div>Error: {error.message}</div>;
return ( <div> <button onClick={handleCalculation}>Calculate</button> {data.teamMetrics && ( <div>Team Score: {data.teamMetrics.score}</div> )} </div> );}
Advanced Self-Hosted Features
Shared Worker (Browser)
The reactive clients automatically use SharedWorker for WebSocket multiplexing across browser tabs:
// Automatically shared across all tabs on the same originconst client = new LumenizeClient({ agent: "mylumenizeserver", name: "instance-name", useSharedWorker: true // Default in browser environments});
Custom Storage Reactivity
Build your own reactive layer using the storage-based core:
import { LumenizeStorage } from "lumenize/storage";
const storage = new LumenizeStorage("mylumenizeserver");
// Listen for changesstorage.addEventListener("change", (event) => { console.log("Data updated:", event.detail); // Update your UI framework's reactive state});
// Trigger updatesawait storage.set("teamMetrics", { score: 95 });
Next Steps
- React Client - React-specific hooks and components
- Concepts Overview - Learn about real-time features and MCP integration
- Access Control - Understand ReBAC permissions
- Authentication - User authentication and sessions
- Real-time Features - WebSocket subscriptions