The Custom Entity Store lets you store data in custom entities, which are data structures you can define according to your app's needs. Custom entities let you assign multiple values (or "attributes") to a single key (or "entity") and define indexes to optimize queries against these values.
For example:
1 2 3 4 5 6 7 8 9
import kvs, { WhereConditions, Sort } from "@forge/kvs"; await kvs .entity('employee') .query() .index('by-age') .where(WhereConditions.greaterThan(30)) .sort(Sort.DESC) .getMany()
Each installation of your app is subject to the Storage API's quotas and limits. See Storage quotas and Storage limits for more details.
Using the @forge/kvs
package requires the storage:app
scope in your manifest file:
1 2permissions: scopes: - storage:app
See Permissions for more information about scopes.
Stored entity values must adhere to the following type requirements:
Type | Requirements | |
---|---|---|
integer | Must be a 32-bit signed integer, with the following value limits:
| |
float | The value must either be 0, or fall within the following range limits (both inclusive):
We provide 38 digits of precision as a base-10 digit. | |
string |
| |
boolean | Can only be true or false | |
any | The
|
The Custom Entity Store strictly enforces attribute types.
Attempting to store a value whose type doesn't
match its field will result in an error (for example, when you try to set a string
value to an attribute with an integer
type).
The Atlassian cloud provides features that allow admins to control and verify where their Jira and Confluence data is hosted. These features support them in meeting company requirements or regulatory obligations relating to data residency.
Forge's persistent storage options use this same cloud infrastructure to store data. This allows Forge to extend similar data residency features to your app. All data stored on Forge persistent storage automatically inherit these features.
Specifically, if your app stores data on Forge persistent storage, an admin can control where that data is stored. For more details about how this works, see Data residency.
Data in Forge hosted storage is namespaced. The namespace includes all metadata relevant to an app's current installation. As a result:
Data is transferred, encrypted, and stored on disk according to the Atlassian cloud’s data encryption policies. Once stored, data will persist until deleted or updated by your app.
In addition, the Atlassian Cloud backs up all persistent storage for disaster recovery.
Writes to keys using set
or delete
use a "last write wins" conflict resolution strategy.
Writes to individual keys are atomic - For example, the value is either updated in full or not.
The app storage API is able to persist any JSON data type except null
. For example:
The JavaScript storage API serializes your objects using JSON.stringify
, and as such removes functions and the value undefined
from any object you attempt to serialize.
The Key-Value Store and Custom Entity Store use cursors for paginated data access. Queries (both through the Query builder and Complex Query builder) return a cursor in the results. This cursor can be provided to subsequent queries to paginate over larger data sets than you would otherwise be able to fetch.
Cursors are derived from underlying storage identifiers, and hence are subject to change anytime there is any change in how these underlying storage identifiers are created. This means that cursors are not stable and should not be persisted.
You will have to use the same parameters as the initial query. See the example below.
1 2// Inserts some dummy data await kvs.set('account.1', 'account 1'); await kvs.set('account.2', 'account 2'); await kvs.set('account.3', 'account 3'); await kvs.set('account.4', 'account 4'); await kvs.set('account.5', 'account 5'); await kvs.set('user.1', 'user 1'); await kvs.set('user.2', 'user 2'); await kvs.set('user.3', 'user 3'); await kvs.set('user.4', 'user 4'); // Fetch up to 2 results, but will return "account.1" and "account.2" and a cursor to next page const { nextCursor: firstCursor } = await kvs .query() .limit(2) .where('key', WhereConditions.beginsWith('account.')) .getMany(); // Fetch up to 2 results after the cursor, but will return "account.3" and "account.4" and a cursor to next page const { nextCursor: secondCursor } = await kvs.query() .limit(2) .where('key', WhereConditions.beginsWith('account.')) .cursor(firstCursor) .getMany(); // Fetch up to 2 results, but will only return "account.5" and no cursor const { nextCursor: undefinedCursor } = await kvs.query() .limit(2) .where('key', WhereConditions.beginsWith('account.')) .cursor(secondCursor) .getMany();
Keys are lexicographically ordered; this means, for example, that the Query builder and Complex Query builder) will return entities ordered by their key. This property can be used to group related entities or build ad-hoc indexes.
The Key-Value Store and Custom Entity Store don't support indexing of arbitrary attributes. However, it is possible to support this sort of access pattern if your keys are constructed in such a way as to support indexed reads.
Hierarchical keys can be constructed to allow for nested entities to be fetched in a single list operation such as the example below.
1 2import { kvs, WhereConditions } from '@forge/kvs'; // Nested entities await kvs.set('survey-responses#1#{UUID}', { }); await kvs.set('survey-responses#1#{UUID}', { }); await kvs.set('survey-responses#1#{UUID}', { }); await kvs.set('survey-responses#1#{UUID}', { }); const results = await kvs .query() .where('key', WhereConditions.beginsWith('survey-response#1#')) .limit(10) .getMany();
The entity().query
method used by the Custom Entity Store is eventually consistent. This means that the method returns data that may be slightly out of date.
The entity().get
method, on the other hand, is strictly consistent. It will always return current data.
Legacy versions of the Key-Value Store and Custom Entity Store were provided through the storage
module of the @forge/api
package.
We will continue supporting this module. However, all future KVS and Custom Entity Store updates will only be built on modules in the current @forge/kvs
package.
Rate this page: