Once your remote backend has received a request from Forge, you can access storage GraphQL APIs.
When setting up your app to:
You must set endpoint.auth.appSystemToken
to true
in your manifest.yml
. This will ensure requests to your remote contain a x-forge-oauth-system
header, that contains a token that you can use to call Forge storage APIs. This token is valid for at least 55 minutes.
Your app must also request the storage:app
scope and the read:app-system-token
scope to access Forge Storage APIs.
This can be done by including them in the permissions
section of your app's manifest.yml
file.
Access to Forge storage is provided to remote backends through GraphQL. You can get, set, update, and delete information in your app's Forge storage with GraphQL queries.
Atlassian GraphQL APIs only accept OAuth bearer tokens as authentication. Your app must supply this token in the Authorization: Bearer {bearerToken}
header of the request.
All Forge Storage GraphQL requests should be HTTP POST
requests to the endpoint: https://api.atlassian.com/graphql
.
The request body should be JSON encoded and of the format:
1 2{ "query": "...", "operationName": "...", "variables": { "myVariable": "someValue", ... } }
Where:
query
is the GraphQL query, in string format.operationName
is an optional field and is used to define the operation that is executed if the query contains multiple operations. See the GraphQL documentation for more information.The response will be in JSON format and of the shape:
1 2{ "data": { ... }, "errors": [ ... ] }
Where:
data
contains the field requested in the GraphQL query.errors
is a list of errors that occurred. The field is only included if there was an error when executing the request.For GraphQL requests, the HTTP status code may be 200 even if there was an error. Please make sure that your implementation takes into consideration the errors field in the response body, in addition to HTTP errors. Here are some links on error handling with GraphQL for more information:
When making the following GraphQL query with an invalid contextAri
:
1 2query badGet { appStoredEntity( contextAri: "ari:cloud:confluence::site/bad-site", key: "test-key", encrypted: false ) { value } }
The app receives the response:
1 2{ "errors": [ { "path": [ "appStoredEntity" ], "locations": [ { "line": 2, "column": 3 } ], "message": "Context ari:cloud:confluence::site/bad-site is not accessible in this request", "extensions": { "statusCode": 400, "errorType": "CONTEXT_INVALID" } } ], "data": { "appStoredEntity": null } }
Your app may also encounter HTTP errors in the event of some severe issues. For instance:
HTTP 401 is returned if the OAuth token provided as authorization for the request was invalid or missing.
HTTP 400 is returned if the query was malformed (e.g - requesting for a field that doesn't exist or using invalid GraphQL syntax).
HTTP 500 is returned if the GraphQL Gateway is down.
You will see contextAri
as an input variable being used for the majority of Forge Storage APIs.
The contextAri
is associated with the context where a Forge invocation is being performed. It is assembled by concatenating 'ari:cloud:confluence::site/'
with context.cloudId
obtained from the Forge Invocation Token.
For example: ari:cloud:confluence::site/1234
The examples on this page are provided in the following format.
GraphQL query
1 2query nameOfQuery($variable: VariableType) { fieldRequiringArgs(variable: $variable) { fieldA fieldB } }
GraphQL variables, to be used in the query
1 2{ "variable" : { "nestedVariable": "value" } }
This is the equivalent of:
1 2query nameOfQuery { fieldRequiringArgs(variable: { nestedVariable: "value" }) { fieldA fieldB } }
With GraphQL queries, you will get back the fields you asked for in your query. The response to the query provided may look something like this:
1 2{ "data": { "fieldRequiringArgs": { "fieldA": "valueA", "fieldB": "valueB" } } }
Sets the value
for a given key
.
If the key already exists, its value is updated. If the key does not exist, the key-value pair is added.
You cannot set a key's value
property to null
. If your app attempts this, GraphQL returns a validation error.
1 2mutation forge_app_setExample($input: SetAppStoredEntityMutationInput!) { appStorage { setAppStoredEntity(input: $input) { success errors { message extensions { errorType statusCode } } } } }
Property | Description |
---|---|
success | A boolean indicating whether the query succeeded. If false , see errors for more information. |
errors.message | A descriptive error message to assist in troubleshooting. |
errors.extensions.errorType | The type of error that occurred. For example, "INVALID_KEY" . |
errors.extensions.statusCode | The status code returned by the error. For example, 400 . |
GraphQL variables
The encrypted
field is how you would store secrets in Forge storage. Setting it to true
is the equivalent of the storage.setSecret
from the
Key-Value Store.
1 2{ "input": { "contextAri": "contextAri associated with the request", "key": "example key", "value": "example value", "encrypted": true | false } }
Variable | Description |
---|---|
contextAri | The unique identifier for the context in which the GraphQL query is invoked. |
key | The key to set. |
value | The value to set for the key. |
encrypted | A boolean indicating whether this storage entry should be encrypted. |
GraphQL response (Successfully sets a value)
1 2{ "data": { "appStorage": { "setAppStoredEntity": { "success": true, "errors": null } } } }
GraphQL response (Fails to set a value due to an invalid key provided in the request)
1 2{ "data": { "appStorage": { "setAppStoredEntity": { "success": false, "errors": [ { "message": "Key \"test-key@\" should match the pattern \"/^(?!\\s+$)[a-zA-Z0-9:._\\s-#]+$/\"", "extensions": { "errorType": "INVALID_KEY" } } ] } } } }
Set the key "example-key" with the non-encrypted string value of "hello world".
GraphQL variables
1 2{ "input": { "contextAri": "ari:cloud:confluence::site/1234", "key": "example-key", "value": "hello world", "encrypted": false } }
1 2{ "data": { "appStorage": { "setAppStoredEntity": { "success": true, "errors": null } } } }
Gets a value
by key
.
If the requested key
is not found, the value
is returned as null
.
1 2query forge_app_getExample($contextAri: ID!, $key: ID!, $encrypted: Boolean!) { appStoredEntity( contextAri: $contextAri, key: $key, encrypted: $encrypted ) { value } }
GraphQL variables
1 2{ "contextAri": "ari:cloud:confluence::site/1234", "key": "example-key", "encrypted": true | false }
Variable | Description |
---|---|
contextAri | The unique identifier for the context in which the GraphQL query is invoked. |
key | The key for which to retrieve a value from Forge Storage. |
encrypted | A boolean indicating whether this Forge Storage entry is encrypted. |
GraphQL response (Successfully retrieved the key's value)
1 2{ "data": { "appStoredEntity": { "value": "example-value" } } }
GraphQL response (Key not found)
1 2{ "data": { "appStoredEntity": { "value": null } } }
Get the value of the key "example-key".
GraphQL variables
1 2{ "contextAri": "ari:cloud:confluence::site/1234" "key": "example-key", "encrypted": false }
1 2{ "data": { "appStoredEntity": { "value": "example-value" } } }
Builds a query that returns a list of entities (key and value).
1 2query forge_app_queryExample($contextAri: ID!, $where: [AppStoredEntityFilter!]!, $first: Int!, $after: String) { appStoredEntities(contextAri: $contextAri, where: $where, first: $first, after: $after) { edges { node { value key } cursor } pageInfo { hasNextPage } } }
Property | Description |
---|---|
edges | An array of returned key-value pairs and cursors. |
edges.node | The requested contents of Forge Storage, returned as a key-value pair. |
edges.cursor | A string value that can be supplied in the after field of a subsequent query, to fetch the next page of data. |
pageInfo.hasNextPage | A boolean indicating whether there is still more data that can be returned if the query is invoked again with after set to the cursor value. |
GraphQL variables
1 2{ "contextAri": "ari:cloud:confluence::site/1234", "where": { "field": "key", "condition": "STARTS_WITH", "value": "example key" }, "first": 10, "encrypted": true | false, "after": "Optional, used for paginated requests. Use the cursor field from the previous query to fetch the next page." }
Variable | Description |
---|---|
contextAri | The unique identifier for the context in which the GraphQL query is invoked. |
where | The selection criteria for query results. |
where.field | The field on which to query. This must be set to "key" . |
where.condition | The query operator to use. This must be set to "STARTS_WITH" . |
where.value | The key for which to retrieve values. |
first | The maximum number of entries this query can return. This can be set to at most 20. |
encrypted | A boolean indicating whether the data is encrypted or not. |
after | An optional cursor value used to retrieve subsequent pages of data for a query that has more than one page of matching results. |
GraphQL response (Successfully retrieved 2 entries)
1 2{ "data": { "appStoredEntities": { "edges": [ { "node": { "value": "test-value-1", "key": "test-key-1" }, "cursor": "FAJ0ZXN0LWtleS0xAA==" }, { "node": { "value": "test-value-2", "key": "test-key-2" }, "cursor": "FAJ0ZXN0LWtleS0yAA==" } ], "pageInfo": { "hasNextPage": true } } } }
Get up to 10 results where the key begins with “example”. Start from the cursor location FAJ0ZXN0LWtleS0yAA==
.
Set the maximum number of responses to return using the first
property. The maximum number of responses you can request is 20.
You can retrieve the value of the cursor
from the response of your previous query. If there are more pages to fetch, the hasNextPage
field will be true
.
GraphQL variables
1 2{ "contextAri": "ari:cloud:confluence::site/1234", "where": { "field": "key", "condition": "STARTS_WITH", "value": "example" }, "first": 10, "encrypted": false, "after": "FAJ0ZXN0LWtleS0yAA==" }
1 2{ "data": { "appStoredEntities": { "edges": [ { "node": { "value": "test-value-2", "key": "example-2" }, "cursor": "FAJ0ZXN0LWtleS0yAA==" }, { "node": { "value": "test-value-3", "key": "example-3" }, "cursor": "FAJ0ZXN0LWtleS0zAA==" } ] } } }
Deletes a value
by key
.
1 2mutation forge_app_deleteExample($input: DeleteAppStoredEntityMutationInput!) { appStorage { deleteAppStoredEntity(input: $input) { success errors { message extensions { errorType statusCode } } } } }
GraphQL variables
1 2{ "input": { "contextAri": "ari:cloud:confluence::site/1234", "key": "example key", "encrypted" true | false } }
Variable | Description |
---|---|
contextAri | The unique identifier for the context in which the GraphQL query is invoked. |
key | The key for which to retrieve a value from Forge Storage. |
encrypted | A boolean indicating whether this Forge Storage entry is encrypted. |
GraphQL response (Successfully deletes the key)
1 2{ "data": { "appStorage": { "deleteAppStoredEntity": { "success": true, "errors": null } } } }
GraphQL response (Fails to delete the key due to an invalid key provided in the request)
1 2{ "data": { "appStorage": { "deleteAppStoredEntity": { "success": false, "errors": [ { "message": "Key \"test-key-@\" should match the pattern \"/^(?!\\s+$)[a-zA-Z0-9:._\\s-#]+$/\"", "extensions": { "errorType": "INVALID_KEY", "statusCode": 400 } } ] } } } }
Delete the value associated with the key "example-key".
GraphQL variables
1 2{ "input": { "contextAri": "ari:cloud:confluence::site/1234", "key": "example key", "encrypted" false } }
1 2{ "data": { "appStorage": { "deleteAppStoredEntity": { "success": true, "errors": null } } } }
For further help, see how you can:
Rate this page: