# Vue.js Integration Guide Complete guide to integrating Brainy with Vue.js applications, covering Vue 3, Nuxt.js, Composition API, Options API, and advanced patterns. > **Runtime**: Brainy 8.0 runs on Node.js 22+ and Bun, server-side only โ€” it is not a browser library. In Vue apps, Brainy lives behind an HTTP endpoint (a Nuxt server route, or any Node/Bun backend), and your components call that endpoint. The composables, plugin, and store below are wrappers around `fetch`; the single Brainy instance is created once on the server (see [Nuxt Server Route](#nuxt-server-route)). ## ๐Ÿš€ Quick Start ### Installation ```bash npm create vue@latest my-brainy-app cd my-brainy-app npm install npm install @soulcraft/brainy ``` ### Basic Setup The composable talks to a Brainy-backed endpoint (see [Nuxt Server Route](#nuxt-server-route)). It is always "ready" because there is no client-side initialization โ€” the server owns the Brainy instance. ```javascript // src/composables/useBrainy.js import { ref } from 'vue' export function useBrainy(endpoint = '/api/brain') { const error = ref(null) const search = async (query, options = {}) => { error.value = null try { const res = await fetch(`${endpoint}/search`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query, options }) }) if (!res.ok) throw new Error(`Search failed: ${res.status}`) return (await res.json()).results } catch (err) { error.value = err.message console.error('Brainy search failed:', err) return [] } } const add = async (data, type, metadata) => { const res = await fetch(`${endpoint}/add`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ data, type, metadata }) }) return (await res.json()).id } const stats = async () => { const res = await fetch(`${endpoint}/stats`) return await res.json() } return { search, add, stats, error: readonly(error) } } ``` ## ๐Ÿงฉ Composition API ### Search Component ```vue ``` ### Data Management ```vue ``` ## ๐Ÿ—๏ธ Options API ### Search Component (Options API) ```vue ``` ## ๐Ÿ”Œ Vue Plugin ### Global Brainy Plugin The plugin exposes a global `$searchBrain` helper that calls your Brainy-backed endpoint. Brainy itself runs on the server (see [Nuxt Server Route](#nuxt-server-route)). ```javascript // src/plugins/brainy.js export default { install(app, options = {}) { const endpoint = options.endpoint ?? '/api/brain' // Add global search method (calls the server endpoint) app.config.globalProperties.$searchBrain = async (query, searchOptions) => { const res = await fetch(`${endpoint}/search`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query, options: searchOptions }) }) return (await res.json()).results } } } ``` ### Plugin Usage ```javascript // src/main.js import { createApp } from 'vue' import App from './App.vue' import BrainyPlugin from './plugins/brainy' const app = createApp(App) app.use(BrainyPlugin, { endpoint: '/api/brain' }) app.mount('#app') ``` ```vue ``` ## ๐Ÿฐ Nuxt.js Integration Nuxt's server engine (Nitro) is the natural home for Brainy: it runs on Node/Bun, so the Brainy instance lives in `server/`, and pages/components call its routes. ### Nuxt Server Route ```javascript // server/utils/brain.js (server-only โ€” Nitro never bundles this into the client) 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 } ``` ```javascript // server/api/brain/search.post.js import { getBrain } from '../../utils/brain' export default defineEventHandler(async (event) => { const { query, options } = await readBody(event) const brain = await getBrain() return { results: await brain.find(query, options) } }) ``` ```javascript // server/api/brain/add.post.js import { getBrain } from '../../utils/brain' export default defineEventHandler(async (event) => { const { data, type, metadata } = await readBody(event) const brain = await getBrain() return { id: await brain.add({ data, type, metadata }) } }) ``` ```javascript // server/api/brain/stats.get.js import { getBrain } from '../../utils/brain' export default defineEventHandler(async () => { const brain = await getBrain() return await brain.stats() }) ``` ### Nuxt Composable ```javascript // composables/useBrainy.js export const useBrainy = () => { const search = async (query, options = {}) => { return (await $fetch('/api/brain/search', { method: 'POST', body: { query, options } })).results } const add = async (data, type, metadata) => { return (await $fetch('/api/brain/add', { method: 'POST', body: { data, type, metadata } })).id } const stats = async () => { return await $fetch('/api/brain/stats') } return { search, add, stats } } ``` ### Nuxt Page Example ```vue ``` ## ๐Ÿ› ๏ธ Advanced Patterns ### Global State Management with Pinia The store caches results and stats client-side; all Brainy work happens behind the server endpoints. ```javascript // stores/brainy.js import { defineStore } from 'pinia' export const useBrainyStore = defineStore('brainy', () => { const error = ref(null) const stats = ref(null) const search = async (query, options = {}) => { error.value = null try { const res = await fetch('/api/brain/search', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query, options }) }) return (await res.json()).results } catch (err) { error.value = err.message throw err } } const add = async (data, type, metadata) => { const res = await fetch('/api/brain/add', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ data, type, metadata }) }) const { id } = await res.json() await loadStats() // Refresh stats return id } const loadStats = async () => { try { const res = await fetch('/api/brain/stats') stats.value = await res.json() } catch (err) { console.error('Failed to load stats:', err) } } return { error: readonly(error), stats: readonly(stats), search, add, loadStats } }) ``` ### Real-time Search Component ```vue ``` ## ๐Ÿ“Š Performance Optimization ### Lazy Loading ```vue ``` ### Virtual Scrolling for Large Results ```vue ``` ## ๐Ÿงช Testing ### Component Testing with Vitest The component talks to the server over `fetch`, so mock the endpoint, not Brainy itself. ```javascript // tests/components/Search.test.js import { mount } from '@vue/test-utils' import { describe, it, expect, vi, beforeEach } from 'vitest' import Search from '../src/components/Search.vue' // Mock the server endpoint beforeEach(() => { global.fetch = vi.fn().mockResolvedValue({ ok: true, json: async () => ({ results: [{ id: '1', data: 'Test result', score: 0.9 }] }) }) }) describe('Search Component', () => { let wrapper beforeEach(() => { wrapper = mount(Search) }) it('renders search input', () => { expect(wrapper.find('input').exists()).toBe(true) }) it('performs search when input changes', async () => { const input = wrapper.find('input') await input.setValue('test query') await input.trigger('input') // Wait for debounced search await new Promise(resolve => setTimeout(resolve, 350)) expect(global.fetch).toHaveBeenCalled() expect(wrapper.text()).toContain('Test result') }) }) ``` ### E2E Testing with Playwright ```javascript // tests/e2e/search.spec.js import { test, expect } from '@playwright/test' test('search functionality works', async ({ page }) => { await page.goto('/') // Wait for brain to initialize await page.waitForSelector('[data-testid="search-input"]') // Perform search await page.fill('[data-testid="search-input"]', 'test query') // Wait for results await page.waitForSelector('[data-testid="search-results"]') // Check results const results = await page.locator('[data-testid="result-item"]') await expect(results).toHaveCountGreaterThan(0) }) ``` ## ๐Ÿš€ Production Tips ### Bundle Optimization Keep Brainy out of the client bundle โ€” it belongs to the server build only. Mark it external for SSR so the bundler resolves it at runtime instead of inlining it. ```javascript // vite.config.js import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], ssr: { external: ['@soulcraft/brainy'] } }) ``` ### Error Handling Wrap the endpoint call with retry/backoff so transient network failures don't surface to the user. ```javascript // src/composables/useBrainyWithErrorHandling.js import { ref } from 'vue' export function useBrainyWithErrorHandling(endpoint = '/api/brain') { const error = ref(null) const search = async (query, options = {}, maxRetries = 3) => { error.value = null for (let attempt = 0; attempt <= maxRetries; attempt++) { try { const res = await fetch(`${endpoint}/search`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query, options }) }) if (!res.ok) throw new Error(`Search failed: ${res.status}`) return (await res.json()).results } catch (err) { error.value = err.message if (attempt === maxRetries) throw err // Exponential backoff before retrying await new Promise(resolve => setTimeout(resolve, 2000 * (attempt + 1))) } } } return { error: readonly(error), search } } ``` ## ๐ŸŽฏ Complete Example Here's a complete Vue 3 application structure: ``` my-brainy-vue-app/ โ”œโ”€โ”€ server/ # Server-side (Node/Bun) โ€” hosts Brainy โ”‚ โ”œโ”€โ”€ utils/ โ”‚ โ”‚ โ””โ”€โ”€ brain.js # Shared Brainy instance (getBrain) โ”‚ โ””โ”€โ”€ api/brain/ โ”‚ โ”œโ”€โ”€ search.post.js โ”‚ โ”œโ”€โ”€ add.post.js โ”‚ โ””โ”€โ”€ stats.get.js โ”œโ”€โ”€ src/ โ”‚ โ”œโ”€โ”€ components/ โ”‚ โ”‚ โ”œโ”€โ”€ Search.vue โ”‚ โ”‚ โ”œโ”€โ”€ DataManager.vue โ”‚ โ”‚ โ””โ”€โ”€ RealTimeSearch.vue โ”‚ โ”œโ”€โ”€ composables/ โ”‚ โ”‚ โ”œโ”€โ”€ useBrainy.js โ”‚ โ”‚ โ””โ”€โ”€ useBrainyWithErrorHandling.js โ”‚ โ”œโ”€โ”€ stores/ โ”‚ โ”‚ โ””โ”€โ”€ brainy.js โ”‚ โ”œโ”€โ”€ utils/ โ”‚ โ”‚ โ””โ”€โ”€ debounce.js โ”‚ โ”œโ”€โ”€ App.vue โ”‚ โ””โ”€โ”€ main.js โ”œโ”€โ”€ tests/ โ”‚ โ”œโ”€โ”€ components/ โ”‚ โ””โ”€โ”€ e2e/ โ”œโ”€โ”€ vite.config.js โ””โ”€โ”€ package.json ``` This provides a complete, production-ready Vue.js application: Brainy runs on the server, and the client talks to it over HTTP. ## ๐Ÿ“š Next Steps - [Framework Integration Guide](framework-integration.md) - Multi-framework patterns - [Production Deployment](../deployment/CLOUD_DEPLOYMENT_GUIDE.md) - Deploy to production - [API Reference](../api/README.md) - Complete API documentation - [Examples Repository](https://github.com/soulcraftlabs/brainy-examples) - More examples