GridGain Developers Hub
GitHub logo GridGain iso GridGain.com
GridGain Software Documentation

Node.js Thin Client

Prerequisites

Once node and npm are installed, you can use one of the following installation options.

Installation

Installing via npm

Execute the following command to install the Node.js thin client package:

npm install -g apache-ignite-client

Installing from Sources

The source files of the Node.js thin client come with the GridGain distribution package under the {GRIDGAIN_HOME}/platforms/nodejs/ directory.

If you want to build and install the thin client from the sources, follow these steps:

  1. Go to the {GRIDGAIN_HOME}/platforms/nodejs folder.

  2. Execute the npm link command.

  3. Give a name to the client library by executing the npm link 'gridgain-client' command.

Creating a Client Instance

The thin client API is provided the the IgniteClient class. You can obtain an instance of the client as follows:

const IgniteClient = require('gridgain-client');

const igniteClient = new IgniteClient(onStateChanged);

function onStateChanged(state, reason) {
    if (state === IgniteClient.STATE.CONNECTED) {
        console.log('Client is started');
    }
    else if (state === IgniteClient.STATE.DISCONNECTED) {
        console.log('Client is stopped');
        if (reason) {
            console.log(reason);
        }
    }
}

The constructor accepts one optional parameter that represents a callback function that is called every time the connection state changes (see below).

You can create as many IgniteClient instances as needed. All of them will work independently.

Connecting to Cluster

To connect the client to a cluster, use the IgniteClient.connect() method. It accepts an object of the IgniteClientConfiguration class that represents connection parameters. The connection parameters must contain a list of nodes (in the host:port format) that will be used for failover purposes.

const IgniteClient = require('gridgain-client');
const IgniteClientConfiguration = IgniteClient.IgniteClientConfiguration;

async function connectClient() {
    const igniteClient = new IgniteClient(onStateChanged);
    try {
        const igniteClientConfiguration = new IgniteClientConfiguration(
            '127.0.0.1:10800', '127.0.0.1:10801', '127.0.0.1:10802');
        // connect to Ignite node
        await igniteClient.connect(igniteClientConfiguration);
    }
    catch (err) {
        console.log(err.message);
    }
}

function onStateChanged(state, reason) {
    if (state === IgniteClient.STATE.CONNECTED) {
        console.log('Client is started');
    }
    else if (state === IgniteClient.STATE.CONNECTING) {
        console.log('Client is connecting');
    }
    else if (state === IgniteClient.STATE.DISCONNECTED) {
        console.log('Client is stopped');
        if (reason) {
            console.log(reason);
        }
    }
}

connectClient();

The client has three connection states: CONNECTING, CONNECTED, DISCONNECTED. You can specify a callback fuction in the client configuration object which will be called every time the connection state changes.

Any interactions with the cluster are only possible in the CONNECTED state. If the client looses the connection, it automatically switches to the CONNECTING state and tries to re-connect using the failover mechanims. If it fails to reconnect to all the endpoints from the provided list, the client switches to the DISCONNECTED state.

You can call the disconnect() method to close the connection. This will switch the client to the DISCONNECTED state.

Enabling Debug

const IgniteClient = require('gridgain-client');

const igniteClient = new IgniteClient();
igniteClient.setDebug(true);

Using Key-Value API

Getting Cache Instance

The key-value API is provided through an instance of a cache. The thin client provides several methods for obtaining a cache instance:

  • get a cache by its name,

  • create a cache with a specified name and optional cache configuration,

  • get or create a cache, destroys a cache, etc.

You can obtain as many cache instances as needed - for the same or different caches - and work with all of them in parallel.

The following example shows how to get access to a cache by name and destroy its later:

const IgniteClient = require('gridgain-client');
const IgniteClientConfiguration = IgniteClient.IgniteClientConfiguration;

async function getOrCreateCacheByName() {
    const igniteClient = new IgniteClient();
    try {
        await igniteClient.connect(new IgniteClientConfiguration('127.0.0.1:10800'));
        // get or create cache by name
        const cache = await igniteClient.getOrCreateCache('myCache');

        // perform cache key-value operations
        // ...

        // destroy cache
        await igniteClient.destroyCache('myCache');
    }
    catch (err) {
        console.log(err.message);
    }
    finally {
        igniteClient.disconnect();
    }
}

getOrCreateCacheByName();

Cache Configuration

When creating a new cache, you can provide an instance of cache configuration.

const IgniteClient = require('gridgain-client');
const IgniteClientConfiguration = IgniteClient.IgniteClientConfiguration;
const CacheConfiguration = IgniteClient.CacheConfiguration;

async function createCacheByConfiguration() {
    const igniteClient = new IgniteClient();
    try {
        await igniteClient.connect(new IgniteClientConfiguration('127.0.0.1:10800'));
        // create cache by name and configuration
        const cache = await igniteClient.createCache(
            'myCache',
            new CacheConfiguration().setSqlSchema('PUBLIC'));
    }
    catch (err) {
        console.log(err.message);
    }
    finally {
        igniteClient.disconnect();
    }
}

