Skip to main content

Find and inspect data

Use the read operations of the Management API to find out what an environment stores and to inspect the data of individual users. All read operations require the Data: Read permission or the Admin permission. They never change data.

The examples on this page send operations to https://personalization.service.eu.axinom.net/graphql-management with an Authorization: Bearer <token> header. See Get started for how to obtain a token and send a request.

Choose a read operation​

GoalOperationReturns
Find which keys hold array itemslistArrayKeysKey names and the scopes that use them
Find which keys hold key-value entrieslistKeyValueKeysKey names and the scopes that use them
Inspect key-value entries, JSON and progresslistKeyValuesEntries with their kind, value, and owner
Inspect array itemslistArrayItemsItems with their value, itemId, and owner

All four operations take a single input argument and return a page of results with pageInfo.

Discover keys​

Key listings return key names and the scopes that hold data under each key, without any values. Use them to learn which keys exist before you read the data under a key.

A key appears in the listing as soon as its first record is written and disappears when its last record is deleted. Keys are returned in ascending order, and each key is listed once, even when many users store data under it.

List array keys​

query ListArrayKeys($input: ListArrayKeysInput!) {
listArrayKeys(input: $input) {
nodes {
key
scopes
}
pageInfo {
hasNextPage
endCursor
}
}
}
{
"input": {
"first": 50
}
}

List key-value keys​

A key that holds both JSON and progress entries is listed once.

query ListKeyValueKeys($input: ListKeyValueKeysInput!) {
listKeyValueKeys(input: $input) {
nodes {
key
scopes
}
pageInfo {
hasNextPage
endCursor
}
}
}
{
"input": {
"first": 50
}
}

Read the scopes of a key​

scopes lists every scope that has data under the key, in the order USER, PROFILE, APPLICATION. Use it to decide how to filter the next read. A key whose scopes contains only APPLICATION, for example, holds data that belongs to applications of profiles.

Read key-value entries​

listKeyValues returns JSON entries and progress entries. Every entry carries its kind and its owner.

query ListKeyValues($input: ListKeyValuesInput!) {
listKeyValues(input: $input) {
nodes {
key
kind
value
owner {
userId
profileId
applicationId
scope
key
}
}
pageInfo {
hasNextPage
endCursor
}
}
}

The following variables read all JSON entries that one user stored with APPLICATION scope for one application:

{
"input": {
"filter": {
"userId": "11111111-1111-4111-8111-111111111111",
"applicationId": "22222222-2222-4222-8222-222222222222",
"scope": "APPLICATION",
"kind": "JSON"
},
"first": 25
}
}

Read progress entries​

Set kind to PROGRESS to read playback progress. Progress values are integers.

{
"input": {
"filter": {
"userId": "11111111-1111-4111-8111-111111111111",
"key": "playback:movie:100",
"kind": "PROGRESS"
}
}
}

Progress is read-only in the Management API. Progress entries reflect what has been stored so far, so a value can trail live playback slightly. Leave out kind to receive JSON and progress entries together, and check each entry's kind before you use its value.

Match entry content​

Add a match to return only entries whose JSON value contains the given properties. The following variables find JSON entries under one key whose value has "theme": "dark".

{
"input": {
"filter": {
"key": "ui:preferences"
},
"match": {
"value": { "theme": "dark" }
},
"first": 100
}
}

A match compares object properties. Scalar values, including progress values, never match. See Filtering, matching, and patching for the exact matching rules.

Read array items​

listArrayItems returns the items of arrays. Every item carries its own itemId, its stored value, the service-owned dateModified, and its owner.

query ListArrayItems($input: ListArrayItemsInput!) {
listArrayItems(input: $input) {
nodes {
id
itemId
value
dateModified
owner {
userId
profileId
applicationId
scope
key
}
}
pageInfo {
hasNextPage
endCursor
}
}
}

The following variables read the first page of one user's favorites:

