evrythng.js v5.0.0

The release of evrythng.js version 5.0.0 brings a number of minor breaking changes, mostly relating to semantics and paving a way for future versions to easily expand functionality.

This migration guide details all those that require existing code to be updated, and what to do in each case. Most can be updated using standard Find and Replace functionality. It also includes a list of all the new features added in this version.


Breaking Changes

Jump To ↓

Now includes evrythng-extended.js
Browser global variables
Updated scope names
User scope now requires only API key
$init has been replaced
The authorization API option is now apiKey
Resource iterators are now async generators
Reactor API consolidated


Now includes evrythng-extended.js

This new version of evrythng.js brings in the additional Trusted Application and Operator related functionality into the main library, removing the need for a separate library and extra dependency. Simply update your app to version [email protected] or above and remove evrythng-extended from the dependency list.

Browser global variables

Apps that include evrythng.js using a <script> tag and accessing the EVT global variable will now need to access the evrythng global variable instead.

// Before
EVT.setup({ geolocation: false });
EVT.api(options);

// After
evrythng.setup({ geolocation: false });
evrythng.api(options);

Updated scope names

The App and TrustedApp scopes have been given clearer names to bring them inline with the rest of the scope types.

// Before
const app = new EVT.App(API_KEY);
const trustedApp = new EVT.TrustedApp(API_KEY);

// After
const app = new evrythng.Application(API_KEY);
const trustedApp = new evrythng.TrustedApplication(API_KEY);

User scope now requires only API key

Apps that store Application User credentials to be re-created each time an app is used should now do so using only the API key.

const id = localStorage.getItem('userId');
const apiKey = localStorage.getItem('userApiKey');

// Before
const user = new EVT.User({ id, apiKey });

// After
const user = new evrythng.User(apiKey);

$init has been replaced

Apps that required a scope's resource data immediately were previously required to await the $init promise exposed as a scope property. This has been formalised into a similarly named init() method that provides the same functionality.

Note: If a scope's data is not immediately required, this step can continue to be skipped.

// Before
const app = new EVT.App(API_KEY);
await app.$init;
console.log(app.customFields);

// After
const app = new evrythng.Application(API_KEY);
await app.init();
console.log(app.customFields);

The authorization API option is now apiKey

When using api(), specifying an API key was previously done using the authorization option. This is now called apiKey to align with other instances where keys are specified, instead of representing the header name directly.

// Before
EVT.api({
  url: '/thngs',
  method: 'post',
  authorization: apiKey,
  data,
});

// After
evrythng.api({
  url: '/thngs',
  method: 'post',
  apiKey,
  data,
});

Resource iterators are now async generators

When paging through more than 100 resources (such as Thngs), iterator() must be used. Previously this was done with an Iterator-like object and the EVT.Utils.forEachAsync() method. Now, async generators can be used instead with pages().

const params = { perPage: 100, project }; 

// Before
const iterator = operator.thng().iterator({ params });
EVT.Utils.forEachAsync(iterator, (page) => {
  console.log(page);
});

// After
const iterator = operator.thng().pages({ params });
let page = await iterator.next();
while(!page.done) {
  console.log(page.value);
  page = await iterator.next();
}

Reactor API Consolidated

The methods for working with Reactor scripts, schedules, and logs have been consolidated into methods instead of the idiosyncratic property. An application resource now has reactorScript(), reactorSchedule(), and reactorLog(). An example for Reactor schedules is shown below:

// Before
trustedApp.reactor.schedule().read();

// After
trustedApp.reactorSchedule().read();

 New APIs

In addition to the above breaking changes, this new version brings some new features and APIs that were missing in previous versions.

Jump To ↓

Thng and Product Redirection
Shared Accounts and Account Accesses
Domains and Short Domains
Trusted Application Secret Key
Account and Application Redirector
Aliased Resources
Parameter Setters
New Resource Methods


 Thng and Product Redirection

Create, read, update, or delete a Thng or product's redirection directly.

const thngId = '...';
const data = { defaultRedirectUrl: 'https://google.com?thng={shortId}' };

// Before
EVT.api({
  url: `/thngs/${thngId}/redirector`,
  method: 'PUT',
  authorization: operator.apiKey,
  data,
});

// After (can be thng or product)
await operator.thng(thngId).redirection().update(data);

Shared Accounts and Account Accesses

Read and update accounts that the chosen operator has access to and is sharing with others.

// Before
EVT.api({
  url: '/accounts',
  authorization: operator.apiKey,
});

// After
const accounts = await operator.sharedAccount().read();

Operators can also read the individual accesses granted to each shared account:

const accountId = '...';

// Before
EVT.api({
  url: `/accounts/${accountId}/accesses`,
  authorization: operator.apiKey,
});

// After
const accounts = await operator.sharedAccount(account.id)
  .access()
  .read();

Domains and Short Domains

Setting up redirections and GS1 Digital Links requires knowledge of which domains and short domains are available. This is now easier to do:

const accountId = '...';

// Before
const shortDomains = await EVT.api({
  url: `/accounts/${accountId}/shortDomains`,
  authorization: operator.apiKey,
});

// After
const shortDomains = await operator.sharedAccount(accountId)
  .shortDomain()
  .read();

Similarly for Domains:

const accountId = '...';

// Before
const domains = await EVT.api({
  url: `/accounts/${accountId}/domains`,
  authorization: operator.apiKey,
});

// After
const domains = await operator.sharedAccount(accountId)
  .domain()
  .read();

Trusted Application Secret Key

An Operator can read an Application's secret API key (otherwise known as the Trusted Application API Key) in order to create a scope with that level of access.

const projectId = '...';
const applicationId = '...';

// Before
const { secretApiKey } = await EVT.api({
  url: `/projects/${projectId}/applications/${applicationId}/secretKey`,
  authorization: operator.apiKey,
});

// After
const { secretApiKey } = await operator.project(projectId)
  .application(application.id)
  .secretKey()
  .read();

Account and Application Redirector

Adding and updating Redirector rules for the account and individual Applications is now easier to do, for example in response to external integrations, or as part of some project setup script etc.

const data = {
  rules: [{
    match: 'thng.name=test',
  }],
};

// Before
EVT.api({
  url: '/redirector',
  authorization: operator.apiKey,
  method: 'PUT',
  data,
});

// After
operator.redirector().update(data);

And similarly for an Application Redirector:

const projectId = '...';
const applicationId = '...';

// Before
EVT.api({
  url: `/projects/${projectId}/applications/${applicationId}/redirector`,
  authorization: operator.apiKey,
  method: 'PUT',
  data,
});

// After
await operator.project(projectId).application(application.id)
  .redirector()
  .update(data);

Aliased Resources

If a use-case or integration is to be used in a space or by a team with special domain-specific language, a convenient new feature is alias(), which allows aliasing of an existing resource type under a new name. The existing resource is still available; the aliased one simply behaves as if it had the old name. For example, if an EVRYTHNG product is known as an SKU:

// Add 'sku' alias to any Operator scopes created
evrythng.alias({ product: 'sku' }, 'Operator');

// Read a list of 'sku's
const params = { perPage: 100, project };
const skuList = await operator.sku().read({ params });

Parameter Setters

Existing apps may use the params option when making an SDK-based API request to specify things like the number of items per page, which project to scope the request to, etc. The syntax for this is a bit cumbersome (especially so if only one parameter is to be used).

To this end, some new chainable helper methods are available for most resources. As many as required can be used before finally calling create(), read(), update(), or delete() as appropriate.

const projectId = '...';

// Before
operator.thng().read({
  params: {
    filter: {
      tags: 'test',
    },
    project: projectId,
    perPage: 100,
  }
});

// After
operator.thng()
  .setFilter({ tags: 'test' })
  .setProject(projectId)
  .setPerPage(100)
  .read();

 New Resource Methods

Some new convenience methods have been added to most resources to allow common operations to be performed.

Rescope a resource

Change a resource's projects and users scopes without requiring a complex payload object. If users scopes are not provided, they are preserved.

const projectId = '...';
const thngId = '...';

// Before
const payload = {
  scopes: {
    projects: [projectId]
  }
};
await operator.thng(thngId).update(payload);

// After
// Update just project scopes
await operator.thng(thngId).rescope([projectId]);

// Also scope to all users
await operator.thng(thngId).rescope([projectId], ['all']);

// Removed from all projects
await operator.thng(thngId).rescope([]);

Upsert a resource

📘

Note

As of v5.3.0, upsert() also accepts a string to upsert by 'name'.

If a resource is to exist uniquely according to some identifiers key (such as a serial number), the upsert() method on the resource can be used to update it according to some payload, or else use that payload to create it. By default, it will throw an error and do nothing if more than one match is found for the given identifier value, but this can be overridden if required.

const payload = {
  name: 'Example Thng',
  identifiers: { serial: '2n8sfdn89f' },
};

// Before
const params = {
  filter: `identifiers.serial=${payload.identifiers.serial}`,
};
const found = await user.thng().read({ params });
if (!found) {
  return user.thng().create(payload);
}
return user.thng(found[0].id).update(payload);

// After
const updateKey = { serial: payload.identifiers.serial };
await user.thng().upsert(payload, updateKey);

 Find a resource

The find() convenience method on a resource can be used to locate one or more of that resource type by identifier.

const identifier = { serial: 'a87df678dfj' };

// Before
const params = {
  filter: `identifiers.serial=${identifier.serial}`, 
};
const found = await operator.thng().read({ params });

// After
const found = await operator.thng().find(identifier);

Updated about a year ago

evrythng.js v5.0.0


Suggested Edits are limited on API Reference Pages

You can only suggest edits to Markdown body content, but not to the API spec.