createCacheByConfiguration();

Type Mapping Configuration

The node.js types do not always uniquely map to the java types, and in some cases you may want to specify the key and value types in the cache configuration explisitly. The client will using these types to convert the key and value objects between java/javascript data types when executing read/write cache operations.

If you don’s specify the the types, the client will use the Default Mapping. This is an example of type mapping:

const cache = await igniteClient.getOrCreateCache('myCache');
// set cache key/value types
cache.setKeyType(ObjectType.PRIMITIVE_TYPE.INTEGER)
    .setValueType(new MapObjectType(
        MapObjectType.MAP_SUBTYPE.LINKED_HASH_MAP,
        ObjectType.PRIMITIVE_TYPE.SHORT,
        ObjectType.PRIMITIVE_TYPE.BYTE_ARRAY));

Data Types

The client supports two ways of type mapping between Ignite types and JavaScript types:

  • Explicit mapping

  • Default mapping

Explicit Mapping

A mapping occurs every time an application writes or reads a field to/from an Ignite via the client’s API. The field here is any data stored in Ignite - the whole key or value of an Ignite entry, an element of an array or set, a field of a complex object, etc.

Using the client’s API methods, an application can explicitly specify an Ignite type for a particular field. The client uses this information to transform the field from JavaScript to Java type and vice verse during the read/write operations. The field is transformed to JavaScript type as a result of read operations. It validates the corresponding JavaScript type in inputs of write operations.

If an application does not explicitly specify an Ignite type for a field, the client uses default mapping during the field read/write operations.

Default Mapping

The default mapping is explained here.

Basic Key-Value Operations

The CacheClient class provides methods for working with the cache entries using key-value operations - put, get, put all, get all, replace and others. The following example shows how to do that:

const IgniteClient = require('gridgain-client');
const IgniteClientConfiguration = IgniteClient.IgniteClientConfiguration;
const ObjectType = IgniteClient.ObjectType;
const CacheEntry = IgniteClient.CacheEntry;

async function performCacheKeyValueOperations() {
    const igniteClient = new IgniteClient();
    try {
        await igniteClient.connect(new IgniteClientConfiguration('127.0.0.1:10800'));
        const cache = (await igniteClient.getOrCreateCache('myCache')).
        setKeyType(ObjectType.PRIMITIVE_TYPE.INTEGER);
        // put and get value
        await cache.put(1, 'abc');
        const value = await cache.get(1);
        console.log(value);

        // put and get multiple values using putAll()/getAll() methods
        await cache.putAll([new CacheEntry(2, 'value2'), new CacheEntry(3, 'value3')]);
        const values = await cache.getAll([1, 2, 3]);
        console.log(values.flatMap(val => val.getValue()));

        // removes all entries from the cache
        await cache.clear();
    }
    catch (err) {
        console.log(err.message);
    }
    finally {
        igniteClient.disconnect();
    }
}

performCacheKeyValueOperations();

Scan Queries

The IgniteClient.query(scanquery) method can be used to fetch all entries from the cache. It returns a cursor object that can be used to iterate over a result set lazily or to get all results at once.

To execute a scan query, create a ScanQuery object and call IgniteClient.query(scanquery):

const cache = (await igniteClient.getOrCreateCache('myCache')).
    setKeyType(ObjectType.PRIMITIVE_TYPE.INTEGER);

// put multiple values using putAll()
await cache.putAll([
    new CacheEntry(1, 'value1'),
    new CacheEntry(2, 'value2'),
    new CacheEntry(3, 'value3')]);

// create and configure scan query
const scanQuery = new ScanQuery().
    setPageSize(1);
// obtain scan query cursor
const cursor = await cache.query(scanQuery);
// getAll cache entries returned by the scan query
for (let cacheEntry of await cursor.getAll()) {
    console.log(cacheEntry.getValue());
}

Executing SQL Statements

The Node.js thin client supports all SQL commands that are supported by GridGain. The commands are executed via the query(SqlFieldQuery) method of the cache object. The method accepts an instance of SqlFieldsQuery that represents a SQL statement and return an instance of the SqlFieldsCursor class. Use the cursor to iterate over the result set or get all results at once.

// cache configuration required for sql query execution
const cacheConfiguration = new CacheConfiguration().
    setQueryEntities(
        new QueryEntity().
            setValueTypeName('Person').
            setFields([
                new QueryField('name', 'java.lang.String'),
                new QueryField('salary', 'java.lang.Double')
            ]));
const cache = (await igniteClient.getOrCreateCache('sqlQueryPersonCache', cacheConfiguration)).
    setKeyType(ObjectType.PRIMITIVE_TYPE.INTEGER).
    setValueType(new ComplexObjectType({ 'name' : '', 'salary' : 0 }, 'Person'));

