The FRAGMENT API is available in the following AWS regions:
us-east-1us-east-2us-west-2eu-west-1The region for a workspace can be found in the API URL in the settings tab of the dashboard. Use the top-left dropdown in the dashboard to create a new workspace.
Contact us if you don't see your desired AWS region.
FRAGMENT uses OAuth2's client credentials flow to authenticate API clients. The call requesting an access token follows the OAuth2 spec. Use an OAuth2 library that supports the client credentials grant to retrieve the token or make an HTTP request. The flow is:
Authorization request header with the value Bearer {{access_token}} when calling the GraphQL API.The token request payload should be in x-www-form-urlencoded format, with the keys grant_type, scope and client_id.
grant_type=client_credentials&scope={{scope}}&client_id={{client_id}}The token request headers should contain the following items, where client_credentials is the Base64-encoded version of: {{client_id}}:{client_secret}}.
{
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": "Basic {{client_credentials}}",
"Accept": "*/*"
}The response is a JSON object containing the access token.
{
"access_token": "<access token>",
"expires_in": 3600,
"token_type": "Bearer"
}
Putting it all together:
import axios from "axios";
const btoa = (str) => Buffer.from(str).toString("base64");
// credentials from the dashboard
const clientId = "2h4t0cv7qv5c9r0qgs1o3erkuk";
const secret = "superSecretClientSecret";
const scope = "https://api.us-west-2.fragment.dev/*";
const authUrl = "https://auth.us-west-2.fragment.dev/oauth2/token";
const getToken = async () => {
// encode the client id and secret
const auth = btoa(`${clientId}:${secret}`);
// create the request body
const data = new URLSearchParams();
data.append("grant_type", "client_credentials");
data.append("scope", scope);
data.append("client_id", clientId);
// retrieve the token
const response = await axios.request({
method: "POST",
url: authUrl,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Authorization: `Basic ${auth}`,
Accept: "*/*",
},
data,
});
if (!response.data.access_token) {
throw new Error(
"didn't get an access token from auth endpoint"
);
}
return response.data.access_token;
};
To ensure write operations are executed exactly once, all write mutations in FRAGMENT are idempotent. This enables applications to safely retry operations without risk of duplicate effects. Applications only need to guarantee that the API is called at least once.
Mutations require a unique idempotency key (ik). For calls to reconcileTx, syncCustomAccounts and syncCustomTxs, FRAGMENT internally uses the transaction or account ID from the request as the idempotency key.
Additional requests with the same ik are ignored and the original response is returned with isIkReplay: true in the response.
Idempotency keys are scoped per-Ledger; requests with the same IK to different Ledgers will execute the mutation and return isIkReplay: false.
According to the GraphQL spec, all responses will return an object containing data and errors.
data: the result of a query or mutation. In the FRAGMENT API, all successful mutations return a union type that represents an application error or a successful result.
For example, the response type of the addLedgerEntry mutation is:
union AddLedgerEntryResponse =
AddLedgerEntryResult | BadRequestError | InternalErrorQueries can return null if an error was raised during the request. This is accompanied by a non-empty errors list.
errors: a list of errors raised during the request. This typically gets populated when issuing a query if a required argument is missing or an invalid ID is provided.
When calling the API, handle errors in the following order:
null responses from queries by checking the errors key. This typically happens when querying an item with an invalid ID.__typename field. This contains BadRequestError, InternalError or the appropriate <Mutation>Result type.InternalError with retries and exponential backoff.BadRequestError by failing the operation.<Mutation>Result types such as AddLedgerEntryResult.An example of this written in TypeScript:
import assert from 'assert';
type RequestParams = {
query: string;
variables: Record<string, unknown>;
};
const makeRequest = async ({ query, variables }: RequestParams): Promise<Response> => {
const token = await getToken();
return fetch('Fragment GraphQL URL', {
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ query, variables }),
});
};
const query = '''
mutation addLedgerEntry($ik: SafeString, $entry: LedgerEntryInput!) {
addLedgerEntry(ik: $ik, entry: $entry) {
__typename
...
}
}
''';
const variables = { ik: 'sample-ik', entry: {...} };
const handleRequest = (req: Response) => {
if (req.status === 429 || req.status >= 500) {
// Rate-limited or intermittent http failures. Retry the request.
return handleRequest(await makeRequest({ query, variables }));
}
if ((req.status >= 400 || req.status < 500) && req.status !== 429) {
// Invalid GraphQL request provided to Fragment. Handle the error.
throw new Error('Invalid GraphQL request');
}
// .json() checks that it was a 200
const response = await req.json();
if (response.data.addLedgerEntry.__typename === 'InternalError') {
// Retry the request in case of internal errors, with backoff.
return handleRequest(await makeRequest({ query, variables }));
}
if (response.data.addLedgerEntry.__typename === 'BadRequestError') {
// Invalid request provided to Fragment. Handle the error.
throw new Error('Invalid API request to Fragment');
}
return response;
};
const response = handleRequest(await makeRequest({ query, variables }));
// Entry successfully posted to Fragment. Handle the response.
assert(response.data.addLedgerEntry.__typename === 'AddLedgerEntryResult');
handlePostedEntry(response.data.addLedgerEntry);
To test how your application handles errors, use the X-Fragment-Return-Error request header to instruct the API to return erroneous responses.
Set the X-Fragment-Return-Error header to:
internalError to instruct the API to return an InternalErrorbadRequestError to instruct the API to return a BadRequestErrorWhen requesting an erroneous response, FRAGMENT will skip processing your request and return the error immediately.