{
"input": {
"filter": {
"userId": "11111111-1111-4111-8111-111111111111",
"key": "content:favorites",
"scope": "USER"
},
"first": 50
}
}

itemId is the identifier that the customer supplied when the item was written. It is null for older items that have neither a stored itemId nor a usable value.id. See the Array API for the item model.

Find one item by identity​

Use match.itemId to look for one item across the filtered data. This example finds every owner's item that has the identity movie:100 under the favorites key.

{
"input": {
"filter": {
"key": "content:favorites"
},
"match": {
"itemId": "movie:100"
},
"first": 100
}
}

You can combine itemId and value in one match. Every condition you give must hold. Omit match to select every item that the filter selects. An empty match is rejected.

Filter by owner and scope​

Every field you supply in a filter narrows the result. An empty filter selects the whole environment, which can be a large amount of data. Prefer filters that include a key or a userId.

Filter fieldEffect
keyOnly records stored under this key
userIdOnly records of this user
profileIdOnly records of this profile, including PROFILE and APPLICATION scope data
applicationIdOnly APPLICATION scope data of this application
scopeOnly records written with this scope
kindKey-value reads only: JSON or PROGRESS

Ids and scopes must agree. A filter with scope: USER cannot include profileId or applicationId, and a filter with scope: PROFILE cannot include applicationId. These contradictions are rejected with the FILTER_CONSTRAINT reason instead of silently matching nothing.

Paginate with cursors​

Reads return one page at a time. Use first to choose the page size, from 1 to 500. The default is 20. Pass the endCursor of one page as after in the next request to continue.

{
"input": {
"filter": {
"key": "content:favorites"
},
"first": 100,
"after": "<endCursor from the previous page>"
}
}

Follow hasNextPage, not the page size​

Each page examines a bounded number of stored records. When you use a match, a page can therefore contain fewer than first entries, or none at all, while pageInfo.hasNextPage is still true. Keep requesting pages with the returned endCursor until hasNextPage is false.

{
"data": {
"listArrayItems": {
"nodes": [],
"pageInfo": {
"hasNextPage": true,
"endCursor": "<opaque cursor>"
}
}
}
}

An empty page with hasNextPage: true is a normal, successful response. It means that the page examined records without finding a match, and that more records remain.

The following TypeScript sketch reads every page.

type Page = {
nodes: unknown[];
pageInfo: { hasNextPage: boolean; endCursor: string | null };
};

async function readAllPages(
fetchPage: (after: string | null) => Promise<Page>,
): Promise<unknown[]> {
const results: unknown[] = [];
let after: string | null = null;

while (true) {
const page = await fetchPage(after);
results.push(...page.nodes);

if (!page.pageInfo.hasNextPage) {
return results;
}
after = page.pageInfo.endCursor;
}
}

Treat cursors as opaque​

A cursor is only valid for the operation, environment, filter, and match that produced it. Do not decode, build, or change cursor values. Do not reuse a cursor with a different operation, a different filter, or a different match. Start a new traversal without after instead. A cursor that does not fit the request is rejected with the CURSOR_CONSTRAINT reason.

Results appear in a stable order that depends on the filter. Continue with cursors rather than assuming a business order such as newest first.

endCursor is null when a page examined no records.

Page size and cursors for key listings​

The key listings paginate the same way, with first and after. They return each key once in ascending order. A key that exists for the whole traversal appears exactly once.

Handle read errors​

Reason or codeMeaning
FORBIDDENThe token lacks Data: Read or Admin
PAGINATION_CONSTRAINTfirst is outside the range from 1 to 500
CURSOR_CONSTRAINTafter is malformed or belongs to another operation, filter, or match
FILTER_CONSTRAINTThe filter contains ids that contradict its scope
MATCH_CONSTRAINTThe match is empty or malformed

Inspect errors[].extensions rather than the message text. See Errors and limits for the full reference.

Next steps​

Was this page helpful?