// put multiple values using putAll()
await cache.putAll([
    new CacheEntry(1, { 'name' : 'John Doe', 'salary' : 1000 }),
    new CacheEntry(2, { 'name' : 'Jane Roe', 'salary' : 2000 }),
    new CacheEntry(3, { 'name' : 'Mary Major', 'salary' : 1500 })]);

// create and configure sql query
const sqlQuery = new SqlQuery('Person', 'salary > ? and salary <= ?').
    setArgs(900, 1600);
// obtain sql query cursor
const cursor = await cache.query(sqlQuery);
// getAll cache entries returned by the sql query
for (let cacheEntry of await cursor.getAll()) {
    console.log(cacheEntry.getValue());
}

Security

SSL/TLS

To use encrypted communication between the thin client and the cluster, you have to enable it both in the cluster configuration and the client configuration. Refer to the Enabling SSL/TLS for Thin Clients section for the instruction on the cluster configuration.

Below is an example configuration for enabling SSL in the thin client:

const tls = require('tls');

const FS = require('fs');
const IgniteClient = require("gridgain-client");
const ObjectType = IgniteClient.ObjectType;
const IgniteClientConfiguration = IgniteClient.IgniteClientConfiguration;

const ENDPOINT = 'localhost:10800';
const USER_NAME = 'gridgain';
const PASSWORD = 'gridgain';

const TLS_KEY_FILE_NAME = __dirname + '/certs/client.key';
const TLS_CERT_FILE_NAME = __dirname + '/certs/client.crt';
const TLS_CA_FILE_NAME = __dirname + '/certs/ca.crt';

const CACHE_NAME = 'AuthTlsExample_cache';

// This example demonstrates how to establish a secure connection to an Ignite node and use username/password authentication,
// as well as basic Key-Value Queries operations for primitive types:
// - connects to a node using TLS and providing username/password
// - creates a cache, if it doesn't exist
//   - specifies key and value type of the cache
// - put data of primitive types into the cache
// - get data from the cache
// - destroys the cache
class AuthTlsExample {

    async start() {
        const igniteClient = new IgniteClient(this.onStateChanged.bind(this));
        igniteClient.setDebug(true);
        try {
            const connectionOptions = {
                'key' : FS.readFileSync(TLS_KEY_FILE_NAME),
                'cert' : FS.readFileSync(TLS_CERT_FILE_NAME),
                'ca' : FS.readFileSync(TLS_CA_FILE_NAME)
            };
            await igniteClient.connect(new IgniteClientConfiguration(ENDPOINT).
            setUserName(USER_NAME).
            setPassword(PASSWORD).
            setConnectionOptions(true, connectionOptions));

            const cache = (await igniteClient.getOrCreateCache(CACHE_NAME)).
            setKeyType(ObjectType.PRIMITIVE_TYPE.INTEGER).
            setValueType(ObjectType.PRIMITIVE_TYPE.SHORT_ARRAY);

            await this.putGetData(cache);

            await igniteClient.destroyCache(CACHE_NAME);
        }
        catch (err) {
            console.log('ERROR: ' + err.message);
        }
        finally {
            igniteClient.disconnect();
        }
    }

    async putGetData(cache) {
        let keys = [1, 2, 3];
        let values = keys.map(key => this.generateValue(key));

        // put multiple values in parallel
        await Promise.all([
            await cache.put(keys[0], values[0]),
            await cache.put(keys[1], values[1]),
            await cache.put(keys[2], values[2])
        ]);
        console.log('Cache values put successfully');

        // get values sequentially
        let value;
        for (let i = 0; i < keys.length; i++) {
            value = await cache.get(keys[i]);
            if (!this.compareValues(value, values[i])) {
                console.log('Unexpected cache value!');
                return;
            }
        }
        console.log('Cache values get successfully');
    }

    compareValues(array1, array2) {
        return array1.length === array2.length &&
            array1.every((value1, index) => value1 === array2[index]);
    }

    generateValue(key) {
        const length = key + 5;
        const result = new Array(length);
        for (let i = 0; i < length; i++) {
            result[i] = key * 10 + i;
        }
        return result;
    }

    onStateChanged(state, reason) {
        if (state === IgniteClient.STATE.CONNECTED) {
            console.log('Client is started');
        }
        else if (state === IgniteClient.STATE.DISCONNECTED) {
            console.log('Client is stopped');
            if (reason) {
                console.log(reason);
            }
        }
    }
}

const authTlsExample = new AuthTlsExample();
authTlsExample.start().then();

Authentication

Configure authentication on the cluster side and provide a valid user name and password in the client configuration.

const ENDPOINT = 'localhost:10800';
const USER_NAME = 'gridgain';
const PASSWORD = 'gridgain';

const igniteClientConfiguration = new IgniteClientConfiguration(
    ENDPOINT).setUserName(USER_NAME).setPassword(PASSWORD);

Authorization

Thin client authorization can be configured in the cluster. Refer to the Authorization page for details.