Framework Integration
Framework Integration
Section titled “Framework Integration”The OstrichDB SDK works seamlessly with popular JavaScript frameworks and libraries. This guide provides comprehensive examples and best practices for integrating OstrichDB into your applications.
Express.js Integration
Section titled “Express.js Integration”Basic Setup
Section titled “Basic Setup”import express from 'express';import OstrichDB from 'ostrichdb-js';
const app = express();app.use(express.json());
// Database configurationconst db = new OstrichDB({ baseUrl: process.env.OSTRICHDB_URL || 'http://localhost:8042', apiKey: process.env.OSTRICHDB_TOKEN, timeout: 15000});
// Health check endpointapp.get('/health', async (req, res) => { try { await db.health_check(); res.json({ status: 'healthy', database: 'connected' }); } catch (error) { res.status(503).json({ status: 'unhealthy', error: error.message }); }});
const PORT = process.env.PORT || 3000;app.listen(PORT, () => { console.log(`Server running on port ${PORT}`);});
User Management API
Section titled “User Management API”import express from 'express';import OstrichDB from 'ostrichdb-js';
const router = express.Router();
// Middleware to ensure database connectionfunction requireDatabase(req: any, res: any, next: any) { const db = new OstrichDB({ baseUrl: process.env.OSTRICHDB_URL, apiKey: req.headers.authorization?.replace('Bearer ', '') });
if (!db.apiKey) { return res.status(401).json({ error: 'Authorization token required' }); }
req.db = db; next();}
router.use(requireDatabase);
// Create userrouter.post('/', async (req, res) => { try { const { username, email, age } = req.body; const userId = `user-${Date.now()}`;
const users = req.db.project('app').collection('users').cluster('active');
// Create user records await users.record(`${userId}-username`, 'STRING', username) .create(`${userId}-username`, 'STRING', username); await users.record(`${userId}-email`, 'STRING', email) .create(`${userId}-email`, 'STRING', email); await users.record(`${userId}-age`, 'INTEGER', age.toString()) .create(`${userId}-age`, 'INTEGER', age.toString()); await users.record(`${userId}-created`, 'DATETIME', new Date().toISOString()) .create(`${userId}-created`, 'DATETIME', new Date().toISOString());
res.status(201).json({ success: true, userId, message: 'User created successfully' }); } catch (error) { res.status(500).json({ error: error.message }); }});
// Get userrouter.get('/:userId', async (req, res) => { try { const { userId } = req.params; const users = req.db.project('app').collection('users').cluster('active');
const username = await users.record(`${userId}-username`).get(`${userId}-username`); const email = await users.record(`${userId}-email`).get(`${userId}-email`); const age = await users.record(`${userId}-age`).get(`${userId}-age`); const created = await users.record(`${userId}-created`).get(`${userId}-created`);
res.json({ id: userId, username: username.value, email: email.value, age: parseInt(age.value), created: created.value }); } catch (error) { if (error.statusCode === 404) { res.status(404).json({ error: 'User not found' }); } else { res.status(500).json({ error: error.message }); } }});
// List usersrouter.get('/', async (req, res) => { try { const users = req.db.project('app').collection('users').cluster('active'); const records = await users.listRecords();
// Group records by user ID const userMap = new Map(); records.forEach(record => { const parts = record.name.split('-'); const userId = parts.slice(0, 2).join('-'); const field = parts.slice(2).join('-');
if (!userMap.has(userId)) { userMap.set(userId, { id: userId }); }
userMap.get(userId)[field] = record.value; });
const userList = Array.from(userMap.values()) .filter(user => user.username) // Only complete users .map(user => ({ ...user, age: user.age ? parseInt(user.age) : null }));
res.json({ users: userList, count: userList.length }); } catch (error) { res.status(500).json({ error: error.message }); }});
// Delete userrouter.delete('/:userId', async (req, res) => { try { const { userId } = req.params; const users = req.db.project('app').collection('users').cluster('active');
// Delete all user records await users.record(`${userId}-username`).delete(`${userId}-username`); await users.record(`${userId}-email`).delete(`${userId}-email`); await users.record(`${userId}-age`).delete(`${userId}-age`); await users.record(`${userId}-created`).delete(`${userId}-created`);
res.json({ success: true, message: 'User deleted successfully' }); } catch (error) { res.status(500).json({ error: error.message }); }});
export default router;
Blog API with Express
Section titled “Blog API with Express”import express from 'express';import { z } from 'zod'; // For validationimport OstrichDB from 'ostrichdb-js';
const router = express.Router();
// Validation schemasconst createPostSchema = z.object({ title: z.string().min(1).max(200), content: z.string().min(1), author: z.string().min(1).max(100), tags: z.array(z.string()).optional().default([])});
// Database middlewarerouter.use((req: any, res, next) => { req.db = new OstrichDB({ baseUrl: process.env.OSTRICHDB_URL, apiKey: req.headers.authorization?.replace('Bearer ', ''), timeout: 10000 }); next();});
// Create blog postrouter.post('/posts', async (req, res) => { try { const postData = createPostSchema.parse(req.body); const postId = `post-${Date.now()}`;
const posts = req.db.project('blog').collection('content').cluster('published');
// Create post using builder pattern await posts.record(`${postId}-title`, 'STRING', postData.title) .create(`${postId}-title`, 'STRING', postData.title); await posts.record(`${postId}-content`, 'STRING', postData.content) .create(`${postId}-content`, 'STRING', postData.content); await posts.record(`${postId}-author`, 'STRING', postData.author) .create(`${postId}-author`, 'STRING', postData.author); await posts.record(`${postId}-tags`, '[]STRING', JSON.stringify(postData.tags)) .create(`${postId}-tags`, '[]STRING', JSON.stringify(postData.tags)); await posts.record(`${postId}-published`, 'DATETIME', new Date().toISOString()) .create(`${postId}-published`, 'DATETIME', new Date().toISOString());
res.status(201).json({ success: true, postId, title: postData.title }); } catch (error) { if (error instanceof z.ZodError) { res.status(400).json({ error: 'Validation failed', details: error.errors }); } else { res.status(500).json({ error: error.message }); } }});
// Get blog posts with paginationrouter.get('/posts', async (req, res) => { try { const page = parseInt(req.query.page as string) || 1; const limit = parseInt(req.query.limit as string) || 10; const offset = (page - 1) * limit;
const posts = req.db.project('blog').collection('content').cluster('published');
// Get paginated records const records = await posts.searchRecords({ sortBy: 'name', sortOrder: 'desc', limit, offset });
// Group by post ID and build complete posts const postMap = new Map(); records.forEach(record => { const parts = record.name.split('-'); const postId = parts.slice(0, 2).join('-'); const field = parts.slice(2).join('-');
if (!postMap.has(postId)) { postMap.set(postId, { id: postId }); }
if (field === 'tags') { postMap.get(postId)[field] = JSON.parse(record.value); } else { postMap.get(postId)[field] = record.value; } });
const postList = Array.from(postMap.values()) .filter(post => post.title && post.content);
res.json({ posts: postList, pagination: { page, limit, hasMore: postList.length === limit } }); } catch (error) { res.status(500).json({ error: error.message }); }});
export default router;
Next.js Integration
Section titled “Next.js Integration”App Router API Routes
Section titled “App Router API Routes”import { NextRequest, NextResponse } from 'next/server';import OstrichDB from 'ostrichdb-js';
// Initialize database clientfunction createDBClient(request: NextRequest) { const token = request.headers.get('authorization')?.replace('Bearer ', '');
return new OstrichDB({ baseUrl: process.env.OSTRICHDB_URL!, apiKey: token || process.env.OSTRICHDB_TOKEN!, timeout: 10000 });}
export async function GET(request: NextRequest) { try { const db = createDBClient(request); const { searchParams } = new URL(request.url); const category = searchParams.get('category');
const products = db.project('ecommerce').collection('inventory').cluster('products');
let records; if (category) { records = await products.searchRecords({ search: 'category', valueContains: category, limit: 50 }); } else { records = await products.listRecords(); }
// Group records by product ID const productMap = new Map(); records.forEach(record => { const parts = record.name.split('-'); const productId = parts.slice(0, 2).join('-'); const field = parts.slice(2).join('-');
if (!productMap.has(productId)) { productMap.set(productId, { id: productId }); }
productMap.get(productId)[field] = record.value; });
const productList = Array.from(productMap.values()) .filter(product => product.name && product.price) .map(product => ({ ...product, price: parseFloat(product.price) }));
return NextResponse.json({ products: productList }); } catch (error) { console.error('Products API error:', error); return NextResponse.json( { error: 'Failed to fetch products' }, { status: 500 } ); }}
export async function POST(request: NextRequest) { try { const db = createDBClient(request); const body = await request.json();
const { name, price, category, description } = body;
// Validation if (!name || !price || !category) { return NextResponse.json( { error: 'Name, price, and category are required' }, { status: 400 } ); }
const productId = `product-${Date.now()}`; const products = db.project('ecommerce').collection('inventory').cluster('products');
// Create product using builder pattern await products.record(`${productId}-name`, 'STRING', name) .create(`${productId}-name`, 'STRING', name); await products.record(`${productId}-price`, 'FLOAT', price.toString()) .create(`${productId}-price`, 'FLOAT', price.toString()); await products.record(`${productId}-category`, 'STRING', category) .create(`${productId}-category`, 'STRING', category);
if (description) { await products.record(`${productId}-description`, 'STRING', description) .create(`${productId}-description`, 'STRING', description); }
await products.record(`${productId}-created`, 'DATETIME', new Date().toISOString()) .create(`${productId}-created`, 'DATETIME', new Date().toISOString());
return NextResponse.json({ success: true, productId, name }, { status: 201 }); } catch (error) { console.error('Create product error:', error); return NextResponse.json( { error: 'Failed to create product' }, { status: 500 } ); }}
Pages Router API
Section titled “Pages Router API”import type { NextApiRequest, NextApiResponse } from 'next';import OstrichDB from 'ostrichdb-js';
const db = new OstrichDB({ baseUrl: process.env.OSTRICHDB_URL!, apiKey: process.env.OSTRICHDB_TOKEN!, timeout: 15000});
export default async function handler( req: NextApiRequest, res: NextApiResponse) { const { userId } = req.query;
if (typeof userId !== 'string') { return res.status(400).json({ error: 'Invalid user ID' }); }
const users = db.project('social-app').collection('profiles').cluster('users');
try { switch (req.method) { case 'GET': const username = await users.record(`${userId}-username`).get(`${userId}-username`); const email = await users.record(`${userId}-email`).get(`${userId}-email`); const bio = await users.record(`${userId}-bio`).get(`${userId}-bio`); const joined = await users.record(`${userId}-joined`).get(`${userId}-joined`);
res.json({ id: userId, username: username.value, email: email.value, bio: bio.value, joined: joined.value }); break;
case 'PUT': const { username: newUsername, email: newEmail, bio: newBio } = req.body;
if (newUsername) { await users.record(`${userId}-username`, 'STRING', newUsername) .create(`${userId}-username`, 'STRING', newUsername); } if (newEmail) { await users.record(`${userId}-email`, 'STRING', newEmail) .create(`${userId}-email`, 'STRING', newEmail); } if (newBio) { await users.record(`${userId}-bio`, 'STRING', newBio) .create(`${userId}-bio`, 'STRING', newBio); }
res.json({ success: true, message: 'User updated successfully' }); break;
case 'DELETE': await users.record(`${userId}-username`).delete(`${userId}-username`); await users.record(`${userId}-email`).delete(`${userId}-email`); await users.record(`${userId}-bio`).delete(`${userId}-bio`); await users.record(`${userId}-joined`).delete(`${userId}-joined`);
res.json({ success: true, message: 'User deleted successfully' }); break;
default: res.setHeader('Allow', ['GET', 'PUT', 'DELETE']); res.status(405).end(`Method ${req.method} Not Allowed`); } } catch (error) { console.error('User API error:', error);
if (error.statusCode === 404) { res.status(404).json({ error: 'User not found' }); } else { res.status(500).json({ error: 'Internal server error' }); } }}
Server Components and Actions
Section titled “Server Components and Actions”'use server'
import OstrichDB from 'ostrichdb-js';import { revalidatePath } from 'next/cache';
const db = new OstrichDB({ baseUrl: process.env.OSTRICHDB_URL!, apiKey: process.env.OSTRICHDB_TOKEN!});
export async function createTask(formData: FormData) { try { const title = formData.get('title') as string; const description = formData.get('description') as string; const priority = formData.get('priority') as string;
if (!title) { throw new Error('Title is required'); }
const taskId = `task-${Date.now()}`; const tasks = db.project('productivity').collection('tasks').cluster('active');
await tasks.record(`${taskId}-title`, 'STRING', title) .create(`${taskId}-title`, 'STRING', title); await tasks.record(`${taskId}-description`, 'STRING', description || '') .create(`${taskId}-description`, 'STRING', description || ''); await tasks.record(`${taskId}-priority`, 'STRING', priority) .create(`${taskId}-priority`, 'STRING', priority); await tasks.record(`${taskId}-status`, 'STRING', 'pending') .create(`${taskId}-status`, 'STRING', 'pending'); await tasks.record(`${taskId}-created`, 'DATETIME', new Date().toISOString()) .create(`${taskId}-created`, 'DATETIME', new Date().toISOString());
revalidatePath('/dashboard'); return { success: true, taskId }; } catch (error) { console.error('Create task error:', error); return { success: false, error: error.message }; }}
export async function updateTaskStatus(taskId: string, status: string) { try { const tasks = db.project('productivity').collection('tasks').cluster('active');
await tasks.record(`${taskId}-status`, 'STRING', status) .create(`${taskId}-status`, 'STRING', status);
if (status === 'completed') { await tasks.record(`${taskId}-completed`, 'DATETIME', new Date().toISOString()) .create(`${taskId}-completed`, 'DATETIME', new Date().toISOString()); }
revalidatePath('/dashboard'); return { success: true }; } catch (error) { console.error('Update task error:', error); return { success: false, error: error.message }; }}
export async function getTasks() { try { const tasks = db.project('productivity').collection('tasks').cluster('active'); const records = await tasks.listRecords();
// Group records by task ID const taskMap = new Map(); records.forEach(record => { const parts = record.name.split('-'); const taskId = parts.slice(0, 2).join('-'); const field = parts.slice(2).join('-');
if (!taskMap.has(taskId)) { taskMap.set(taskId, { id: taskId }); }
taskMap.get(taskId)[field] = record.value; });
return Array.from(taskMap.values()) .filter(task => task.title) .sort((a, b) => new Date(b.created).getTime() - new Date(a.created).getTime()); } catch (error) { console.error('Get tasks error:', error); return []; }}
React Integration
Section titled “React Integration”Custom Hook for OstrichDB
Section titled “Custom Hook for OstrichDB”import { useState, useEffect, useCallback } from 'react';import OstrichDB from 'ostrichdb-js';
interface UseOstrichDBOptions { baseUrl?: string; apiKey?: string; timeout?: number;}
export function useOstrichDB(options: UseOstrichDBOptions) { const [db, setDb] = useState<OstrichDB | null>(null); const [isConnected, setIsConnected] = useState(false); const [error, setError] = useState<string | null>(null);
useEffect(() => { const initializeDB = async () => { try { const client = new OstrichDB({ baseUrl: options.baseUrl || process.env.REACT_APP_OSTRICHDB_URL!, apiKey: options.apiKey || process.env.REACT_APP_OSTRICHDB_TOKEN!, timeout: options.timeout || 10000 });
// Test connection await client.health_check();
setDb(client); setIsConnected(true); setError(null); } catch (err) { const errorMessage = err instanceof Error ? err.message : 'Failed to connect'; setError(errorMessage); setIsConnected(false); console.error('OstrichDB connection error:', err); } };
if (options.apiKey) { initializeDB(); } }, [options.apiKey, options.baseUrl, options.timeout]);
const updateToken = useCallback((newToken: string) => { if (db) { db.setAuthToken(newToken); setError(null); } }, [db]);
return { db, isConnected, error, updateToken };}
React Component Example
Section titled “React Component Example”import React, { useState, useEffect } from 'react';import { useOstrichDB } from '../hooks/useOstrichDB';
interface Task { id: string; title: string; description: string; status: 'pending' | 'in-progress' | 'completed'; created: string;}
export function TaskManager() { const { db, isConnected, error } = useOstrichDB({ apiKey: localStorage.getItem('authToken') || '' });
const [tasks, setTasks] = useState<Task[]>([]); const [loading, setLoading] = useState(false); const [newTask, setNewTask] = useState({ title: '', description: '' });
const loadTasks = async () => { if (!db || !isConnected) return;
setLoading(true); try { const taskCluster = db.project('productivity') .collection('tasks') .cluster('active');
const records = await taskCluster.listRecords();
// Group records by task ID const taskMap = new Map(); records.forEach(record => { const parts = record.name.split('-'); const taskId = parts.slice(0, 2).join('-'); const field = parts.slice(2).join('-');
if (!taskMap.has(taskId)) { taskMap.set(taskId, { id: taskId }); }
taskMap.get(taskId)[field] = record.value; });
const taskList = Array.from(taskMap.values()) .filter(task => task.title) .sort((a, b) => new Date(b.created).getTime() - new Date(a.created).getTime());
setTasks(taskList); } catch (err) { console.error('Failed to load tasks:', err); } finally { setLoading(false); } };
const createTask = async () => { if (!db || !newTask.title.trim()) return;
try { const taskId = `task-${Date.now()}`; const taskCluster = db.project('productivity') .collection('tasks') .cluster('active');
await taskCluster.record(`${taskId}-title`, 'STRING', newTask.title) .create(`${taskId}-title`, 'STRING', newTask.title); await taskCluster.record(`${taskId}-description`, 'STRING', newTask.description) .create(`${taskId}-description`, 'STRING', newTask.description); await taskCluster.record(`${taskId}-status`, 'STRING', 'pending') .create(`${taskId}-status`, 'STRING', 'pending'); await taskCluster.record(`${taskId}-created`, 'DATETIME', new Date().toISOString()) .create(`${taskId}-created`, 'DATETIME', new Date().toISOString());
setNewTask({ title: '', description: '' }); await loadTasks(); // Refresh task list } catch (err) { console.error('Failed to create task:', err); } };
const updateTaskStatus = async (taskId: string, newStatus: string) => { if (!db) return;
try { const taskCluster = db.project('productivity') .collection('tasks') .cluster('active');
await taskCluster.record(`${taskId}-status`, 'STRING', newStatus) .create(`${taskId}-status`, 'STRING', newStatus);
await loadTasks(); // Refresh task list } catch (err) { console.error('Failed to update task:', err); } };
useEffect(() => { if (isConnected) { loadTasks(); } }, [isConnected]);
if (error) { return <div className="error">Error: {error}</div>; }
if (!isConnected) { return <div>Connecting to OstrichDB...</div>; }
return ( <div className="task-manager"> <h2>Task Manager</h2>
{/* Create new task */} <div className="create-task"> <input type="text" placeholder="Task title" value={newTask.title} onChange={(e) => setNewTask({ ...newTask, title: e.target.value })} /> <textarea placeholder="Task description" value={newTask.description} onChange={(e) => setNewTask({ ...newTask, description: e.target.value })} /> <button onClick={createTask} disabled={!newTask.title.trim()}> Create Task </button> </div>
{/* Task list */} {loading ? ( <div>Loading tasks...</div> ) : ( <div className="task-list"> {tasks.map(task => ( <div key={task.id} className={`task task-${task.status}`}> <h3>{task.title}</h3> <p>{task.description}</p> <div className="task-meta"> <span>Status: {task.status}</span> <span>Created: {new Date(task.created).toLocaleDateString()}</span> </div> <div className="task-actions"> {task.status === 'pending' && ( <button onClick={() => updateTaskStatus(task.id, 'in-progress')}> Start </button> )} {task.status === 'in-progress' && ( <button onClick={() => updateTaskStatus(task.id, 'completed')}> Complete </button> )} </div> </div> ))} </div> )} </div> );}
Vue.js Integration
Section titled “Vue.js Integration”Composable for OstrichDB
Section titled “Composable for OstrichDB”import { ref, onMounted, computed } from 'vue';import OstrichDB from 'ostrichdb-js';
export function useOstrichDB(options: { baseUrl?: string; apiKey?: string; timeout?: number;}) { const db = ref<OstrichDB | null>(null); const isConnected = ref(false); const error = ref<string | null>(null); const loading = ref(false);
const initialize = async () => { loading.value = true; try { const client = new OstrichDB({ baseUrl: options.baseUrl || import.meta.env.VITE_OSTRICHDB_URL, apiKey: options.apiKey || import.meta.env.VITE_OSTRICHDB_TOKEN, timeout: options.timeout || 10000 });
await client.health_check();
db.value = client; isConnected.value = true; error.value = null; } catch (err) { error.value = err instanceof Error ? err.message : 'Connection failed'; isConnected.value = false; } finally { loading.value = false; } };
const updateToken = (newToken: string) => { if (db.value) { db.value.setAuthToken(newToken); error.value = null; } };
onMounted(() => { if (options.apiKey) { initialize(); } });
return { db: computed(() => db.value), isConnected: computed(() => isConnected.value), error: computed(() => error.value), loading: computed(() => loading.value), initialize, updateToken };}
Vue Component Example
Section titled “Vue Component Example”<template> <div class="product-catalog"> <h2>Product Catalog</h2>
<div v-if="loading" class="loading"> Loading products... </div>
<div v-else-if="error" class="error"> Error: {{ error }} </div>
<div v-else> <!-- Add product form --> <form @submit.prevent="addProduct" class="add-product"> <input v-model="newProduct.name" type="text" placeholder="Product name" required /> <input v-model.number="newProduct.price" type="number" step="0.01" placeholder="Price" required /> <select v-model="newProduct.category" required> <option value="">Select category</option> <option value="Electronics">Electronics</option> <option value="Clothing">Clothing</option> <option value="Books">Books</option> </select> <button type="submit" :disabled="!isFormValid"> Add Product </button> </form>
<!-- Product list --> <div class="product-grid"> <div v-for="product in products" :key="product.id" class="product-card" > <h3>{{ product.name }}</h3> <p class="price">${{ product.price.toFixed(2) }}</p> <p class="category">{{ product.category }}</p> <button @click="deleteProduct(product.id)" class="delete-btn"> Delete </button> </div> </div> </div> </div></template>
<script setup lang="ts">import { ref, computed, onMounted } from 'vue';import { useOstrichDB } from '../composables/useOstrichDB';
interface Product { id: string; name: string; price: number; category: string; created: string;}
const { db, isConnected, error } = useOstrichDB({ apiKey: localStorage.getItem('authToken') || ''});
const products = ref<Product[]>([]);const loading = ref(false);const newProduct = ref({ name: '', price: 0, category: ''});
const isFormValid = computed(() => { return newProduct.value.name.trim() && newProduct.value.price > 0 && newProduct.value.category;});
const loadProducts = async () => { if (!db.value || !isConnected.value) return;
loading.value = true; try { const productCluster = db.value.project('store') .collection('inventory') .cluster('products');
const records = await productCluster.listRecords();
// Group records by product ID const productMap = new Map(); records.forEach(record => { const parts = record.name.split('-'); const productId = parts.slice(0, 2).join('-'); const field = parts.slice(2).join('-');
if (!productMap.has(productId)) { productMap.set(productId, { id: productId }); }
productMap.get(productId)[field] = record.value; });
products.value = Array.from(productMap.values()) .filter(product => product.name && product.price) .map(product => ({ ...product, price: parseFloat(product.price) })); } catch (err) { console.error('Failed to load products:', err); } finally { loading.value = false; }};
const addProduct = async () => { if (!db.value || !isFormValid.value) return;
try { const productId = `product-${Date.now()}`; const productCluster = db.value.project('store') .collection('inventory') .cluster('products');
await productCluster.record(`${productId}-name`, 'STRING', newProduct.value.name) .create(`${productId}-name`, 'STRING', newProduct.value.name); await productCluster.record(`${productId}-price`, 'FLOAT', newProduct.value.price.toString()) .create(`${productId}-price`, 'FLOAT', newProduct.value.price.toString()); await productCluster.record(`${productId}-category`, 'STRING', newProduct.value.category) .create(`${productId}-category`, 'STRING', newProduct.value.category); await productCluster.record(`${productId}-created`, 'DATETIME', new Date().toISOString()) .create(`${productId}-created`, 'DATETIME', new Date().toISOString());
// Reset form newProduct.value = { name: '', price: 0, category: '' };
// Reload products await loadProducts(); } catch (err) { console.error('Failed to add product:', err); }};
const deleteProduct = async (productId: string) => { if (!db.value) return;
if (!confirm('Are you sure you want to delete this product?')) return;
try { const productCluster = db.value.project('store') .collection('inventory') .cluster('products');
await productCluster.record(`${productId}-name`).delete(`${productId}-name`); await productCluster.record(`${productId}-price`).delete(`${productId}-price`); await productCluster.record(`${productId}-category`).delete(`${productId}-category`); await productCluster.record(`${productId}-created`).delete(`${productId}-created`);
await loadProducts(); } catch (err) { console.error('Failed to delete product:', err); }};
onMounted(() => { if (isConnected.value) { loadProducts(); }});
// Watch for connection changeswatch(isConnected, (connected) => { if (connected) { loadProducts(); }});</script>
Best Practices for Framework Integration
Section titled “Best Practices for Framework Integration”1. Connection Management
Section titled “1. Connection Management”Singleton Pattern:
import OstrichDB from 'ostrichdb-js';
let dbInstance: OstrichDB | null = null;
export function getDatabase(): OstrichDB { if (!dbInstance) { dbInstance = new OstrichDB({ baseUrl: process.env.OSTRICHDB_URL!, apiKey: process.env.OSTRICHDB_TOKEN!, timeout: 15000 }); } return dbInstance;}
export function updateDatabaseToken(token: string) { if (dbInstance) { dbInstance.setAuthToken(token); }}
2. Error Handling
Section titled “2. Error Handling”Centralized Error Handler:
import { OstrichDBError } from 'ostrichdb-js';
export function handleDatabaseError(error: unknown) { if (error instanceof OstrichDBError) { switch (error.statusCode) { case 401: // Handle authentication error window.location.href = '/login'; break; case 403: console.error('Access denied:', error.message); break; case 404: console.error('Resource not found:', error.message); break; case 429: console.error('Rate limit exceeded:', error.message); break; default: console.error('Database error:', error.message); } } else { console.error('Unexpected error:', error); }}
3. Type Safety
Section titled “3. Type Safety”TypeScript Interfaces:
export interface User { id: string; username: string; email: string; created: string;}
export interface Product { id: string; name: string; price: number; category: string; description?: string; created: string;}
export interface DatabaseResponse<T> { success: boolean; data?: T; error?: string;}
4. Environment Configuration
Section titled “4. Environment Configuration”Multi-environment Setup:
interface DatabaseConfig { baseUrl: string; timeout: number;}
const configs: Record<string, DatabaseConfig> = { development: { baseUrl: 'http://localhost:8042', timeout: 30000 }, production: { baseUrl: process.env.OSTRICHDB_URL!, timeout: 10000 }, test: { baseUrl: 'http://localhost:8043', timeout: 5000 }};
export function getDatabaseConfig(): DatabaseConfig { const env = process.env.NODE_ENV || 'development'; return configs[env];}
Next Steps
Section titled “Next Steps”Now that you understand framework integration:
- Error Handling - Implement comprehensive error management
- Configuration - Optimize settings for different environments
- API Reference - Explore all available methods
- Builder Pattern - Master the chainable API
Framework integration makes OstrichDB accessible from any JavaScript environment. Choose the patterns that best fit your application architecture and follow the established conventions of your chosen framework.