Skip to main content

JavaScript client

The official client for the Attlaz API, written in TypeScript and published to npm as @attlaz/client. It handles authentication, token refresh, pagination and error mapping, so you work with typed objects instead of HTTP.

Install

npm install @attlaz/client

Authenticate

Two ways, matching the two ways to get a token described in Authentication.

With a token — a personal access token, or one you obtained yourself:

import {Client} from '@attlaz/client';

const client = new Client('<access_token>');

With client credentials — for a server-to-server integration. The client mints a token on the first request and refreshes it when it expires:

const client = new Client();
client.setClientCredentials('<client_id>', '<client_secret>');

For a browser app or anywhere a secret cannot be kept, use a public client instead — no secret, and you supply the token yourself:

client.setPublicClient('<client_id>');

Concurrent requests share a single token acquisition, so a burst of calls on a cold client produces one token request rather than one per call.

Make a call

Endpoints hang off the client, one accessor per area:

const flows = await client.getFlowEndpoint().getByProject('<project_id>');
const dataset = await client.getQualityEndpoint().getDataset('<dataset_id>');

A method that fetches one thing returns null when it does not exist, rather than throwing — so a missing record is something you branch on:

const dataset = await client.getQualityEndpoint().getDataset('<dataset_id>');
if (dataset === null) {
// no such dataset
}

Pagination

List endpoints are cursor-paginated and return a CollectionResult — the page plus whether more exist:

import {CursorPagination} from '@attlaz/client';

const pagination = new CursorPagination();
pagination.limit = 100;

const page = await client.getQualityEndpoint().getEntities('<dataset_id>', pagination);
page.getData(); // the records
page.hasMore; // is there another page

To walk every page, use LoadAllHelper rather than looping yourself:

import {LoadAllHelper} from '@attlaz/client';

const all = await LoadAllHelper.loadAll(
(pagination) => client.getQualityEndpoint().getEntities('<dataset_id>', pagination),
);

It stops at 50,000 records and throws — it is for collections you know are small. For anything larger, page explicitly and process each page as it arrives. It also throws if a page comes back empty while still reporting that more results exist: the cursor moves on from the last record, so there is no way to page past an empty one, and stopping with an error beats looping on the same request.

Errors

Failed requests throw. Transport failures throw ClientError; errors the API reported throw ApiError, which extends it — so one catch covers both, and httpStatus tells you which:

import {ApiError, ClientError} from '@attlaz/client';

try {
await client.getQualityEndpoint().getDataset('<dataset_id>');
} catch (error) {
if (error instanceof ApiError) {
error.httpStatus; // e.g. 403
}
}

Timeouts

A request is given 80 seconds by default, after which it is aborted and a ClientError is thrown naming the URL and the budget. Without this a request against an unresponsive host would hang indefinitely — fetch applies no timeout of its own in a browser, and bounds only response headers in Node.

Change it when constructing the client's options, or set 0 to disable it (not recommended):

options.timeoutMs = 30_000;

Reporting data-quality problems

Open a scan, report what you found in as many batches as you like, then complete it — completing is what resolves problems that were checked and are no longer there:

import {QualityReportEntity, QualityReportProblem} from '@attlaz/client';

const quality = client.getQualityEndpoint();
const scan = await quality.openScan('<dataset_id>', 'my-checks');

const entity = new QualityReportEntity('product', 'SKU-1');
entity.name = 'Blue lamp';
entity.problems = [new QualityReportProblem('shop.ean_invalid_checkdigit', 'ean')];
entity.passed = ['shop.name_too_short'];

await quality.reportToScan('<dataset_id>', scan.id, [entity]);
await quality.completeScan('<dataset_id>', scan.id);

If you already have everything in memory, reportProblems does all three in one call. Both are bound by the request size limit — batch into a scan for anything larger than a few thousand entities.