blob: 10121a7ce5b7561cc8b33f064b0a38d042ed52d1 [file] [view]
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
# Node.js Native API
The Node.js native API supports interacting with the IoTDB tree model through `Session` and `SessionPool`, enabling data insertion, queries, non-query SQL, and connection management. Since `Session` is not thread-safe, `SessionPool` is recommended for production environments. Under high concurrency, `SessionPool` manages connection resources in a unified way and supports multi-node load balancing, failover, and write redirection.
This document focuses on the usage of `SessionPool`, covering environment preparation, core steps, and common interfaces.
## 1. Environment Preparation
### 1.1 Prerequisites
* Node.js >= 14.0.0
* npm >= 6.0.0
* IoTDB >= 2.0.11
### 1.2 Installation
* **Option 1: Install directly via npm (recommended)**
Run the following command in your Node.js project:
```bash
npm install @iotdb/client
```
* **Option 2: Build from source**
To use the development version from the repository, clone the source code and install dependencies:
```bash
git clone https://github.com/apache/iotdb-client-nodejs.git
cd iotdb-client-nodejs
git checkout develop
npm ci
```
On Linux, macOS, or WSL, run:
```bash
npm run build
```
On Windows PowerShell, run:
```powershell
npm run build:esbuild
npm run build:types
New-Item -ItemType Directory -Force -Path dist\thrift\generated
Copy-Item src\thrift\generated\*.js,src\thrift\generated\*.d.ts -Destination dist\thrift\generated -Force
```
After the build is complete, you can install the client locally in your business project via the absolute path of the client source directory:
```bash
npm install /absolute/path/to/iotdb-client-nodejs
```
If you use TypeScript, no additional type declarations are required. The client comes with complete TypeScript type definitions built in.
**Note: Do not use a higher-version client to connect to a lower-version server.**
## 2. Core Steps
The three core steps of operating the IoTDB tree model with the Node.js native API are as follows:
1. Create a connection pool instance: initialize a `SessionPool` object with connection parameters and pool size.
2. Execute database operations: perform data insertion, queries, or non-query SQL directly through the connection pool.
3. Close the connection pool: call `pool.close()` when the program ends to release all connections.
The following sections describe the core development workflow and do not demonstrate all parameters and interfaces. For the complete capabilities, refer to the [`@iotdb/client` source code](https://github.com/apache/iotdb-client-nodejs/tree/develop/src) and [examples](https://github.com/apache/iotdb-client-nodejs/tree/develop/examples).
### 2.1 Creating a Connection Pool Instance
#### 2.1.1 Single-Node Connection
```typescript
import { SessionPool } from '@iotdb/client';
const pool = new SessionPool('localhost', 6667, {
username: 'root',
password: 'root',
maxPoolSize: 10,
minPoolSize: 2,
maxIdleTime: 60000,
waitTimeout: 60000,
});
await pool.init();
```
#### 2.1.2 Multi-Node Connection
In a cluster environment, it is recommended to configure multiple nodes with `nodeUrls`. The connection pool distributes connections across nodes in a round-robin manner and tries other available nodes when a connection fails.
```typescript
import { SessionPool } from '@iotdb/client';
const pool = new SessionPool({
nodeUrls: [
'192.168.1.100:6667',
'192.168.1.101:6667',
'192.168.1.102:6667',
],
username: 'root',
password: 'root',
maxPoolSize: 15,
minPoolSize: 3,
});
await pool.init();
```
You can also create the connection pool configuration with the builder pattern:
```typescript
import { SessionPool, PoolConfigBuilder } from '@iotdb/client';
const pool = new SessionPool(
new PoolConfigBuilder()
.nodeUrls([
'192.168.1.100:6667',
'192.168.1.101:6667',
'192.168.1.102:6667',
])
.username('root')
.password('root')
.maxPoolSize(15)
.minPoolSize(3)
.build()
);
await pool.init();
```
Connection pool parameters can be adjusted according to your workload: `minPoolSize` is recommended to be set to the average concurrent load, while `maxPoolSize` should be set to the peak concurrent load with a 20% to 30% buffer. `maxIdleTime` is used to clean up long-idle connections, and `waitTimeout` controls the maximum waiting time when the pool is exhausted. In production, it is recommended to monitor `getPoolSize()`, `getAvailableSize()`, and `getInUseSize()`, and adjust the pool size based on peak load.
#### 2.1.3 SSL/TLS Connection
If SSL/TLS is enabled on the IoTDB server, you can enable SSL when creating the connection pool and specify certificate-related parameters.
```typescript
import { SessionPool } from '@iotdb/client';
import * as fs from 'fs';
const pool = new SessionPool({
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 pool.init();
```
#### 2.1.4 Write Redirection
In a multi-node IoTDB cluster, the client supports write redirection. When a write operation is sent to a non-target node, the server may return a redirection hint. The client caches the device-to-node mapping and prefers the target node for subsequent writes to the same device.
```typescript
import { SessionPool } from '@iotdb/client';
const pool = new SessionPool({
nodeUrls: [
'192.168.1.100:6667',
'192.168.1.101:6667',
'192.168.1.102:6667',
],
username: 'root',
password: 'root',
maxPoolSize: 10,
enableRedirection: true,
redirectCacheTTL: 300000,
});
await pool.init();
```
With redirection enabled, cross-node forwarding is reduced, improving write throughput and lowering network latency. This capability applies to device-level write scenarios in the tree model.
### 2.2 Database Operations
#### 2.2.1 Creating a Database and Time Series
```typescript
await pool.executeNonQueryStatement('CREATE DATABASE root.test');
await pool.executeNonQueryStatement(
'CREATE TIMESERIES root.test.device1.temperature WITH DATATYPE=FLOAT, ENCODING=RLE'
);
await pool.executeNonQueryStatement(
'CREATE TIMESERIES root.test.device1.humidity WITH DATATYPE=FLOAT, ENCODING=RLE'
);
```
#### 2.2.2 Inserting Tablet Data
`insertTablet` supports batch insertion of multiple rows per device. `values` is a two-dimensional array organized by row: each row corresponds to a timestamp, consistent with the order of `timestamps`; each column corresponds to a measurement, consistent with the order of `measurements`.
```typescript
await pool.insertTablet({
deviceId: 'root.test.device1',
measurements: ['temperature', 'humidity'],
dataTypes: [3, 3],
timestamps: [Date.now(), Date.now() + 1000],
values: [
[25.5, 60.0],
[26.0, 61.5],
],
});
```
Here `dataTypes` can use data type codes, which can also be encapsulated as constants in your project. See Chapter 4 of this document for common type codes.
For data insertion, it is recommended to use `insertTablet` for batch writes to reduce network round trips. A common batch size to start with is 100 to 1000 rows, then adjust based on data volume, network, and server resources.
#### 2.2.3 Querying Data
Query results are returned through `SessionDataSet`, which supports paged fetching and is suitable for large result sets. Call `close()` after use to release server-side query resources.
```typescript
const dataSet = await pool.executeQueryStatement(
'SELECT temperature, humidity FROM root.test.device1'
);
while (await dataSet.hasNext()) {
const row = dataSet.next();
console.log(row.getTimestamp(), row.getFields());
}
await dataSet.close();
```
For small result sets, you can also use `toArray()` to load all results into memory:
```typescript
const dataSet = await pool.executeQueryStatement('SHOW DATABASES');
const rows = await dataSet.toArray();
console.log(rows);
await dataSet.close();
```
### 2.3 Closing the Connection Pool
```typescript
await pool.close();
```
It is recommended to close the connection pool uniformly when the application exits, a scheduled task ends, or the service is destroyed, to avoid connection leaks.
## 3. Common Interfaces
### 3.1 SessionPool
#### 3.1.1 Description
`SessionPool` is the recommended connection pool interface for the tree model and supports automatic session management. When a query, insert, or non-query method is called, the pool automatically acquires an available `Session` and recycles the connection after execution.
#### 3.1.2 Constructors
| Constructor | Description |
| --- | --- |
| `new SessionPool(hosts, port, config)` | Traditional constructor, suitable for single-node or multi-host configurations with the same port |
| `new SessionPool(config)` | Constructed with a configuration object, suitable for `nodeUrls` multi-node configurations |
| `new SessionPool(new PoolConfigBuilder().build())` | Constructed with the builder pattern, recommended when there are many parameters |
#### 3.1.3 Methods
| Method | Description |
| --- | --- |
| `init()` | Initializes the connection pool |
| `close()` | Closes the connection pool and releases all connections |
| `executeQueryStatement(sql, timeoutMs?)` | Executes a query SQL, with an optional query timeout |
| `executeNonQueryStatement(sql)` | Executes a non-query SQL, such as DDL or DML |
| `insertTablet(tablet)` | Inserts Tablet data |
| `getSession()` | Acquires a `Session` from the pool, which must be returned manually |
| `releaseSession(session)` | Returns a manually acquired `Session` to the pool |
| `getPoolSize()` | Gets the current pool size |
| `getAvailableSize()` | Gets the current number of available connections |
| `getInUseSize()` | Gets the current number of connections in use |
#### 3.1.4 Configuration Options
| Option | Description |
| --- | --- |
| `host` | Host address |
| `port` | Port |
| `nodeUrls` | Multiple node addresses, in the format `host:port` |
| `username` | User name |
| `password` | Password |
| `database` | Default database |
| `timezone` | Time zone |
| `fetchSize` | Batch fetch size for query results |
| `maxPoolSize` | Maximum number of connections |
| `minPoolSize` | Minimum number of connections |
| `maxIdleTime` | Maximum idle time in milliseconds |
| `waitTimeout` | Wait timeout for acquiring a connection, in milliseconds |
| `enableSSL` | Whether to enable SSL |
| `sslOptions` | SSL options |
| `enableRedirection` | Whether to enable write redirection |
| `redirectCacheTTL` | Redirection cache expiration time in milliseconds |
### 3.2 Session
#### 3.2.1 Description
`Session` represents an independent session, suitable for simple scripts or single-threaded scenarios. `Session` is not thread-safe; use `SessionPool` for multi-threaded or high-concurrency scenarios.
#### 3.2.2 Methods
| Method | Description |
| --- | --- |
| `open()` | Opens the session |
| `close()` | Closes the session |
| `executeQueryStatement(sql, timeoutMs?)` | Executes a query SQL |
| `executeNonQueryStatement(sql)` | Executes a non-query SQL |
| `insertTablet(tablet)` | Inserts Tablet data |
| `isOpen()` | Checks whether the session is open |
## 4. Data Types
When inserting Tablet data, you need to specify the corresponding data type for each measurement. The common types supported by the Node.js client are as follows:
| Type Code | Type Name | JavaScript Type | Description |
| --- | --- | --- | --- |
| `0` | `BOOLEAN` | `boolean` | Boolean value |
| `1` | `INT32` | `number` | 32-bit integer |
| `2` | `INT64` | `bigint` | 64-bit integer |
| `3` | `FLOAT` | `number` | 32-bit floating point |
| `4` | `DOUBLE` | `number` | 64-bit floating point |
| `5` | `TEXT` | `string` | UTF-8 text |
| `8` | `TIMESTAMP` | `Date` | Millisecond-precision timestamp |
| `9` | `DATE` | `Date` | Date type |
| `10` | `BLOB` | `Buffer` | Binary data |
| `11` | `STRING` | `string` | UTF-8 string |
When handling the `INT64` type, it is recommended to use `bigint` in JavaScript to avoid precision loss when values exceed the safe integer range of `number`.
## 5. FAQ
1. Connection refused: If `ECONNREFUSED` occurs, check whether the IoTDB service is running, whether the RPC port is correct, and whether the network and firewall allow access. The default port is usually `6667`.
2. Connection acquisition timeout: If waiting for an available connection times out, the pool is usually exhausted. Increase `waitTimeout` or `maxPoolSize` as appropriate, and check whether a manually acquired `Session` is not returned via `releaseSession(session)`.
3. Query results consume too much memory: For large result sets, it is recommended to read in batches with `hasNext()` and `next()` and reduce `fetchSize`. Use `toArray()` only for small result sets.
4. Unstable write performance: It is recommended to use `insertTablet` for batch writes and adjust the number of rows per batch based on data volume, network, and server resources. In multi-node environments, configure `nodeUrls` so that the pool distributes load across nodes.