|
…
|
||
|---|---|---|
| .. | ||
| index.d.ts | ||
| README.md | ||
| simperium-tests.ts | ||
| tsconfig.json | ||
| tslint.json | ||
simperium usage notes
Simperium is a cross-platform data synchronization service. This is its official JavaScript/Node client library.
The simperium library is a flow-typed project and these type definitions are manually created to match the public-facing API.
Although Simperium allows for unconstrainted data access it can be helpful to define a type schema on each bucket for additional safety and autocomplete.
Example
import { default as createClient, Auth } from 'simperium';
interface Project {
name: string;
authors: string[];
version: string;
includesTypes: boolean;
stars: number;
}
interface Language {
name: string;
projects: string[];
}
interface Buckets {
projects: Project;
languages: Language;
}
new Auth( 'my-app-id', 'my-api-key' )
.authorize( 'username', 'password' )
.then( ( { access_token } ) => {
const client = createClient<Buckets>( 'my-app-id', access_token );
const projectBucket = client.bucket('projects');
const languageBucket = client.bucket('languages');
projectBucket.add( {
name: 'simperium',
authors: [ 'beaucollins', 'roundhill', 'dmsnell', 'belcherj' ],
version: '1.1.1',
includesTypes: true,
stars: 65
} );
languageBucket.add( {
name: 'flowtyped',
projects: [ 'simperium' ]
} );
languageBucket.add( {
name: 'typescript',
projects: [ 'simperium' ]
} );
projectBucket.on( 'update', ( id, project ) => {
console.log( `Discovered a new project: ${ project.name }` );
} );
client.on( 'unauthorized', () => {
console.log( 'All good things must come to an end.' );
process.exit( 0 );
} );
} );
Custom storage providers
By default the library creates an in-memory copy of the data but you can provide a custom storage mechanism to support persistence and other behaviors. There are two fundamental means of storage: the bucket and the ghost.
The bucket represents the current value of an entity in a Simperium bucket. It's the local working copy and likely what your application directly interacts with.
The ghost represents that last-known version or revision of that entity as last reported by the server. This is an immutable value affixed to a specific version number and will be used to generate a diff for the current bucket value when sync'ing.
Persisting contents in a browser
In this example we can store the local copy of the data in localStorage in a browser.
It will be shared across tabs and persist across app sessions.
We'd want to create a ghostStoreProvider as well but for brevity only
the objectStoreProvider is shown here.
import type {
BucketObject,
BucketStore,
EntitiesCallback
EntityCallback,
EntityId
} from 'simperium';
class LocalStorageBucket<T> implements BucketStore<T> {
prefix: string;
constructor(storagePrefix: string) {
this.prefix = storagePrefix;
}
get(id: EntityId, callback: EntityCallback<BucketObject<T>>) {
const entity = localStorage.getItem( `${ this.prefix }.${ id }` );
if ( null === entity ) {
callback( new Error( 'missing entity' ), null );
} else {
try {
callback( null, { id, data: JSON.parse( entity ) } );
} except (e) {
callback( new TypeError('unable to decode entity'), null );
}
}
}
…
}
…
const client = createClient<Buckets>( 'my-app-id', 'my-token', {
objectStoreProvider: bucket => new LocalStorageBucket( bucket.name ),
ghostStoreProvider: bucket => new LocalStorageGhost( bucket.name )
} );
Backing copies in a databsae
In this example we're storing hard-copies of the ghost data in a local database.
We'd want to create an objectStoreProvider as well but for brevity only
the ghostStoreProvider is shown here.
import type { ChangeVersion, EntityId, GhostStore, Ghost } from 'simperium';
interface Counter {
name: string;
limit: number;
}
class DBCounterGhost implements GhostStore<Counter> {
connection: DBConnection;
constructor() {
this.connection = connect( 'db-server', 'db-host' );
}
get( id: EntityId ): Promise<Ghost<Counter>> {
return this.connection
.query( 'SELECT * FROM counters WHERE id = {} ORDER BY version DESC', id )
.then( results => {
return {
key: id,
version: results[0].version,
data: JSON.parse( results[0].data )
}
} )
}
put( id: EntityId, version: number, data: Counter ): Promise<Ghost<Counter>> {
return this.connection.query(
'INSERT INTO counters (id, version, data) VALUES ({}, {}, {})',
id,
version,
JSON.stringify(data)
).then( () => ({ key: id, version, data } ) );
}
…
}
…
const client = createClient<Buckets>( 'my-app-id', 'my-token', {
objectStoreProvider: () => new DBCounterBucket(),
ghostStoreProvider: () => new DBCounterGhost()
} );