0.1.48 • Published 5 years ago

@wirelineio/store-client v0.1.48

Weekly downloads
1
License
UNLICENSED
Repository
github
Last release
5 years ago

id: tutorial-store

title: Wireline Key/Value Store

A simple key/value storage service. Supported operations are:

  • set
  • get
  • inc
  • del
  • scan
  • clear
  • format

Quick Start

Add the Wireline key/value store API client to your project:

$ yarn add @wirelineio/store-client

Then import it in your code and create the Store client object by passing it the context:

import Wireline from '@wirelineio/sdk';
import Store from '@wirelineio/store-client';

module.exports = {
  test: Wireline.exec(async (event, context) => {
    let store = new Store(context);

    // The rest of your code here...
  })
};

The last step is to indicate which deployment of the wireline.io/store service you would like to use, either a new one or an existing one. The HTTP endpoint details for whichever wireline.io/store you choose will be infused automatically into the context object by Wireline.exec(), providing everything needed to configure your Store client object.

In this example, we will use one that already exists by adding it as a reference to our stack.yml. Use the wire CLI to list active deployments of the wireline.io/store service.

$ wire svc list-deployments --service 'wireline.io/store'
# NOTE: Some columns omitted for clarity.

Domain        Stack         Service               WRN                                 Version
---------------------------------------------------------------------------------------------
wireline.io   wireline      wireline.io/store     wrn::wireline.io/wireline/store     0.0.1
...

Add a reference to it at the bottom of the stack.yml of your Wireline project using its name, wireline.io/store, and the WRN of the deployment you wish to use, wrn::wireline.io/wireline/store.

  wireline.io/store:
    reference:
      endpointUrl: wrn::wireline.io/wireline/store

Get/Set

The most basic operations are set and get, storing or retrieving a key/value pair (respectively). The key must be a string. The value may be a string, number. boolean, or an object.

    await store.set('key', 'value'));
    let value = await store.get('key');

Inc

This provides an atomically incrementing counter. If the counter does not exist, it will be created and set to a value of 1.

    let value = await store.inc('counterA');

Del

Removes a single key/value pair, if it exists. No value is returned, and there is no indication as to whether the key existed.

    await store.del('counterA');

Scan

Scan for key/value pairs in the bucket. If provided, prefix restricts the results to keys which equal or begin with that prefix. This is a case-sensitive comparison. The limit option will limit the maximum number of results returned. Depending on the number of pairs and the storage provider, this may be an expensive operation.

  # Scan everything.
  let results = await store.scan();

  # Scan for up to ten items, with keys that equal or start with 'aaa'.
  results = await store.scan('aaa', { 'limit': 10 })

Clear

Clear all key/value pairs in the bucket. Depending on the number of pairs and the storage provider, this may be an expensive operation.

  await store.clear();

Format

Clear all key/value pairs in all buckets, as well as removing the "stake". Depending on the storage provider, this may be an extremely expensive operation.

Using Buckets

Nearly all operations support the bucket option, which scopes the operation to the specified "bucket". A "bucket" provides namespacing for keyi/value pairs, so that keyA in bucketA is distinct from keyA in bucketB.

  store.set('keyA', 'valueA', { 'bucket': 'bucketA' });

The default bucket may also be set when the Store client object is created:

  let store = new Store(ctx, { 'bucket': 'myBucket' });

Planting Stakes

Each Wireline deployment is given a unique namespace within the store. The first operation for the deployment implicitly "stakes a claim" to the namespace by generating a unique value which will be used to validate all subsequent operations. In addition to the unique Wireline WRN details (domain/stack/deployment), the account ID performing the operation is tied to the "stake". If multiple accounts are used, an explicit "stake" value should be used instead. This can be provided when creating the Store client object. The value should be treated securely, as a kind of credential.

  let store = new Store(ctx, { 'stake': 'myVerySecretStakeValue' });

The format operation not only removes all values from the unique namespace given to the deployment, it releases the stake.

Configuration

The HTTP endpoint will be automatically discovered by adding a reference to a deployed store's WRN under the name wireline.io/store in either the stack.yml or wireline.yml for your service. If for some reason that automatic process needs to be overridden, the full HTTP URL can be set explicitly using the WRL_STORE_URL environment variable.

stack.yml WRN:

name: my-stack
domain: example.com
stage: dev
provider:
  name: aws
  region: us-east-1
stack:
  my-deployment:
    deployment:
      image: 'wrn::my-service'
      version: 0.0.1
  wireline.io/store:
    reference:
      endpointUrl: 'wrn::wireline.io/wireline/store'

wireline.yml WRN:

services:
  'wireline.io/store': 'wrn::wireline.io/wireline/store'

stack.yml by ENV:

name: my-stack
domain: example.com
stage: dev
provider:
  name: aws
  region: us-east-1
stack:
  my-deployment:
    deployment:
      image: 'wrn::my-service'
      version: 0.0.1
      environment:
        WRL_STORE_URL: 'https://example.store.url/store'

Example Usage

Example A - Basic functionality

    let store = new Store(context);

    // All of these operations will happen in the default bucket.
    console.log(await store.set('A-key', 'valA'));
    console.log(await store.get('A-key'));

    console.log(await store.inc('A-counter'));
    console.log(await store.inc('A-counter'));
    console.log(await store.inc('A-counter'));

    console.log(await store.scan('A-'));

    await store.del('A-key');

    console.log(await store.scan('A-'));

    await store.clear();
    console.log(await store.scan());

Example B - Using different buckets to separate types and indices...

    let store = new Store(context);
    
    let userOpts = { 'bucket': 'users' };
    let idxUserByEmailOpts = { 'bucket': 'idx.users.email' };
    let idxUserByUsernameOpts = { 'bucket': 'idx.users.username' };
    
    let deviceOpts = { 'bucket': 'devices' };
    let idxDevByUser = { 'bucket': 'idx.devices.byuser', limit: 50 };
    
    // Insert 'bob' by his UUID.
    await store.put(bob.uuid, bob, userOpts);
    
    // Index bob's username to his UUID.
    await store.put(bob.username, bob.uuid, idxUserByUsernameOpts);
    
    // Also index bob's e-mail address to his uuid.
    await store.put(bob.email, bob.uuid, idxUserByEmailOpts);
    
    // Insert bob's device into the device's bucket.
    await store.put(dev.uuid, dev, deviceOpts);
    
    // And index it to his user.
    await store.put(`${bob.uuid}::${dev.uuid}`, { 'usr:' bob.uuid, 'dev': dev.uuid }, idxDevByUser);
    
    // Scan for all bob's devices...
    let results = await store.scan(`${bob.uuid}::`, idxDevByUser); 
0.1.48

5 years ago

0.1.47

5 years ago

0.1.43

5 years ago

0.1.41

5 years ago

0.1.40

5 years ago

0.1.39

5 years ago

0.1.38

5 years ago

0.1.25

5 years ago

0.1.24

5 years ago

0.1.23

5 years ago

0.1.22

5 years ago

0.1.21

5 years ago

0.1.20

5 years ago

0.1.19

5 years ago

0.1.18

5 years ago

0.1.17

5 years ago

0.1.16

5 years ago

0.1.15

5 years ago

0.1.14

5 years ago

0.1.13

5 years ago

0.1.12

5 years ago

0.1.11

5 years ago

0.1.10

5 years ago

0.1.9

6 years ago

0.1.8

6 years ago

0.1.7

6 years ago

0.1.6

6 years ago

0.1.5

6 years ago

0.1.4

6 years ago

0.1.3

6 years ago

0.1.2

6 years ago