SDK API client

The API client of the SDK signs every request, retries the calls that are safe to repeat and throws one error class per documented error code.

Create the API client

AggregatorClient takes the aggregator’s base URL, your API key and its secret. Every request it sends carries the four signature headers the authentication page describes, signed by the same module the aggregator verifies with. Its methods and types are generated from the OpenAPI document, so a field the API does not have fails to compile.

client.ts

import { AggregatorClient } from '@asteriks-aggregator/sdk/api'

/** One client per API key; it signs every request with the key’s secret. */
export function aggregatorClientOf(apiKey: string, secret: string): AggregatorClient {
  return new AggregatorClient({
    baseUrl: 'https://aggregator.asteriks.casino',
    apiKey,
    secret,
    timeoutMs: 10_000, // per attempt, the answer body included
    retry: { attempts: 3, baseDelayMs: 200, maxDelayMs: 5_000 },
  })
}

A GET is safe to repeat, so the API client retries it after a network failure, a timeout or an undocumented 5xx, up to retry.attempts attempts in all, with a random backoff between them. A POST is sent once: a session launch is never sent twice. The options fetch, now, nonce, sleep and random replace the global fetch, the clock and the random sources, for example in your tests.

Launch a game

createSession sends the body of Create a session and returns the game_url to open. In real mode it creates a session; in demo mode the game opens without one and session_id is null. The sessions page explains both modes.

launch.ts

import { type AggregatorClient, LaunchNotSupportedError, type SessionCreated } from '@asteriks-aggregator/sdk/api'

/** The launch of a game in real mode, or undefined when the game cannot be played in the player’s currency. */
export async function launch(
  client: AggregatorClient,
  playerId: string,
  gameId: string,
): Promise<SessionCreated | undefined> {
  try {
    return await client.createSession({
      player_id: playerId,
      currency: 'EUR',
      game_id: gameId,
      language: 'en',
      country: 'TR',
      device: 'desktop',
      return_url: 'https://casino.example.com/lobby',
      mode: 'real',
    })
  } catch (error) {
    if (error instanceof LaunchNotSupportedError && error.code === 'CURRENCY_NOT_SUPPORTED') return undefined
    throw error
  }
}

Read the catalogue page by page

Every list operation has two methods: listGames returns one page with its next_cursor, and listGamesAll follows the cursors and yields every item of every page. The pagination page describes the cursors, and List games the filters.

catalogue.ts

import type { AggregatorClient, GameSummary } from '@asteriks-aggregator/sdk/api'

/** Every mobile game of one provider, across all pages. */
export async function mobileGames(client: AggregatorClient): Promise<GameSummary[]> {
  const games: GameSummary[] = []
  for await (const game of client.listGamesAll({ provider: 'drakon', device: 'mobile', limit: 100 }))
    games.push(game)
  return games
}

The same pair exists for rounds and transactions: listRounds and listRoundsAll, listTransactions and listTransactionsAll, filtered by player, game and time range. getRound returns one round with its transactions, and getReconciliationReport the report of one UTC day, or getReconciliationReportCsv the same report as CSV.

history.ts

import type { AggregatorClient, RoundSummary } from '@asteriks-aggregator/sdk/api'

/** The rounds one player opened on one UTC day, and that day’s reconciliation report as CSV. */
export async function dayOf(
  client: AggregatorClient,
  playerId: string,
  day: string,
): Promise<{ rounds: RoundSummary[]; report: string }> {
  const rounds: RoundSummary[] = []
  const to = new Date(Date.parse(`${day}T00:00:00Z`) + 86_400_000).toISOString()
  for await (const round of client.listRoundsAll({ playerId, from: `${day}T00:00:00Z`, to })) rounds.push(round)
  return { rounds, report: await client.getReconciliationReportCsv({ date: day }) }
}

Handle errors

Every error the API client throws is an AggregatorError with the operationId of the call. Its subclasses tell the cases apart:

  • AggregatorApiError: the aggregator answered with its error body; status and code are the answer’s. Each documented code has its own class: MalformedRequestError, UnauthorizedError, NotFoundError, LaunchNotSupportedError and InternalError. The errors page lists the codes.
  • AggregatorTransportError: no answer arrived, after attempts attempts. Its cause keeps only a safe name and code, never the request’s headers.
  • AggregatorResponseError: the answer matches nothing the contract documents for the operation.

errors.ts

import {
  AggregatorApiError,
  type AggregatorClient,
  AggregatorTransportError,
  type GameSummary,
  NotFoundError,
} from '@asteriks-aggregator/sdk/api'

/** One game, or undefined when the id is unknown; a failure to reach the aggregator is reported and thrown again. */
export async function gameOf(client: AggregatorClient, gameId: string): Promise<GameSummary | undefined> {
  try {
    return await client.getGame({ gameId })
  } catch (error) {
    if (error instanceof NotFoundError) return undefined
    if (error instanceof AggregatorTransportError) console.warn(`${error.operationId}: no answer after ${error.attempts} attempts`)
    else if (error instanceof AggregatorApiError) console.warn(`${error.operationId}: ${error.status} ${error.code}`)
    throw error
  }
}

Was this page helpful?