[![npm version][npm-version-src]][npm-version-href] [![npm downloads][npm-downloads-src]][npm-downloads-href] [![License][license-src]][license-href]
npm install @sfxcode/nuxt-typesense[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]
[![License][license-src]][license-href]
A Nuxt module for integrating Typesense - a fast, typo-tolerant search engine - into your Nuxt 3 application.
> š Read the full documentation for detailed guides, API references, and examples.
- š Full Typesense API Support - Complete TypeScript API client auto-generated from Typesense OpenAPI spec
- šÆ Auto-imported Composables - Ready-to-use composables for all Typesense operations
- ā”ļø Type Safety - Full TypeScript support with type definitions
- š§ Zero Config - Simple setup with module configuration
- šØ SSR Compatible - Works seamlessly with Nuxt's SSR
1. Add @sfxcode/nuxt-typesense dependency to your project:
``bashUsing pnpm
pnpm add @sfxcode/nuxt-typesense
2. Add
@sfxcode/nuxt-typesense to the modules section of nuxt.config.ts:`typescript
export default defineNuxtConfig({
modules: [
'@sfxcode/nuxt-typesense'
],
typesense: {
url: 'http://localhost:8108', // Your Typesense server URL
apiKey: 'xyz' // Your Typesense API key
}
})
`3. You can also use environment variables:
`typescript
export default defineNuxtConfig({
modules: [
'@sfxcode/nuxt-typesense'
],
typesense: {
url: process.env.TYPESENSE_URL,
apiKey: process.env.TYPESENSE_API_KEY,
}
})
`Create a
.env file:`bash
TYPESENSE_URL=http://localhost:8108
TYPESENSE_API_KEY=your-api-key-here
`That's it! You can now use Typesense in your Nuxt app āØ
Usage
$3
> ā ļø Important: Use search-only API keys on the client-side and admin keys only on the server-side.
The module works seamlessly in both contexts:
| Operation | Client-Side | Server-Side | Recommended Key |
|-----------|-------------|-------------|-----------------|
| Search documents | ā
| ā
| Search-only key |
| Get collections | ā
| ā
| Search-only key |
| Create collections | ā | ā
| Admin key |
| Manage API keys | ā | ā
| Admin key |
| Import documents | ā | ā
| Admin key |
$3
Use composables in your Vue components for search and read operations:
`vue
Searching...
Error: {{ error.message }}
Found {{ results?.found }} products
{{ hit.document.name }}
${{ hit.document.price }}
`#### Helper Composables for Client-Side
`vue
`$3
Use the full API in server routes for admin operations:
`typescript
// server/api/collections/create.post.ts
export default defineEventHandler(async (event) => {
const { collectionsApi } = useTypesenseApi()
// Use admin key for creating collections
const collection = await collectionsApi.createCollection({
collectionSchema: {
name: 'products',
fields: [
{ name: 'id', type: 'string' },
{ name: 'name', type: 'string' },
{ name: 'price', type: 'float', sort: true },
{ name: 'category', type: 'string', facet: true }
],
default_sorting_field: 'id'
}
})
return { success: true, collection }
})
`#### Import Documents (Server-Only)
`typescript
// server/api/products/import.post.ts
export default defineEventHandler(async (event) => {
const { documentsApi } = useTypesenseApi()
const products = await readBody(event)
// Convert to JSONL format
const jsonl = products.map((p: any) => JSON.stringify(p)).join('\n')
// Batch import (requires admin key)
const result = await documentsApi.importDocuments({
collectionName: 'products',
body: jsonl,
action: 'upsert'
})
return { success: true, result }
})
`#### Manage API Keys (Server-Only)
`typescript
// server/api/keys/create.post.ts
export default defineEventHandler(async (event) => {
const { keysApi } = useTypesenseApi()
// Create a search-only key (requires admin key)
const key = await keysApi.createKey({
apiKeySchema: {
description: 'Search-only key for frontend',
actions: ['documents:search'],
collections: ['products']
}
})
return { key: key.value }
})
`$3
Recommended approach for production:
`typescript
// server/api/search.ts - Server endpoint with admin key
export default defineEventHandler(async (event) => {
const query = getQuery(event)
const { documentsApi } = useTypesenseApi()
const results = await documentsApi.multiSearch({
searches: [{
collection: 'products',
q: query.q as string,
query_by: 'name,description',
filter_by: query.filter as string
}]
})
return results.results[0]
})
``vue
`$3
All operations through
useTypesenseApi():`typescript
const {
analyticsApi, // Analytics and event tracking
collectionsApi, // Collection management (server-side for create/delete)
conversationsApi, // Conversation model management
curationSetsApi, // Curation and overrides
debugApi, // Debug operations
documentsApi, // Document CRUD and search (search ok for client)
healthApi, // Health check endpoint
keysApi, // API key management (server-side only)
searchModelsApi, // NL search models
operationsApi, // Cluster operations (server-side only)
presetsApi, // Search presets
stemmingApi, // Stemming dictionaries
stopwordsApi, // Stopwords management
synonymsApi, // Synonyms management
} = useTypesenseApi()
`
Configuration
$3
`typescript
interface ModuleOptions {
url?: string // Typesense server URL
apiKey?: string // Typesense API key
}
`$3
For client-side search operations, use a search-only API key:
`typescript
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@sfxcode/nuxt-typesense'],
typesense: {
url: process.env.TYPESENSE_URL,
apiKey: process.env.TYPESENSE_SEARCH_KEY // Search-only key
}
})
``bash
.env
TYPESENSE_URL=https://xxx.a1.typesense.net
TYPESENSE_SEARCH_KEY=search-only-api-key-here
`$3
For applications with both search and admin operations:
`typescript
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@sfxcode/nuxt-typesense'],
// Public search key (exposed to client)
typesense: {
url: process.env.TYPESENSE_URL,
apiKey: process.env.TYPESENSE_SEARCH_KEY
},
// Private admin key (server-only)
runtimeConfig: {
typesense: {
adminKey: process.env.TYPESENSE_ADMIN_KEY
}
}
})
``bash
.env
TYPESENSE_URL=https://xxx.a1.typesense.net
TYPESENSE_SEARCH_KEY=search-only-key # Client-safe
TYPESENSE_ADMIN_KEY=admin-key-secret # Server-only
`Use the admin key in server routes:
`typescript
// server/api/admin/collection.post.ts
export default defineEventHandler(async (event) => {
const config = useRuntimeConfig(event)
// Override with admin key for this request
const { collectionsApi } = useTypesenseApi()
// Note: You may need to create a new API instance with admin key
// or implement a server-side only composable
const collection = await collectionsApi.createCollection({
collectionSchema: { / ... / }
})
return collection
})
`$3
| ā
Do | ā Don't |
|-------|----------|
| Use search-only keys in
nuxt.config.ts | Expose admin keys to the client |
| Store admin keys in runtimeConfig | Hardcode API keys in source code |
| Use server API routes for admin operations | Perform admin operations from client |
| Create scoped keys per user/tenant | Share API keys between environments |
| Rotate keys regularly | Commit keys to version control |> š Security Rule: If it modifies data, it belongs on the server with an admin key.
Common Patterns
$3
`vue
Loading...
{{ results.found }} results
{{ hit.document.name }}
`$3
`typescript
// server/api/seed.post.ts
export default defineEventHandler(async (event) => {
const { collectionsApi, documentsApi } = useTypesenseApi()
// 1. Create collection (if not exists)
try {
await collectionsApi.createCollection({
collectionSchema: {
name: 'products',
fields: [
{ name: 'id', type: 'string' },
{ name: 'name', type: 'string' },
{ name: 'price', type: 'float' }
],
default_sorting_field: 'id'
}
})
} catch (e) {
// Collection might already exist
}
// 2. Import data
const products = [
{ id: '1', name: 'Laptop', price: 999 },
{ id: '2', name: 'Mouse', price: 29 }
]
const jsonl = products.map(p => JSON.stringify(p)).join('\n')
await documentsApi.importDocuments({
collectionName: 'products',
body: jsonl,
action: 'upsert'
})
return { success: true }
})
`$3
Best for production - keeps admin key secure:
`typescript
// server/api/products/search.get.ts
export default defineEventHandler(async (event) => {
const query = getQuery(event)
const { documentsApi } = useTypesenseApi()
const results = await documentsApi.multiSearch({
searches: [{
collection: 'products',
q: (query.q as string) || '*',
query_by: 'name,description',
filter_by: query.category ? category:=${query.category} : undefined,
per_page: 20
}]
})
return results.results[0]
})
``vue
`$3
`typescript
// server/api/products/[id].put.ts
export default defineEventHandler(async (event) => {
const id = getRouterParam(event, 'id')
const updates = await readBody(event)
const { documentsApi } = useTypesenseApi()
// Update in your database first
await updateProductInDB(id, updates)
// Then sync to Typesense
await documentsApi.updateDocument({
collectionName: 'products',
documentId: id!,
body: updates
})
return { success: true }
})
`Development
$3
`bash
Clone the repository
git clone https://github.com/sfxcode/nuxt-typesense.git
cd nuxt-typesenseInstall dependencies
pnpm installGenerate type stubs
pnpm run dev:prepareStart the playground in development mode
pnpm run dev
`$3
`bash
Run playground in development mode
pnpm run devBuild the playground
pnpm run dev:buildBuild the module
pnpm run prepackRun tests
pnpm run testRun tests in watch mode
pnpm run test:watchLint code
pnpm run lintRegenerate API client from OpenAPI spec
pnpm run api-codegenRelease a new version
pnpm run release
`$3
`
nuxt-typesense/
āāā src/
ā āāā module.ts # Module definition
ā āāā runtime/
ā āāā api/ # Auto-generated Typesense API clients
ā āāā composables/ # Auto-imported composables
ā ā āāā typesenseApi.ts
ā ā āāā typesenseApiKey.ts
ā ā āāā typesenseUrl.ts
ā ā āāā typeSenseCollection.ts
ā āāā model/ # TypeScript models
āāā playground/ # Development playground app
āāā test/ # Test files
`$3
The API client is auto-generated from the Typesense OpenAPI specification. To update it:
`bash
pnpm run api-codegen
`This will fetch the latest OpenAPI spec from Typesense and regenerate all TypeScript API clients.
Documentation
Full documentation is available in the
docs/ directory and includes:- Getting Started Guide - Installation and setup instructions
- Configuration - Module options and environment variables
- Core Concepts - Collections, documents, search, and API keys
- API Reference - Complete composables and client documentation
- Examples - Real-world usage examples
$3
`bash
Start documentation dev server
pnpm run docs:devBuild documentation
pnpm run docs:buildPreview built documentation
pnpm run docs:preview
`Visit http://localhost:5173 to view the documentation.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
1. Fork the repository
2. Create your feature branch (
git checkout -b feature/amazing-feature)
3. Commit your changes (git commit -m 'feat: add amazing feature')
4. Push to the branch (git push origin feature/amazing-feature`)- š Typesense Documentation
- š® Typesense API Explorer
- š¬ Typesense Slack Community
- š GitHub Repository
MIT License Ā© 2024 sfxcode
[npm-version-src]: https://img.shields.io/npm/v/@sfxcode/nuxt-typesense/latest.svg?style=flat&colorA=18181B&colorB=28CF8D
[npm-version-href]: https://npmjs.com/package/@sfxcode/nuxt-typesense
[npm-downloads-src]: https://img.shields.io/npm/dm/@sfxcode/nuxt-typesense.svg?style=flat&colorA=18181B&colorB=28CF8D
[npm-downloads-href]: https://npmjs.com/package/@sfxcode/nuxt-typesense
[license-src]: https://img.shields.io/npm/l/@sfxcode/nuxt-typesense.svg?style=flat&colorA=18181B&colorB=28CF8D
[license-href]: https://npmjs.com/package/@sfxcode/nuxt-typesense