Support nodeUrls in string array format (host:port) for simpler multi-node configuration
10 files changed
tree: 2215eb4cfa1faece33ee0aaf8fa71181dd6466c9
  1. .github/
  2. examples/
  3. src/
  4. tests/
  5. thrift/
  6. .gitignore
  7. BUILD_INFRASTRUCTURE_ANALYSIS.md
  8. CHANGELOG.md
  9. CI_CD_SUMMARY.md
  10. COMMENT_RESPONSE_SUMMARY.md
  11. CONTRIBUTING.md
  12. DATA_TYPES.md
  13. DATA_TYPES_IMPLEMENTATION_SUMMARY.md
  14. docker-compose-1c1d.yml
  15. docker-compose-3c3d.yml
  16. E2E_FIX_ANALYSIS.md
  17. esbuild.config.js
  18. eslint.config.mjs
  19. ESLINT_9_UPGRADE.md
  20. GLOB_FIX_DETAILS.md
  21. IMPLEMENTATION.md
  22. IMPROVEMENTS_SUMMARY.md
  23. jest.config.js
  24. LICENSE
  25. NPM_DEPRECATIONS_FIXED.md
  26. package-lock.json
  27. package.json
  28. PROJECT_STATUS.md
  29. README.md
  30. tsconfig.json
README.md

Apache IoTDB Node.js Client

License

A Node.js client for Apache IoTDB with support for SessionPool and TableSessionPool, providing efficient connection management and comprehensive query capabilities.

Features

  • Session Management: Single session with query, non-query, and insertTablet operations
  • SessionPool: Connection pooling for high-concurrency scenarios
  • TableSessionPool: Specialized pool for table model operations
  • Multi-Node Support: Round-robin load balancing across multiple IoTDB nodes
  • SSL/TLS Support: Secure connections with customizable SSL options
  • TypeScript Support: Full TypeScript definitions included
  • Comprehensive Testing: Unit and E2E tests included

Installation

npm install iotdb-client-nodejs

Requirements

  • Node.js >= 14.0.0
  • Apache IoTDB >= 1.0.0

Quick Start

Basic Session Usage

import { Session } from 'iotdb-client-nodejs';

const session = new Session({
  host: 'localhost',
  port: 6667,
  username: 'root',
  password: 'root',
});

await session.open();

// Execute non-query statement
await session.executeNonQueryStatement('CREATE DATABASE root.test');

// Execute query statement with default timeout (60 seconds)
const result = await session.executeQueryStatement('SHOW DATABASES');
console.log('Columns:', result.columns);
console.log('Rows:', result.rows);

// Execute query with custom timeout (30 seconds)
const customResult = await session.executeQueryStatement('SELECT * FROM root.test.**', 30000);

// Insert tablet data
await session.insertTablet({
  deviceId: 'root.test.device1',
  measurements: ['temperature', 'humidity'],
  dataTypes: [3, 3], // FLOAT
  timestamps: [Date.now(), Date.now() + 1000],
  values: [
    [25.5, 60.0],
    [26.0, 61.5],
  ],
});

await session.close();

Using Builder Pattern (Recommended)

The Builder pattern provides a more elegant and fluent API for configuration:

import { Session, ConfigBuilder } from 'iotdb-client-nodejs';

// Build a session configuration
const session = new Session(
  new ConfigBuilder()
    .host('localhost')
    .port(6667)
    .username('root')
    .password('root')
    .fetchSize(1024)
    .timezone('UTC+8')
    .build()
);

await session.open();
// ... use session
await session.close();

SessionPool Usage

import { SessionPool } from 'iotdb-client-nodejs';

const pool = new SessionPool('localhost', 6667, {
  username: 'root',
  password: 'root',
  maxPoolSize: 10,
  minPoolSize: 2,
  maxIdleTime: 60000,
  waitTimeout: 60000,
});

await pool.init();

// Execute queries using the pool
const result = await pool.executeQueryStatement('SELECT * FROM root.test.**');

// Execute non-query statements
await pool.executeNonQueryStatement(
  'CREATE TIMESERIES root.test.device1.temperature WITH DATATYPE=FLOAT'
);

