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()
.rows().exec();
Login as the guest account
const guest = await api.auth.loginGuest();
console.log(api.auth.isGuest()) // True
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 programmatically:
const user = await api.auth.login('spoke', 'username', 'password', '2faCode');
Multiple requests can be chained together to run them in order. The last request's response will be returned
const report = await api.chain({'$/auth/current': {}}, api.slice(12345).select().rows());
All results can be returned together by using the chainMap
const {user, report} = await api.chainMap({
user: {'$/auth/current': {}},
report: api.slice(52131).select().rows()
});
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)
.row().exec();
// Get all slice records
const rows = await api.slice<T>(12345)
.select()
.rows().exec();
// Advanced queries
const rows = await api.slice<T>(12345)
.select()
.fields({'field1': 'field2'})
.where('field1', '<', 0)
.or()
.where({field1: 0, field2: false})
.order('field2', true) // ascending
.limit(10)
.rows().exec();
const count = await api.slice(12345)
.count()
.where({field1: 'value'})
.count().exec();
// Insert record
const key = await api.slice<Contact>(12345)
.insert({first: 'Bilbo', last: 'Baggins'})
.id().exec();
// Insert multiple rows
const keys = await api.slice<Contacts>(12345)
.insert([
{first: 'Darth', last: 'Vader'},
{first: 'John', last: 'Snow'}
])
.ids().exec();
// Update a record
const success = await api.slice(12345)
.update({id: 1, first: 'James', last: 'Kirk'})
.id().exec();
// Update multiple rows with where
await api.slice(12345)
.update({evil: true})
.where({first: 'Darth'})
.ids().exec();
// Delete a record
const success = await api.slice(12345)
.delete(12345)
.id().exec();
// Dlete multiple rows with where
await api.slice(12345)
.delete()
.where({first: 'Darth'})
.ids().exec();
The socket can have it's endpoint override 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 synced 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'});