Datalynk client library to integrate JavaScript clients with the Datalynk API.
- Install the client library
npm install --save @auxilium/datalynk-client
- Optional: Build Datalynk models
$ npx datalynk-models
Output (src/models):
Spoke: spoke
Login: username
Password: ********
- Create an API object & start making requests
import {API} from '@auxilium/datalynk-client';
const api = new API('https://spoke.auxiliumgroup.com');
const resp = await api.request({'$/auth/current':{}});
This library is written with vanilia JS & has no framework dependencies allowing easy integration with any front-end website. Here is some boilerplate to get you going:
File: /src/services/datalynk.service.ts
import {Injectable} from '@angular/core';
import {Api, Slice} from '@auxilium/datalynk-client';
import {environment} from '../environment/environment';
import {Contact} from '../models/contact';
import {Slices} from '../models/slices';
declare global {
interface Window {
api: Api;
}
}
@Injectable({providedIn: 'root'})
export class DatalynkApi extends Api {
contacts!: Slice<Contact>;
constructor() {
// Create API object & expose to window
super(environment.api, {/* options */});
window.api = this;
// Handle logging in
this.auth.handleLogin('spoke', {/* login UI options */});
// Create store to cache slice data
this.contacts = this.slice<Contact>(Slices.Contact);
}
}
File: /src/services/datalynk.service.ts
import {Api} from '@auxilium/datalynk-client';
import {environment} from '../environment/environment';
import {Contact} from '../models/contact';
import {Slices} from '../models/slices';
// Create API object & expose to window
export const api = window.api = new Api(environment.api, {/* options */});
// Handle logging in
api.auth.handleLogin('spoke', {/* login UI options */});
// Create store to cache slice data
export const contacts = api.slice<Contact>(Slices.Contact);
File: /index.html
<script type="module">
import {Api} from '@auxilium/datlaynk-client/dist/index.mjs';
// Create API object & expose to window
var api = new Api('https://spoke.auxiliumgroup.com', /* options */);
// Handle logging in
api.auth.handleLogin('spoke', {/* login UI options */});
// Create store to cache slice data
var contacts = api.slice(12345);
</script>
This library comes with a command line tool for developers to automatically create Typescript models from the Datalynk metadata.
This takes most of the manual labour out of manually mapping the data & provides type safety.
- Simply run the tool:
$ npx datalynk-models
Output (src/models):
Spoke: spoke
Login: username
Password: ********
- Import models:
import {Slices} from 'models/slices'; // Import slices map
import {Contact} from 'models/contact'; // Import model for slice we will be using
const contacts: Contact[] = await api.slice<Contact>(Slices.Contact)
.select()
.exec().rows();
This library comes with some logic to automatically handle the login flow & should be called at the startup of your application:
- It will check the URL for a token param:
?datalynkToken=...
- It will check the localStorage for a saved token
- It will prompt the user to login via UI
- Reload page if token changed
await api.auth.handleLogin('spoke', {
background: 'url("...")', // CSS URL or hex color
color: '#ff0000', // hex color
title: '<img alt="logo" src="..." />', // text or HTML
titleColor: '#ffffff' // Color of title text
});
Alternatively you can manage the login prompt manually:
const prompt = api.auth.loginPrompt('spoke', options);
await prompt.wait; // Wait for the user to login/close the prompt
prompt.close(); // Close prompt manually
Manually login programatically:
const user = await api.auth.login('spoke', 'username', 'password', '2faCode');
This library comes with LINQ style query language to help make interacting with Slices easier by providing types & intelisense.
// Get a single record
const row = await api.slice<T>(12345)
.select(12345)
.exec().row();
// Get all slice records
const rows = await api.slice<T>(12345)
.select()
.exec().rows();
// Advanced queries
const rows = await api.slice<T>(12345)
.select()
.where('field1', '==', 'value')
.fields({'field1': 'field2'})
.order('field2', true /* ascending */)
.limit(10)
.exec().rows();
// Insert record
const key = await api.slice<Contact>(12345)
.insert({first: 'Bilbo', last: 'Baggins'})
.exec().key();
// Insert multiple rows
const keys = await api.slice<Contacts>(12345)
.insert([
{first: 'Darth', last: 'Vader'},
{first: 'John', last: 'Snow'}
])
.exec().keys();
// Update a record
const success = await api.slice(12345)
.update({id: 1, first: 'James', last: 'Kirk'})
.exec().key();
// Update multiple rows with where
await api.slice(12345)
.update({evil: true})
.where({first: 'Darth'})
.exec().keys();
// Delete a record
const success = await api.slice(12345)
.delete(12345)
.exec().key();
// Dlete multiple rows with where
await api.slice(12345)
.delete()
.where({first: 'Darth'})
.exec().keys();
The socket can have it's endpoint overriden or be turned off by setting it to false:
const api = new Api('https://spoke.auxiliumgroup.com', {
socket: false, // Disable
// socket: 'http://localhost:3000', // Override
});
The Slice Engine's cache can be sycned with the server & subscribed to using RXJS:
// Create cache/store
const contacts = api.slice<Contact>(Slices.Contact);
// Enable syncing
contacts.sync();
// Use RXJS to listen for events
contacts.sync().pipe(...).subscribe((cache: Contact[]) => {...});
// Or using Angular templates
'{{ contacts.sync() | async }}'
// Disable syncing
contacts.sync(false);
Alternatively socket events can be listened to directly using callbacks:
api.socket.sliceEvents(123, callbackFn(event)); // Listen to a specific slice
api.socket.addListener(callbackFn(event)); // listen to all socket events
A file URL can be created from the file ID
const url = api.files.get(12345);
Uploading files to datalynk is done by first uploading the file as form-data & then creating a reference to the upload ID in a slice.
// Get files from file input
const files = document.querySelector('#upload').files;
// Upload file
api.files.upload(files).then(uploaded => {
// Associate with record
const slice = 12345, row: 123;
api.files.associate(uploaded.map(r => r.id), slice, row, 'field');
});
// OR do it all at once
api.files.upload(files, {slice: 12345, row: 1234, field: 'abc'});