// Insert data
await pool.insertTablet({
  deviceId: 'root.test.device1',
  measurements: ['temperature'],
  dataTypes: [3], // FLOAT
  timestamps: [Date.now()],
  values: [[25.5]],
});

// Get pool statistics
console.log('Pool size:', pool.getPoolSize());
console.log('Available:', pool.getAvailableSize());

await pool.close();

Explicit Session Management

For more control, you can explicitly get and release sessions from the pool:

import { SessionPool } from 'iotdb-client-nodejs';

const pool = new SessionPool('localhost', 6667, {
  username: 'root',
  password: 'root',
  maxPoolSize: 10,
});

await pool.init();

// Get a session from the pool
const session = await pool.getSession();

try {
  // Use the session for multiple operations
  await session.executeNonQueryStatement('CREATE DATABASE root.test');
  const result = await session.executeQueryStatement('SHOW DATABASES');
  await session.insertTablet({
    deviceId: 'root.test.device1',
    measurements: ['temperature'],
    dataTypes: [3],
    timestamps: [Date.now()],
    values: [[25.5]],
  });
} finally {
  // Always release the session back to the pool
  pool.releaseSession(session);
}

await pool.close();

Multi-Node Support

Method 1: Using nodeUrls with String Format (Recommended)

When nodes have different host:port combinations, use the nodeUrls configuration with string array format:

import { SessionPool, PoolConfigBuilder } from 'iotdb-client-nodejs';

// Using config object with string array (RECOMMENDED)
const pool1 = new SessionPool({
  nodeUrls: [
    'node1.example.com:6667',
    'node2.example.com:6668',
    'node3.example.com:6669',
  ],
  username: 'root',
  password: 'root',
  maxPoolSize: 15,
  minPoolSize: 3,
});

// Or using Builder pattern with string array
const pool2 = new SessionPool(
  new PoolConfigBuilder()
    .nodeUrls([
      'node1.example.com:6667',
      'node2.example.com:6668',
      'node3.example.com:6669',
    ])
    .username('root')
    .password('root')
    .maxPoolSize(15)
    .minPoolSize(3)
    .build()
);

await pool1.init();
// Connections will be distributed across all nodes using round-robin

Method 2: Using nodeUrls with Object Format (Also Supported)

You can also use the object format for nodeUrls:

const pool = new SessionPool({
  nodeUrls: [
    { host: 'node1.example.com', port: 6667 },
    { host: 'node2.example.com', port: 6668 },
    { host: 'node3.example.com', port: 6669 },
  ],
  username: 'root',
  password: 'root',
  maxPoolSize: 15,
});

Method 3: Traditional API (For Same Port)

When all nodes share the same port:

import { SessionPool } from 'iotdb-client-nodejs';

const pool = new SessionPool(
  ['node1.example.com', 'node2.example.com', 'node3.example.com'],
  6667,
  {
    username: 'root',
    password: 'root',
    maxPoolSize: 15,
  }
);

await pool.init();
// Connections will be distributed across all nodes using round-robin

SSL/TLS Support

import { Session } from 'iotdb-client-nodejs';
import * as fs from 'fs';

const session = new Session({
  host: 'localhost',
  port: 6667,
  username: 'root',
  password: 'root',
  enableSSL: true,
  sslOptions: {
    ca: fs.readFileSync('/path/to/ca.crt'),
    cert: fs.readFileSync('/path/to/client.crt'),
    key: fs.readFileSync('/path/to/client.key'),
    rejectUnauthorized: true,
  },
});

await session.open();

TableSessionPool Usage

import { TableSessionPool } from 'iotdb-client-nodejs';

const tablePool = new TableSessionPool('localhost', 6667, {
  username: 'root',
  password: 'root',
  database: 'my_database', // Set default database for table model
  maxPoolSize: 10,
  minPoolSize: 2,
});

await tablePool.init();

// Execute queries in table mode
const result = await tablePool.executeQueryStatement('SHOW DATABASES');

await tablePool.close();

API Reference

Configuration Builders

ConfigBuilder

