feathers-elasticsearch API Documentation
Table of Contents
- Installation
- Quick Start
- Configuration
- Service Methods
- Query Operators
- Special Features
- Error Handling
- TypeScript Support
Installation
npm install feathers-elasticsearch @elastic/elasticsearch
Quick Start
import { Client } from '@elastic/elasticsearch' import service from 'feathers-elasticsearch' // Initialize Elasticsearch client const client = new Client({ node: 'http://localhost:9200' }) // Create service const peopleService = service({ Model: client, index: 'people', id: 'id', paginate: { default: 10, max: 100 } }) // Use in Feathers app app.use('/people', peopleService)
Configuration
Service Options
| Option | Type | Required | Description |
|---|---|---|---|
Model |
Client |
Yes | Elasticsearch client instance |
index |
string |
No | Default index name |
id |
string |
No | ID field name (default: '_id') |
parent |
string |
No | Parent field name for parent-child relationships |
routing |
string |
No | Routing field name |
join |
string |
No | Join field name for parent-child relationships |
meta |
string |
No | Metadata field name (default: '_meta') |
esVersion |
string |
No | Elasticsearch version (e.g., '8.0') |
esParams |
object |
No | Default Elasticsearch parameters |
paginate |
object |
No | Pagination configuration |
whitelist |
string[] |
No | Allowed query operators |
multi |
boolean|string[] |
No | Allow multi operations |
Example Configuration
const service = service({ Model: client, index: 'products', id: 'productId', esVersion: '8.0', esParams: { refresh: true, timeout: '30s' }, paginate: { default: 20, max: 50 }, multi: true, whitelist: ['$match', '$phrase', '$prefix'] })
Service Methods
find(params)
Find multiple documents matching the query.
// Basic find const results = await service.find({ query: { status: 'active', category: 'electronics' } }) // With pagination const page = await service.find({ query: { status: 'active' }, paginate: { default: 10, max: 50 } }) // Returns: { total, limit, skip, data } // Without pagination const all = await service.find({ query: { status: 'active' }, paginate: false })
get(id, params)
Get a single document by ID.
const doc = await service.get('doc123') // With selected fields const doc = await service.get('doc123', { query: { $select: ['name', 'email'] } })
create(data, params)
Create one or more documents.
// Single document const created = await service.create({ name: 'John Doe', email: 'john@example.com' }) // With specific ID const created = await service.create({ id: 'user123', name: 'John Doe', email: 'john@example.com' }) // Bulk creation const items = await service.create([ { name: 'John', age: 30 }, { name: 'Jane', age: 25 } ]) // With upsert const doc = await service.create({ id: 'doc123', name: 'Updated' }, { upsert: true })
update(id, data, params)
Replace a document entirely.
const updated = await service.update('doc123', { name: 'Jane Doe', email: 'jane@example.com', age: 28 }) // With upsert const doc = await service.update('doc123', { name: 'New Document' }, { upsert: true })
patch(id, data, params)
Partially update one or more documents.
// Single document const patched = await service.patch('doc123', { status: 'inactive' }) // Bulk patch const results = await service.patch( null, { archived: true }, { query: { createdAt: { $lt: '2023-01-01' } } } )
remove(id, params)
Remove one or more documents.
// Single document const removed = await service.remove('doc123') // Bulk removal const results = await service.remove(null, { query: { status: 'deleted' } })
raw(method, params)
Execute raw Elasticsearch API methods.
// Direct search const results = await service.raw('search', { body: { query: { match_all: {} }, aggs: { categories: { terms: { field: 'category' } } } } }) // Index operations const mapping = await service.raw('indices.getMapping')
Query Operators
Comparison Operators
| Operator | Description | Example |
|---|---|---|
$gt |
Greater than | { age: { $gt: 18 } } |
$gte |
Greater than or equal | { age: { $gte: 18 } } |
$lt |
Less than | { age: { $lt: 65 } } |
$lte |
Less than or equal | { age: { $lte: 65 } } |
$ne |
Not equal | { status: { $ne: 'deleted' } } |
$in |
In array | { status: { $in: ['active', 'pending'] } } |
$nin |
Not in array | { status: { $nin: ['deleted', 'archived'] } } |
Text Search Operators
| Operator | Description | Example |
|---|---|---|
$match |
Full-text match | { title: { $match: 'elasticsearch' } } |
$phrase |
Phrase match | { title: { $phrase: 'quick brown fox' } } |
$phrase_prefix |
Phrase prefix | { title: { $phrase_prefix: 'quick br' } } |
$prefix |
Term prefix | { username: { $prefix: 'john' } } |
$wildcard |
Wildcard pattern | { email: { $wildcard: '*@example.com' } } |
$regexp |
Regular expression | { phone: { $regexp: '^\\+1.*' } } |
Logical Operators
// $or { $or: [ { status: 'active' }, { priority: 'high' } ] } // $and { $and: [ { status: 'active' }, { category: 'electronics' } ] } // Combined { status: 'active', $or: [ { priority: 'high' }, { deadline: { $lt: '2024-01-01' } } ] }
Special Operators
$all (Match All)
{ $all: true } // Returns all documents
$sqs (Simple Query String)
{ $sqs: { $query: 'nodejs elasticsearch', $fields: ['title', 'description'], $operator: 'and' // Optional: 'and' or 'or' } }
$exists / $missing
{ $exists: ['email', 'phone'] } // Documents with these fields { $missing: ['deletedAt'] } // Documents without these fields
$nested (Nested Documents)
{ $nested: { $path: 'comments', 'comments.author': 'John', 'comments.rating': { $gte: 4 } } }
$child / $parent (Parent-Child Relationships)
// Find child documents { $child: { $type: 'comment', author: 'John' } } // Find parent documents { $parent: { $type: 'post', status: 'published' } }
Special Features
Pagination
// Default pagination const page1 = await service.find({ query: { status: 'active' } }) // Custom pagination const page2 = await service.find({ query: { status: 'active', $limit: 20, $skip: 20 } }) // Disable pagination const all = await service.find({ query: { status: 'active' }, paginate: false })
Sorting
{ query: { $sort: { createdAt: -1, // Descending name: 1 // Ascending } } }
Field Selection
{ query: { $select: ['name', 'email', 'status'] } }
Index Routing
// Query specific index { query: { $index: 'products-2024' } } // With routing { query: { $routing: 'user123' } }
Bulk Operations
// Bulk create const docs = await service.create([{ name: 'Doc1' }, { name: 'Doc2' }, { name: 'Doc3' }]) // Bulk patch const updated = await service.patch( null, { status: 'archived' }, { query: { createdAt: { $lt: '2023-01-01' } } } ) // Bulk remove const removed = await service.remove(null, { query: { status: 'deleted' } })
Error Handling
The service throws Feathers errors that can be caught and handled:
try { const doc = await service.get('nonexistent') } catch (error) { if (error.name === 'NotFound') { // Handle not found } } // Error types: // - BadRequest (400): Invalid query or parameters // - NotFound (404): Document not found // - Conflict (409): Document already exists // - GeneralError (500): Elasticsearch errors
TypeScript Support
The service exports comprehensive TypeScript types:
import service, { ElasticsearchServiceOptions, ElasticsearchServiceParams, ElasticsearchDocument, ESSearchResponse, QueryOperators, ServiceResult, PaginatedResult } from 'feathers-elasticsearch' // Typed service creation const typedService = service<User>({ Model: client, index: 'users' }) // Typed queries const users: User[] = await typedService.find({ query: { age: { $gte: 18 }, status: 'active' } }) // Custom document type interface User extends ElasticsearchDocument { name: string email: string age: number status: 'active' | 'inactive' }
Advanced Examples
Complex Query with Aggregations
const results = await service.raw('search', { body: { query: { bool: { must: [{ term: { status: 'active' } }], filter: [{ range: { age: { gte: 18 } } }] } }, aggs: { age_groups: { histogram: { field: 'age', interval: 10 } } } } })
Parent-Child Relationships
// Setup service with join const service = service({ Model: client, index: 'blog', join: 'post_comment', parent: 'post_id' }) // Create parent document const post = await service.create({ id: 'post1', title: 'My Post', join: 'post' }) // Create child document const comment = await service.create({ content: 'Great post!', parent: 'post1', join: { name: 'comment', parent: 'post1' } })
Retry Configuration
import { createRetryWrapper } from 'feathers-elasticsearch/utils' // Wrap client with retry logic const retryClient = createRetryWrapper(client, { maxRetries: 3, initialDelay: 100, backoffMultiplier: 2 }) const service = service({ Model: retryClient, index: 'products' })
Migration Guide
From v2 to v3
- Update to Feathers v5 (Dove)
- Use new TypeScript types
- Update error handling (errors are now properly thrown)
- Use new query operators format
// Old (v2) service.find({ query: { $search: 'text' } }) // New (v3) service.find({ query: { $match: 'text' } })
Performance Tips
-
Use field selection to reduce data transfer:
{ query: { $select: ['id', 'name'] } }
-
Enable refresh only when needed:
esParams: { refresh: false } // Default
-
Use bulk operations for multiple documents:
service.create([...documents]) // Instead of multiple create calls
-
Leverage Elasticsearch caching:
service.raw('search', { request_cache: true, body: { ... } })
-
Use appropriate pagination limits:
paginate: { default: 20, max: 100 }
Support
- GitHub Issues: Report bugs
- Documentation: Full documentation
- Feathers Discord: Community support