Promise based deepstream client. It's basically the deepstream.io-client-js
with basic calls promisified, plus some extra methods. All methods are added as polyfills.
npm i -S extended-ds-client
Creating a client through this package will give you additional methods on the client object, leaving everything from the default client untouched (getRecord, getList etc).
These are the additional functions:
p.login
(alias:loginP
)record.p.getRecord
(alias:record.getRecordP
)record.p.getList
(alias:record.getListP
)record.p.setData
(alias:record.setDataP
)record.p.snapshot
(alias:record.snapshotP
)record.p.has
(alias:record.hasP
)rpc.p.make
(alias:rpc.makeP
)record.p.getExistingRecord
(alias:record.getExistingRecordP
)record.p.getExistingList
(alias:record.getExistingListP
)record.p.deleteRecord
(alias:record.deleteRecordP
)record.p.deleteList
(alias:record.deleteListP
)record.p.addToList
(alias:record.addToListP
)record.p.removeFromList
(alias:record.removeFromListP
)record.p.updateExistingRecord
(alias:record.updateExistingRecordP
)record.p.getDatasetRecord
(alias:record.getDatasetRecordP
)
In case of rejection on any of these functions, the rejected argument is always an instance of Error.
There is also a utility function to import from this module:
addEntry
(prevents duplicates)
Tunneling export of CONSTANTS
& MERGE_STRATEGIES
(so that you don't also have to import deepstream.io-client-js for these).
import getClient from 'extended-ds-client';
const client = getClient('localhost:6020');
client.p
.login({})
.then(data => {
console.log('Successful login.', data);
})
.catch(data => {
console.log('Login failed.', data);
});
const users = {};
client.record.p.getList('users').then(list => {
// With the list of entries, map each entry to a record promise and
// wait for all to get finished:
Promise.all(list.getEntries().map(path => client.record.p.getRecord(path))).then(records =>
records.forEach(record => {
// Now save all data etc
const user = record.get();
users[user.id] = user;
}),
);
});
// Default functions still work
client.record.getList('article/x35b/comments').whenReady(l => {
// ...
});
import getClient, { CONSTANTS, ds } from 'extended-ds-client';
ds.client = getClient('localhost:6020'); // use singleton feature
ds.client.loginP({}); // using alias pattern (instead of p.login)
console.log(CONSTANTS); // MERGE_STRATEGIES is also available as import
// Given Example 2 file is imported together with this one
import { ds } from 'extended-ds-client';
// And why not use async-await now...
async function fetchUsers() {
const l = await ds.client.record.p.getExistingList('users');
const records = await Promise.all(
l.getEntries().map(path => ds.client.record.p.getExistingRecord(path)),
);
const users = {};
records.forEach(r => {
const user = r.get();
users[user.id] = user;
});
return users;
}
Default export from this module is the function getClient
to create a client (just like deepstream() in original client). In the same way it also accepts an options object.
const client = getClient('localhost:6020', {
datasetRecordFullPaths: false,
datasetRecordIdKey: 'rid',
splitChar: '.',
});
Option | Type | Default | Description |
---|---|---|---|
datasetRecordFullPaths |
boolean |
true | Full paths in lists (or only id) for datasetRecord methods. |
datasetRecordIdKey |
string |
'id' |
ID key for dataset records. |
splitChar |
string |
'/' |
Splitting character in paths (for dataset records). |
The promises will be resolved with the same argument(s) as the default client callbacks would get (or the whenReady
when applicable). See the deepstream JS client documentation.
Alias: loginP
Straightforward promisification of login. See Example 1 above.
Note: Default login still works, you can still do the standard line with callback:
client.login({}, success => console.log(success));
Alias: record.getRecordP
Promisification of record.getRecord
.
client.record.p.getRecord(name)
.then(dsRecord => ...)
.catch(error => ...);
Alias: record.getListP
Promisification of record.getList
.
client.record.p.getList(name)
.then(dsList => ...)
.catch(error => ...);
Alias: record.setDataP
Promisification of record.setData
.
client.record.p.setData(name, path, data)
.then(() => ...)
.catch(error => ...);
Alias: record.snapshotP
Promisification of record.snapshot
.
client.record.p.snapshot(name)
.then(record => ...)
.catch(error => ...);
Alias: record.hasP
Promisification of record.has
, but will reject if it does not exist (and on error of course).
A second optional argument makes the check inverted -- rejecting on existing record. (This can be handy to make sure an id is free).
client.record.p.has(name)
.then(() => ...)
.catch(error => ...);
await client.record.p.has('record1');
// if we get here, record1 is a non-taken record id
Alias: rpc.makeP
Promisification of rpc.make
.
client.rpc.p.make(name, data)
.then(result => ...)
.catch(error => ...);
Alias: record.getExistingRecordP
Additional method that does a .has
-check before .getRecord
to get a record handler without implicit record creation (Compare with snapshot
that also fails if the record does not exist, but returns the actual record instead of a record handler). It rejects the promise if the record does not exist.
client.record.p.getExistingRecord(name)
.then(dsRecord => ...)
.catch(error => ...);
Alias: record.getExistingListP
Like p.getExistingRecord
above, but for List.
client.record.p.getExistingList(name)
.then(dsList => ...)
.catch(error => ...);
Alias: record.deleteRecordP
Will resolve when the delete event is emitted (avoiding the race condition risk).
client.record.p.deleteRecord(arg)
.then(() => ...)
.catch(error => ...);
Argument | Type | Default | Description |
---|---|---|---|
arg |
string /Object |
The path to the record OR a DS Record object. |
Alias: record.deleteListP
Like p.deleteRecord
above, but for List.
client.record.p.deleteList(arg)
.then(() => ...)
.catch(error => ...);
Argument | Type | Default | Description |
---|---|---|---|
arg |
string /Object |
The path to the list OR a DS List object. |
Alias: record.addToListP
Add an entry or multiple entries to an existing list, without duplicates.
client.record.p.addToList('books', 'selfish-gene')
.then(dsList => ...)
.catch(error => ...);
Argument | Type | Default | Description |
---|---|---|---|
listPath |
string |
The name/path of the record. | |
id |
string /Array |
Entry(ies) to add. |
Alias: record.removeFromListP
Remove an entry or multiple entries from an existing list.
client.record.p.removeFromList('books', 'selfish-gene')
.then(dsList => ...)
.catch(error => ...);
Argument | Type | Default | Description |
---|---|---|---|
listPath |
string |
The name/path of the record. | |
id |
string /Array |
Entry(ies) to add. |
Alias: record.updateExistingRecordP
Update a record, choosing from one of multiple update modes:
shallow
: Default mode. Overwrite each property at the base level.overwrite
: Replace the whole record.deep
: Deep merge of theupdates
into record (usinglodash.merge
).deepConcat
: Deep merge, but concatenate arrays with only simple values.deepConcatAll
: Merge likedeepConcat
, but concatenate ALL arrays.deepIgnore
: Deep merge, but leave unchanged if some value inupdates
is'%IGNORE%'
.deepConcatIgnore
: Merge likedeepConcat
, but skip values likedeepIgnore
.removeKeys
: Withupdates
as an array, remove corresponding keys in record.
Two additional array arguments, lockedKeys
and protectedKeys
, makes it possible to lock or protect given keys, regardless of update mode. Locked keys won't be altered and protected ones won't be removed.
client.record.p.updateExistingRecord('record1', { a: 1, data: { b: 2 }})
.then(() => ...)
.catch(error => ...);
client.record.p.updateExistingRecord(
'records/r-x',
{ data: { configs: [{ items: ['toBeConcatenated'] }] } },
'deepConcat',
)
.then(() => ...)
.catch(error => ...);
client.record.p.updateExistingRecord(
'record1',
{ data: { confs: ['%IGNORE%', { items: ['replacingFirstItem'] }] }, id: 'xb24' },
'deepIgnore',
['id']
)
.then(() => ...)
.catch(error => ...);
Argument | Type | Default | Description |
---|---|---|---|
name |
string |
The name/path of the record. | |
updates |
Object /Array |
The updates. Array for mode = removeKeys |
|
mode |
string |
"shallow" |
Update mode, see details above. |
lockedKeys |
Array |
Keys which values can't be altered. | |
protectedKeys |
Array |
Keys which can't be removed. |
Alias: record.getDatasetRecordP
In case you often end up with the structure of having a list of some type of records as the "parent" of those records. For example a list of all bookings at bookings
and the bookings at bookings/room-19
, bookings/room-27
etc.
On resolve you get back both the deepstream list handle and record handle.
The options described in getClient
above will influence how this function operates.
client.record.p.getDatasetRecord('bookings', 'room-27').then(([dsList, dsRecord]) => {
const booking = dsRecord.get();
console.log(dsList.getEntries());
console.log(booking.time, '-', booking.customer);
});
Argument | Type | Default | Description |
---|---|---|---|
listPath |
string |
The path to the list. | |
recordId |
string |
The ID of the record. | |
initiation |
Object |
If created, initiate record with this. |
Alias: record.deleteDatasetRecordP
Removes both a record and its entry in the list, as created with getDatasetRecord
.
client.record.p.deleteDatasetRecord('bookings', 'room-27').then(dsList => {
console.log('List of records after delete:', dsList.getEntries());
});
Argument | Type | Default | Description |
---|---|---|---|
listPath |
string |
The path to the list. | |
recordId |
string |
The ID of the record. |
These are not extensions of the client object, but freely importable functions.
An alternative way to add entries to a deepstream list, that prevents duplicates.
See also above method record.p.addToList
that utilizes this one.
import { addEntry } from 'extended-ds-client';
client.record.p.getExistingList('books')
.then(dsList => addEntry(dsList, 'selfish-gene'));
.catch(error => ...);
Argument | Type | Default | Description |
---|---|---|---|
list |
Object |
A DS List object. | |
str |
string |
The entry to add. |
MIT
- Removed
\*ListedRecord
in favor of newgetDatasetRecord
&deleteDatasetRecord
- Name changes of options
- listedRecordFullPaths -> datasetRecordFullPaths
- listedRecordIdKey -> datasetRecordIdKey
- Added a very versatile
updateExistingRecord
method record.p.has
now rejects on non-existent record
- Added
getListedRecord
that returns list & record handles- Will create both list & record if non-existent
- Consistent with original
getList
/getRecord
setListedRecord
now only returns the record id- Added method
deleteListedRecord
addToList
&removeFromList
now also accepts multiple entries- Options added that controls how
*listedRecord
operates- datasetRecordFullPaths
- datasetRecordIdKey
- splitChar
- New primary naming / method access, using
p
as scope- Keeping old naming as aliases
- Full test coverage
- Much smaller footprint in node_modules
- Dropping unofficial tenant extension
- Dropping deprecated methods
- Improved documentation with more examples/code
- Methods added as polyfills
- New method addToList
- Re-exporting of deepstream client constants