The official evrythng.js
SDK facilitates communication with the
EVRYTHNG REST API thanks to its fluent and
resource oriented API. It can be used both for server-side scripting (Node.js)
or in client-side web applications in modern browsers.
evrythng.js
is distributed via NPM
and the EVRYTHNG CDN, allowing you to manage the version of the library that
your application or scripts uses.
Install as an app dependency:
npm install --save evrythng
or as a development dependency:
npm install --save-dev evrythng
Then require it in any module:
const evrythng = require('evrythng');
evrythng.api({ url: '/time' }).then(console.log).error(console.error);
Or using ES6 import
/export
syntax when available:
import { Application } from 'evrythng';
import evrythng from 'evrythng';
// Alternatively
import * as evrythng from 'evrythng';
Or use a simple script tag to load it from the CDN.
<script src="https://d10ka0m22z5ju5.cloudfront.net/js/evrythng/6.0.5/evrythng-6.0.5.js"></script>
Then use in a browser script
tag using the evrythng
global variable:
<script>
evrythng.api({ url: '/time' }).then(console.log).catch(console.error);
</script>
evrythng.js
relies on the standard resource fetching API (fetch
) to
communicate with the EVRYTHNG API. fetch
has already been shipped in all the
major browsers (see http://caniuse.com/#feat=fetch). The isomorphic-fetch
dependency of this project should take care of this for you.
When using Node.js, version 10 and above is required.
There are several types of Scopes and API Keys that are used to interact with the API. Each represents a type of user or resource in an EVRYTHNG account.
Note: Only the Application API Key can be safely versioned in public code! The other API key types are secret and secure API Keys with higher permission sets and should not be hard-coded - ideally encrypted in configuration files or fetched at runtime from a server.
In a nutshell, evrythng.js
provides the following scopes. Once a scope is
created it provides an appropriate API for the resources it can manage
(see API below).
For apiVersion:2
these scopes are avaliable:
Operator
AccessToken
- AccessToken is a Scope type for the v2 API with the potential to manage any account resources, depending on the API key's permissions. It represents an API access for a specific purpose, instead of a type of Actor, such as an Operator.
Operator
and AccessToken
scopes can have a different set of permissions, which is defined in an access policy and assigned during creation of operator access and access token.
const accessToken = new evrythng.AccessToken(ACCESS_TOKEN_API_KEY);
For apiVersion:1
these scopes are avaliable:
Operator
- Highest level scope that can manage the account structure, all its resources and projects, etc.
const operator = new evrythng.Operator(OPERATOR_API_KEY);
Application
- Public application scopes used for Identifier Recognition and to authenticate Application Users.
const application = new evrythng.Application(APPLICATION_API_KEY);
TrustedApplication
- Secret type ofApplication
scope with expended permissions, intended for use for scripting and backend integrations on behalf of the application (e.g. trigger rules or system integration functionality).
const trustedApplication = new evrythng.TrustedApplication(TRUSTED_APP_API_KEY);
User
- Usually returned from authentication via anApplication
scope, but can also be created manually with an Application User API Key:
// Registered user with email password
const credentials = { email: '[email protected]', password };
app.login(credentials).then((user) => console.log(user.apiKey));
// Or, an anonymous user
app
.appUser()
.create({ anonymous: true })
.then((anonUser) => console.log(anonUser.apiKey));
// Or using a pre-existing API key
const userApiKey = localStorage.getItem('user_api_key');
const user = new evrythng.User(userApiKey);
ActionApp
- Special version of the Application scope designed to make it as simple as possible to create instrumentation actions in web apps. It creates and remembers an anonymous Application User in LocalStorage and provides a simple interface for creating actions:
import { ActionApp } from 'evrythng';
const actionApp = new ActionApp(appApiKey);
await actionApp.init();
// Create a scan action on a Thng identified in the query
const thng = getQueryParam('thng');
const data = { thng, userAgent };
const action = await actionApp.createAction('scans', data);
// Log a page was visited (the current URL)
await actionApp.pageVisited();
// Retrieve the managed Application User
const anonymousUser = await actionApp.getAnonymousUser();
For any scope, if the scope's own data (such as an Application's customFields
)
is required immediately, use the init()
method to wait until this data is
available. If not, this step can be ignored:
import { Application } from 'evrythng';
const application = new Application(apiKey);
application.init().then(() => console.log(application.customFields));
The methods available for each of the above scope types matches the general
access level defined for each type of
API Key for apiVersion:2 or apiVersion:1.
For example - the Application
scope can read products in its project, but can
only create User
s who in turn have higher access to manage resources.
The API for each scope follows a fluent pattern that decreases the time required to begin making effective use of the SDK. In general, the format is:
SCOPE
.RESOURCE(id)
.METHOD(payload, params)
.then(...)
.catch(console.error)
Where:
SCOPE
- One of the scope types shown above.RESOURCE
- can be any resource type, such asthng
,product
,collection
etc. found in the API Reference.id
- specified if manipulating a specific resource of this type.
METHOD
- one ofcreate
,read
,update
,delete
,rescope
,find
, orupsert
.payload
- JSON payload object if performing a create or update.params
- Parameters object used if required.
Therefore to read all Thngs as a TrustedApplication
scope:
trustedApplication
.thng()
.read()
.then((thngs) => console.log(`Read ${thngs.length} Thngs!`));
or to create a product as a User
:
const payload = { name: 'Test Product', tags: ['evrythng.js'] };
user
.product()
.create(payload)
.then((product) => console.log(`Created product ${product.id}!`));
or to read a known Thng using its id
as an Operator:
const thngId = 'UqKWAsTpdxCA3KwaaGmTxAhp';
operator
.thng(thngId)
.read()
.then((thng) => console.log(`Thng tags: ${thng.tags.join(', ')}`));
All methods return Promises, making chaining operations and catching errors very simple:
user
.thng()
.create(payload)
.then((res) => console.log('Success!'))
.catch((err) => console.log(`Oh no! Error: ${err.message}`));
Users of modern browsers and Node.js 8 can take advantage async
/await
syntax as an alternative to Promise chaining when performing sequences of
operations:
const testThngUpdate = async () => {
// Read all Thngs and find one
const thngs = await operator.thng().read();
const testThng = thngs.find((p) => p.tags.includes('test'));
// Update its tags
const payload = { tags: ['updated'] };
const updatedThng = await operator.thng(testThng.id).update(payload);
// Check the update was successful
expect(updatedThng.tags).to.equal(payload.tags);
};
Each of the methods described above can accept parameters identical to those
available when using the REST API, and are placed in the params
object as
shown below:
const params = {
// Only with these tags
filter: {
tags: 'test',
},
// More items per page
perPage: 100,
};
user
.product()
.read({ params })
.then((products) => console.log(`Found ${products.length} 'test' products`));
Another example is creating resources in a specific project scope:
const params = { project: projectId };
const payload = { name: 'Test Thng' };
user
.thng()
.create(payload, { params })
.then((thng) => console.log(`Created Thng ${thng.id} in project ${projectId}`));
Parameters can also be specified using chainable parameter setter methods:
user
.product()
.setFilter({ tags: 'test' })
.setPerPage(100)
.read()
.then((products) => console.log(`Found ${products.length} 'test' products`));
Other parameter setters include setWithScopes()
, setContext()
,
setPerPage()
, setProject()
and setFilter()
.
This SDK can be extended with plugins that enhance existing functionality by
modifying the capabilities of Scopes and Entities. This is done by supplying an
object with at least an install()
method, that is provided an api
object
(see src/use.js
for details of this API).
For example, adding a getSummary()
method to Thngs:
const SummaryPlugin = {
// Required method
install: (api) => {
// Add new functionality to all Thng entities
api.entities.Thng.prototype.getSummary = function () {
return `${this.name} (${this.id})`;
};
},
};
The plugin is then installed using use()
:
const SummaryPlugin = require('summary-plugin');
evrythng.use(SummaryPlugin);
Then, the plugin's functionality can be used:
// Read one Thng
const [thng] = await user.thng().setPerPage(1).read();
// Use the newly installed method
console.log(thng.getSummary());
Test Thng (U6ssDxRBD8kQATawwGrEyaRm)
For specific resource examples, see the relevant section of the
API Reference, or look in the
examples
directory in this repository.
See ./jenkins/deploy.sh
for instructions on deploying new versions.