Fluent API for building Session configurations:

import { ConfigBuilder } from 'iotdb-client-nodejs';

const config = new ConfigBuilder()
  .host('localhost')
  .port(6667)
  .username('root')
  .password('root')
  .database('mydb')
  .timezone('UTC+8')
  .fetchSize(2048)
  .enableSSL(false)
  .build();

Methods:

  • host(host: string): this - Set the host
  • port(port: number): this - Set the port
  • nodeUrls(nodeUrls: EndPoint[]): this - Set multiple node URLs
  • username(username: string): this - Set the username
  • password(password: string): this - Set the password
  • database(database: string): this - Set the database
  • timezone(timezone: string): this - Set the timezone
  • fetchSize(fetchSize: number): this - Set the fetch size
  • enableSSL(enable: boolean): this - Enable or disable SSL
  • sslOptions(sslOptions: SSLOptions): this - Set SSL options
  • build(): Config - Build and return the configuration

PoolConfigBuilder

Fluent API for building SessionPool configurations (extends ConfigBuilder):

import { PoolConfigBuilder } from 'iotdb-client-nodejs';

const config = new PoolConfigBuilder()
  .host('localhost')
  .port(6667)
  .username('root')
  .password('root')
  .maxPoolSize(20)
  .minPoolSize(5)
  .maxIdleTime(30000)
  .waitTimeout(45000)
  .build();

Additional Methods:

  • maxPoolSize(size: number): this - Set maximum pool size
  • minPoolSize(size: number): this - Set minimum pool size
  • maxIdleTime(time: number): this - Set maximum idle time (ms)
  • waitTimeout(timeout: number): this - Set wait timeout (ms)
  • build(): PoolConfig - Build and return the pool configuration

Data Types

IoTDB Node.js client supports all IoTDB data types including BOOLEAN, INT32, INT64, FLOAT, DOUBLE, TEXT, BLOB, STRING, DATE, and TIMESTAMP. See DATA_TYPES.md for comprehensive documentation on:

  • Type mappings between JavaScript and IoTDB
  • Usage examples for each data type
  • Best practices and encoding options
  • Null value handling

Session

Constructor

Option 1: Using config object

new Session(config: Config)

Option 2: Using Builder pattern (Recommended)

new Session(new ConfigBuilder()...build())

The config must include either:

  • host and port for single node connection
  • nodeUrls for multi-node connection (uses first node)

Methods

  • async open(): Promise<void> - Open the session
  • async close(): Promise<void> - Close the session
  • async executeQueryStatement(sql: string, timeoutMs?: number): Promise<QueryResult> - Execute a query with optional timeout (default: 60000ms)
  • async executeNonQueryStatement(sql: string): Promise<void> - Execute a non-query statement
  • async insertTablet(tablet: Tablet): Promise<void> - Insert tablet data
  • isOpen(): boolean - Check if session is open

SessionPool

Constructor

Option 1: Traditional API (Backward compatible)

new SessionPool(hosts: string | string[], port: number, config?: Partial<PoolConfig>)

Option 2: Using config object with nodeUrls

new SessionPool(config: PoolConfig)

Option 3: Using Builder pattern (Recommended)

new SessionPool(new PoolConfigBuilder()...build())

Methods

Connection Management:

  • async init(): Promise<void> - Initialize the pool
  • async close(): Promise<void> - Close all connections

Automatic Session Management:

  • async executeQueryStatement(sql: string, timeoutMs?: number): Promise<QueryResult> - Execute a query with optional timeout (default: 60000ms)
  • async executeNonQueryStatement(sql: string): Promise<void> - Execute a non-query statement
  • async insertTablet(tablet: Tablet): Promise<void> - Insert tablet data

Explicit Session Management:

  • async getSession(): Promise<Session> - Get a session from the pool (must be released)
  • releaseSession(session: Session): void - Release a session back to the pool

Pool Statistics:

  • getPoolSize(): number - Get current pool size
  • getAvailableSize(): number - Get available connections
  • getInUseSize(): number - Get number of sessions currently in use

TableSessionPool

