The old config-generation subsystem (src/config/ + autoConfiguration.ts) was
superseded during the 8.0 rework and never wired into init(): it emitted settings
for a partitioning subsystem that no longer exists and probed deleted cloud env
vars. The live zero-config path is inline — recall preset → HNSW knobs, storage
auto-detect, auto persistMode, container-memory-aware cache sizing.
The storage progressive-init / cloud-detection cluster was equally dead after the
cloud adapters were dropped: isCloudStorage() is permanently false (no overriders),
scheduleBackgroundInit/runBackgroundInit were never called (the latter an empty
body), initMode was never assigned, and Brainy.isFullyInitialized()/
awaitBackgroundInit() were always-trivial with zero callers. scheduleCountPersist()
collapses to its only-ever-taken immediate write-through path.
Removed:
- src/config/{index,zeroConfig,storageAutoConfig,modelAutoConfig,sharedConfigManager}.ts
- src/utils/autoConfiguration.ts + the inert BrainyZeroConfig export
- Brainy.isFullyInitialized()/awaitBackgroundInit() (+ BrainyInterface decls)
- InitMode type, isCloudStorage/detectCloudEnvironment/resolveInitMode,
scheduleBackgroundInit/runBackgroundInit/ensureValidatedForWrite and their state
- Dead cloud env-var probes (K_SERVICE/K_REVISION/AWS_LAMBDA_FUNCTION_NAME/
FUNCTIONS_TARGET/AZURE_FUNCTIONS_ENVIRONMENT)
Kept (verified live): production-detection logging (environment.ts), container-
memory cache sizing (memoryDetection/paramValidation), on-disk hash bucketing
(sharding.ts).
Docs: scrubbed deleted-subsystem references (JS quantization knobs, cloud/OPFS
adapters, partitioning, old zero-config API) across 14 files; deleted two wholly-
obsolete feature docs (complete-feature-list, v3-features); rewrote
architecture/zero-config for 8.0.
~3,700 LOC removed. Build clean; 1392 unit + 24 db-mvcc green.
14 KiB
Framework Integration Guide
Brainy is framework-friendly - designed to drop into the server side of any modern JavaScript framework. This guide shows you how to integrate Brainy into framework-based apps.
Runtime: Brainy 8.0 runs on Node.js 22+ and Bun (server-side only). It is not a browser library. In framework apps, run Brainy in API routes, server components, server actions, loaders, or a dedicated backend service - never in client-side bundles.
🎯 Why Server-Side?
Brainy embeds an HNSW vector index, a graph engine, and a filesystem-backed persistence layer. These belong on the server:
- Zero configuration: Just
import { Brainy } from '@soulcraft/brainy' - Auto storage detection:
new Brainy()auto-selects filesystem persistence on Node - Cleaner code: No browser polyfills, no conditional client/server imports
- Better DX: One instance shared across your server routes
🚀 Quick Start
Install Brainy
npm install @soulcraft/brainy
Basic Integration
import { Brainy } from '@soulcraft/brainy'
// Run on the server (API route, server component, backend service)
// new Brainy() auto-detects filesystem persistence on Node
const brain = new Brainy()
await brain.init()
// Add data
await brain.add({
data: "Framework integration is awesome!",
type: "concept",
metadata: { framework: "any" }
})
// Search
const results = await brain.find("framework integration")
⚛️ React Integration
Brainy runs on the server, so a React client component talks to it through an API endpoint (see the Next.js API route below). The hook fetches results; it never instantiates Brainy in the browser.
Basic Hook Pattern
import { useState, useCallback } from 'react'
function useBrainySearch(endpoint = '/api/search') {
const [results, setResults] = useState([])
const [loading, setLoading] = useState(false)
const search = useCallback(async (query) => {
if (!query) return
setLoading(true)
try {
const res = await fetch(endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query })
})
const { results } = await res.json()
setResults(results)
} finally {
setLoading(false)
}
}, [endpoint])
return { results, loading, search }
}
// Usage in component
function SearchComponent() {
const { results, loading, search } = useBrainySearch()
return (
<div>
<input
type="text"
placeholder="Search..."
onChange={(e) => search(e.target.value)}
/>
{loading && <div>Searching...</div>}
<div>
{results.map(result => (
<div key={result.id}>
<h3>{result.data}</h3>
<p>Score: {(result.score * 100).toFixed(1)}%</p>
</div>
))}
</div>
</div>
)
}
Shared Server Instance
On the server, create one Brainy instance and reuse it across requests. This module is imported only by server code (API routes, server components), never by client components:
// lib/brain.server.js
import { Brainy } from '@soulcraft/brainy'
let brainPromise
export function getBrain() {
if (!brainPromise) {
brainPromise = (async () => {
// new Brainy() auto-detects filesystem persistence on Node
const brain = new Brainy()
await brain.init()
return brain
})()
}
return brainPromise
}
🟢 Vue.js Integration
Vue components call a server endpoint (see the Nuxt server route in the Vue.js Integration Guide); Brainy itself runs on the server.
Composition API (client component)
<template>
<div>
<input v-model="query" @input="search" placeholder="Search..." />
<div v-for="result in results" :key="result.id">
<h3>{{ result.data }}</h3>
<p>Score: {{ (result.score * 100).toFixed(1) }}%</p>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const query = ref('')
const results = ref([])
const search = async () => {
if (!query.value) return
const res = await fetch('/api/search', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: query.value })
})
results.value = (await res.json()).results
}
</script>
Shared Server Instance
On the server, create one Brainy instance and reuse it across requests:
// server/brain.js (server-only module)
import { Brainy } from '@soulcraft/brainy'
let brainPromise
export function getBrain() {
if (!brainPromise) {
brainPromise = (async () => {
// new Brainy() auto-detects filesystem persistence on Node
const brain = new Brainy()
await brain.init()
return brain
})()
}
return brainPromise
}
🅰️ Angular Integration
The Angular service calls your backend over HTTP; Brainy lives in that backend, not in the browser.
Service Pattern (calls the backend)
// brainy.service.ts
import { Injectable } from '@angular/core'
import { HttpClient } from '@angular/common/http'
import { Observable } from 'rxjs'
@Injectable({
providedIn: 'root'
})
export class BrainyService {
constructor(private http: HttpClient) {}
search(query: string): Observable<{ results: any[] }> {
return this.http.post<{ results: any[] }>('/api/search', { query })
}
add(data: any, type: string, metadata?: any): Observable<{ id: string }> {
return this.http.post<{ id: string }>('/api/add', { data, type, metadata })
}
}
// search.component.ts
import { Component } from '@angular/core'
import { BrainyService } from './brainy.service'
@Component({
selector: 'app-search',
template: `
<div>
<input
[(ngModel)]="query"
(input)="search()"
placeholder="Search..."
/>
<div *ngFor="let result of results">
<h3>{{ result.data }}</h3>
<p>Score: {{ (result.score * 100).toFixed(1) }}%</p>
</div>
</div>
`
})
export class SearchComponent {
query = ''
results: any[] = []
constructor(private brainyService: BrainyService) {}
search() {
if (!this.query) return
this.brainyService.search(this.query).subscribe(({ results }) => {
this.results = results
})
}
}
The matching backend endpoint uses Brainy directly (Node/Bun):
// server: api/search
import { Brainy } from '@soulcraft/brainy'
const brain = new Brainy() // auto-detects filesystem persistence on Node
await brain.init()
export async function handleSearch(query: string) {
return await brain.find(query)
}
🚀 Next.js Integration
In Next.js, Brainy lives in server code only: API routes, server components, or server actions. Create one shared instance in a server-only module.
Shared Server Instance
// lib/brain.server.js (imported only by server code)
import { Brainy } from '@soulcraft/brainy'
let brainPromise
export function getBrain() {
if (!brainPromise) {
brainPromise = (async () => {
const brain = new Brainy({
storage: { type: 'filesystem', rootDirectory: './data' }
})
await brain.init()
return brain
})()
}
return brainPromise
}
API Routes
// app/api/search/route.js
import { getBrain } from '@/lib/brain.server'
export async function POST(request) {
const { query } = await request.json()
const brain = await getBrain()
const results = await brain.find(query)
return Response.json({ results })
}
Server Action
// app/actions.js
'use server'
import { getBrain } from '@/lib/brain.server'
export async function search(query) {
const brain = await getBrain()
return await brain.find(query)
}
🔷 SvelteKit Integration
Brainy runs in a server-only module (*.server.js); the component fetches results from an endpoint.
// src/lib/server/brain.js (server-only — note the .server suffix)
import { Brainy } from '@soulcraft/brainy'
let brainPromise
export function getBrain() {
if (!brainPromise) {
brainPromise = (async () => {
const brain = new Brainy() // auto-detects filesystem persistence
await brain.init()
return brain
})()
}
return brainPromise
}
// src/routes/api/search/+server.js
import { json } from '@sveltejs/kit'
import { getBrain } from '$lib/server/brain'
export async function POST({ request }) {
const { query } = await request.json()
const brain = await getBrain()
return json({ results: await brain.find(query) })
}
<!-- SearchComponent.svelte -->
<script>
let query = ''
let results = []
async function search() {
if (!query) return
const res = await fetch('/api/search', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query })
})
results = (await res.json()).results
}
</script>
<div>
<input bind:value={query} on:input={search} placeholder="Search..." />
{#each results as result}
<div>
<h3>{result.data}</h3>
<p>Score: {(result.score * 100).toFixed(1)}%</p>
</div>
{/each}
</div>
🌟 Solid.js Integration
The component calls a server endpoint (use SolidStart server routes, or any backend, to host Brainy):
import { createSignal } from 'solid-js'
function SearchComponent() {
const [query, setQuery] = createSignal('')
const [results, setResults] = createSignal([])
const search = async () => {
if (!query()) return
const res = await fetch('/api/search', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: query() })
})
setResults((await res.json()).results)
}
return (
<div>
<input
value={query()}
onInput={(e) => {
setQuery(e.target.value)
search()
}}
placeholder="Search..."
/>
<For each={results()}>
{(result) => (
<div>
<h3>{result.data}</h3>
<p>Score: {(result.score * 100).toFixed(1)}%</p>
</div>
)}
</For>
</div>
)
}
📦 Bundler Configuration
Brainy is a server-side dependency, so keep it out of client bundles. Import it only from server-only modules (*.server.js, API routes, server components, server actions). If your bundler ever tries to pull Brainy into a client bundle, that's a sign it's being imported from a client component — move the import to a server module.
For server builds, mark Brainy as external so the bundler doesn't inline it:
// vite.config.js (SSR build)
import { defineConfig } from 'vite'
export default defineConfig({
ssr: {
external: ['@soulcraft/brainy']
}
})
// rollup.config.js (server bundle)
export default {
external: ['@soulcraft/brainy', 'node:fs', 'node:path', 'node:crypto']
}
🌐 SSR/SSG Considerations
Server-Side Rendering
Instantiate Brainy on the server and feed its results into the rendered page. Never construct it in client code.
// Server-side data loading (framework loader / getServerSideProps / load fn)
import { getBrain } from './brain.server'
export async function load({ url }) {
const brain = await getBrain()
const query = url.searchParams.get('q') ?? ''
const results = query ? await brain.find(query) : []
return { results }
}
Static Site Generation
// For build-time usage (runs in Node during the build)
import { Brainy } from '@soulcraft/brainy'
export async function generateStaticProps() {
const brain = new Brainy({
storage: { type: 'filesystem', rootDirectory: './content' }
})
await brain.init()
// Build search index (paginate with { limit, offset } for larger stores)
const allContent = await brain.find({ limit: 1000 })
return {
props: { searchIndex: allContent }
}
}
🔧 Framework-Specific Tips
React
- Keep components client-side and call a Brainy-backed API route
- Use
useCallbackfor fetch handlers to prevent re-renders - Debounce keystroke-driven searches before hitting the endpoint
Vue
- Components call an endpoint; the shared instance lives in a server module
- Consider Pinia for caching results client-side
- Debounce reactive search queries
Angular
- Use
HttpClientand RxJS to call the backend - Hold the shared Brainy instance in your Node backend, not the app
- Consider lazy loading search features in feature modules
Next.js
- Put Brainy in server-only modules (
*.server.js), API routes, or server actions - Reuse one shared instance across requests
- Implement proper error boundaries for failed fetches
🚨 Common Issues & Solutions
Issue: "fs module not found" / "crypto is not defined" in the browser
Cause: Brainy was imported into a client bundle. Brainy 8.0 is a server-side library (Node 22+/Bun) and uses Node built-ins like fs and crypto.
Solution: Import Brainy only from server code — server-only modules (*.server.js), API routes, server components, or server actions. From client components, call those endpoints instead.
Issue: Large client bundle size
Cause: A client module is pulling in Brainy.
Solution: Move the import { Brainy } from '@soulcraft/brainy' into a server-only module so it never reaches the browser bundle.
Issue: SSR hydration mismatch
Solution: Run the search on the server (loader / server action / API route) and pass the results down as props, so server and client render the same markup.
🎯 Best Practices
- Initialize Once: Create one shared Brainy instance per server process, not per request
- Server-Only: Import Brainy only from server modules — never from client components
- Endpoint Boundary: Expose search/add through API routes or server actions
- Handle Loading: Show loading states in the client while the fetch is in flight
- Error Handling: Catch and surface failed endpoint calls gracefully
- Storage: Use
filesystemfor persistence (the default on Node) ormemoryfor ephemeral/tests
📚 Next Steps
- Next.js Integration Guide - Detailed Next.js examples
- Vue.js Integration Guide - Complete Vue.js patterns
- API Reference - Complete API documentation
- Production Deployment - Deploy to production
🤝 Community Examples
Check out community examples in the examples repository:
- React + TypeScript starter
- Vue 3 + Composition API
- Next.js full-stack app
- Svelte SPA with search
- Angular enterprise app