Skip to main content
Version: 0.12.0

JavaScript client

The Lucenia Node.js client (@lucenia/client) lets you talk to a Lucenia cluster from JavaScript or TypeScript instead of building raw HTTP requests. It is derived from the OpenSearch JavaScript client, ships its own TypeScript definitions, and includes helpers for bulk indexing, multi-search, and scrolling.

Compatibility

The client requires Node.js 14 or later. The npm package version tracks the Lucenia server version: client 0.12.x is intended for use with Lucenia 0.12.x clusters.

Installation

Install the client from npm:

npm install @lucenia/client

For production projects, pin the minor version in package.json:

{
"dependencies": {
"@lucenia/client": "~0.12.1"
}
}

Then import the client. In CommonJS:

const { Client } = require('@lucenia/client');

Or as an ES module:

import { Client } from '@lucenia/client';

Creating a client

Connecting to a secured cluster

The default Lucenia configuration runs with TLS and basic authentication enabled. The snippet below creates a client suitable for local development against a cluster using self-signed certificates. Configure a real CA bundle and enable certificate verification in production.

const { Client } = require('@lucenia/client');

const client = new Client({
node: 'https://localhost:9200',
auth: {
username: 'admin',
password: 'MyStrongPassword123!',
},
ssl: {
rejectUnauthorized: false,
},
});

const info = await client.info();
console.log(`Connected to ${info.body.version.distribution} ${info.body.version.number}`);
warning

rejectUnauthorized: false disables TLS verification and should only be used for local development. In production, supply your cluster's CA certificate instead.

To verify certificates, pass a CA bundle:

const fs = require('fs');
const { Client } = require('@lucenia/client');

const client = new Client({
node: 'https://localhost:9200',
auth: {
username: 'admin',
password: 'MyStrongPassword123!',
},
ssl: {
ca: fs.readFileSync('/path/to/root-ca.pem'),
},
});

Authenticating with an API key

Lucenia 0.12 and later can authenticate requests with a native, revocable API key. Pass the api_key value returned when the key is created — it is already base64 encoded, so do not encode it again. Always use HTTPS when sending an API key.

const client = new Client({
node: 'https://localhost:9200',
auth: {
apiKey: process.env.LUCENIA_API_KEY,
},
});

Connecting to an unsecured cluster

For development clusters running without TLS or authentication:

const client = new Client({ node: 'http://localhost:9200' });

Working with documents

Every API call resolves to a response object whose payload is on .body.

Creating an index

Create an index with custom settings and mappings:

const indexName = 'my-index';

const response = await client.indices.create({
index: indexName,
body: {
settings: {
index: {
number_of_shards: 1,
number_of_replicas: 0,
},
},
mappings: {
properties: {
title: { type: 'text' },
text: { type: 'text' },
year: { type: 'integer' },
},
},
},
});

console.log(`Index created: ${response.body.acknowledged}`);

Indexing a single document

const response = await client.index({
index: indexName,
id: '1',
body: {
title: 'Introduction to Lucenia',
text: 'Lucenia is a high-performance search engine.',
year: 2025,
},
refresh: true,
});

console.log(response.body.result);

Bulk indexing

For higher throughput, use the helpers.bulk helper. It batches documents, retries failures, and returns statistics rather than a raw bulk response. onDocument decides the action for each document, and refreshOnCompletion makes the new documents searchable when the run finishes:

const documents = [
{ title: 'Getting Started', text: 'Use the @lucenia/client package.', year: 2025 },
{ title: 'Search Features', text: 'Full-text search and more.', year: 2025 },
{ title: 'Bulk Operations', text: 'Index many docs at once.', year: 2025 },
];

const stats = await client.helpers.bulk({
datasource: documents,
onDocument() {
return { index: { _index: indexName } };
},
refreshOnCompletion: true,
});

console.log(`Indexed ${stats.successful} documents (failed: ${stats.failed})`);

datasource also accepts a stream or an async iterator, so you can index a large file without holding it in memory.

Searching

Run a match query against a specific field:

const response = await client.search({
index: indexName,
body: {
query: {
match: { title: 'Lucenia' },
},
},
});

console.log(`Found ${response.body.hits.total.value} documents:`);
for (const hit of response.body.hits.hits) {
console.log(` [${hit._id}] score=${hit._score} ${hit._source.title}`);
}

Run a multi_match query that boosts matches in the title field:

const response = await client.search({
index: indexName,
body: {
query: {
multi_match: {
query: 'search',
fields: ['title^2', 'text'],
},
},
},
});

For pagination, sorting, point-in-time, and scroll search, see the Query DSL documentation. The client also exposes helpers.scrollSearch and helpers.scrollDocuments for iterating large result sets.

Deleting a document and the index

await client.delete({ index: indexName, id: '1' });

await client.indices.delete({ index: indexName });

TypeScript

The package ships its own type definitions, so no @types package is needed:

import { Client } from '@lucenia/client';

interface Book {
title: string;
year: number;
}

const client = new Client({ node: 'https://localhost:9200' });

const response = await client.search<Book>({
index: 'my-index',
body: { query: { match_all: {} } },
});

Authenticating with AWS SigV4

For a managed cluster that authenticates with AWS Signature V4, use the signer exported from the aws subpath (aws-v3 for the AWS SDK v3):

const { Client } = require('@lucenia/client');
const { AwsSigv4Signer } = require('@lucenia/client/aws');

Next steps

The client supports a wider set of features beyond the basics covered here, including:

  • Bulk indexing helpers with configurable concurrency and retries
  • Multi-search (helpers.msearch)
  • Scroll and point-in-time search helpers
  • AWS SigV4 request signing
  • Long numeral support for values that exceed JavaScript's safe integer range
  • Connection pooling, sniffing, and custom transports