Same as SessionPool but optimized for table model operations. Automatically executes USE DATABASE when configured with a database. All query methods support the same timeout parameter (default: 60000ms).

Constructor

Same constructor options as SessionPool.

Types

Config

interface Config {
  host?: string;
  port?: number;
  nodeUrls?: string[] | EndPoint[];  // String array format: ["host1:6667", "host2:6668"]
  username?: string;
  password?: string;
  database?: string;
  timezone?: string;
  fetchSize?: number;
  enableSSL?: boolean;
  sslOptions?: SSLOptions;
}

Note: Either host/port OR nodeUrls must be provided.

  • Use nodeUrls in string array format (e.g., ["host1:6667", "host2:6668"]) for nodes with different ports (RECOMMENDED)
  • Object format [{ host, port }] is also supported for backward compatibility

EndPoint

interface EndPoint {
  host: string;
  port: number;
}

PoolConfig

interface PoolConfig extends Config {
  maxPoolSize?: number;
  minPoolSize?: number;
  maxIdleTime?: number;
  waitTimeout?: number;
}

Tablet

interface Tablet {
  deviceId: string;
  measurements: string[];
  dataTypes: number[]; // 0=BOOLEAN, 1=INT32, 2=INT64, 3=FLOAT, 4=DOUBLE, 5=TEXT
  timestamps: number[];
  values: any[][];
}

QueryResult

interface QueryResult {
  columns: string[];
  dataTypes: string[];
  rows: any[][];
  queryId?: number;
}

Data Types

When using insertTablet, specify data types using these constants:

  • 0 - BOOLEAN
  • 1 - INT32
  • 2 - INT64
  • 3 - FLOAT
  • 4 - DOUBLE
  • 5 - TEXT

Migration Guide

Upgrading to the New API

The new version maintains full backward compatibility while adding new features. No changes are required for existing code, but you may want to adopt the new features:

Multi-Node with Different Ports

Old way (still works, but limited to same port):

const pool = new SessionPool(
  ['node1', 'node2', 'node3'],
  6667,
  { username: 'root', password: 'root' }
);

New way (supports different ports per node with string format - RECOMMENDED):

const pool = new SessionPool({
  nodeUrls: [
    'node1:6667',
    'node2:6668',
    'node3:6669',
  ],
  username: 'root',
  password: 'root',
});

Alternative (object format also supported):

const pool = new SessionPool({
  nodeUrls: [
    { host: 'node1', port: 6667 },
    { host: 'node2', port: 6668 },
    { host: 'node3', port: 6669 },
  ],
  username: 'root',
  password: 'root',
});

Using Builder Pattern

Old way (still works):

const session = new Session({
  host: 'localhost',
  port: 6667,
  username: 'root',
  password: 'root',
  fetchSize: 2048,
});

New way (more fluent):

import { ConfigBuilder } from 'iotdb-client-nodejs';

const session = new Session(
  new ConfigBuilder()
    .host('localhost')
    .port(6667)
    .username('root')
    .password('root')
    .fetchSize(2048)
    .build()
);

Explicit Session Management

Old way (still works):

// Pool automatically manages sessions
const result = await pool.executeQueryStatement('SELECT ...');

New way (more control):

// Explicitly get and release sessions
const session = await pool.getSession();
try {
  const result1 = await session.executeQueryStatement('SELECT ...');
  const result2 = await session.executeQueryStatement('SELECT ...');
  // ... multiple operations with same session
} finally {
  pool.releaseSession(session);
}

Development

Build

npm install
npm run build

Run Tests

# Run all tests
npm test

# Run unit tests only
npm run test:unit

# Run E2E tests (requires IoTDB instance)
export IOTDB_HOST=localhost
export IOTDB_PORT=6667
npm run test:e2e

Linting

npm run lint

Examples

See the examples/ directory for more usage examples:

  • examples/basic-session.ts - Basic session usage
  • examples/session-pool.ts - SessionPool usage
  • examples/table-session-pool.ts - TableSessionPool usage
  • examples/multi-node.ts - Multi-node configuration
  • examples/ssl-connection.ts - SSL/TLS connection

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

Apache License 2.0

References