Skip to content

Contracts Browser ​

Needs an access token

Demonstrates browsing public region contracts (no auth) and fetching a character's personal contracts (auth required).

ESI Scopes Required:

  • None for public contracts (getPublicContracts)
  • esi-contracts.read_character_contracts.v1 (character contracts)

Calls: contracts.getPublicContracts · contracts.getPublicContractBids · contracts.getPublicContractItems · contracts.getCharacterContracts

Run it ​

bash
git clone https://github.com/lgriffin/ESI.ts.git && cd ESI.ts
npm ci
npm run example:contracts

examples/contracts-browser.ts on GitHub. The imports below are written as a consumer of the package writes them; the file in the repository imports from ../src.

Source ​

ts
import { EsiClient } from '@lgriffin/esi.ts';
import { EsiError } from '@lgriffin/esi.ts/errors';
import { createConsoleLogger } from '@lgriffin/esi.ts';

// The program's own output. The client logs through the same console sink
// at ESI_LOG_LEVEL (default warn), so its diagnostics stay out of the way.
const log = createConsoleLogger('info');
const esiLog = createConsoleLogger();

const THE_FORGE_REGION = 10000002; // Jita's region

async function main() {
  const client = new EsiClient({
    logger: esiLog,
    clientId: 'esi-ts-contracts-demo',
  });

  try {
    log.info('Contracts Browser\n');

    // --- Public contracts (no auth) ---
    log.info(
      `Fetching public contracts in The Forge (region ${THE_FORGE_REGION})...`,
    );
    const publicContracts =
      await client.contracts.getPublicContracts(THE_FORGE_REGION);
    log.info(`  Found ${publicContracts.length} public contracts\n`);

    // Break down by type. ESI lists only outstanding public contracts and
    // sends no status field on them.
    const byType = new Map<string, number>();
    for (const c of publicContracts) {
      byType.set(c.type, (byType.get(c.type) || 0) + 1);
    }

    log.info('Public Contracts by Type');
    log.info('-'.repeat(40));
    for (const [type, count] of [...byType.entries()].sort(
      (a, b) => b[1] - a[1],
    )) {
      log.info(`  ${type}: ${count.toLocaleString()}`);
    }

    // Show a sample auction contract with bids
    const auctions = publicContracts.filter((c) => c.type === 'auction');
    if (auctions.length > 0) {
      const auction = auctions[0]!;
      log.info(`\nSample Auction Contract #${auction.contract_id}`);
      log.info('-'.repeat(40));
      log.info(`  Price:       ${auction.price?.toLocaleString() || 0} ISK`);
      log.info(
        `  Buyout:      ${auction.buyout?.toLocaleString() || 'none'} ISK`,
      );
      log.info(
        `  Volume:      ${auction.volume?.toLocaleString() || 'N/A'} m3`,
      );
      log.info(`  Expires:     ${auction.date_expired}`);

      try {
        const bids = await client.contracts.getPublicContractBids(
          auction.contract_id,
        );
        log.info(`  Bids:        ${bids.length}`);
        if (bids.length > 0) {
          const topBid = bids.sort((a, b) => b.amount - a.amount)[0]!;
          log.info(`  Highest bid: ${topBid.amount.toLocaleString()} ISK`);
        }
      } catch {
        log.info('  Bids:        unavailable');
      }

      try {
        const items = await client.contracts.getPublicContractItems(
          auction.contract_id,
        );
        log.info(`  Items:       ${items.length}`);
        for (const item of items.slice(0, 3)) {
          const label = item.is_included ? 'included' : 'requested';
          log.info(`    Type ${item.type_id} x${item.quantity} (${label})`);
        }
        if (items.length > 3) {
          log.info(`    ... and ${items.length - 3} more items`);
        }
      } catch {
        log.info('  Items:       unavailable');
      }
    }

    // --- Character contracts (requires auth) ---
    // Scope: esi-contracts.read_character_contracts.v1
    log.info('\n--- Character Contracts ---');
    try {
      const characterId = 1689391488;
      const myContracts =
        await client.contracts.getCharacterContracts(characterId);
      log.info(`Found ${myContracts.length} personal contracts`);

      if (myContracts.length > 0) {
        const recent = myContracts.slice(0, 5);
        log.info('-'.repeat(40));
        for (const c of recent) {
          log.info(
            `  #${c.contract_id} | ${c.type} | ${c.status} | ${c.price?.toLocaleString() || 0} ISK`,
          );
        }
        if (myContracts.length > 5) {
          log.info(`  ... and ${myContracts.length - 5} more contracts`);
        }
      }
    } catch (err) {
      if (
        err instanceof Error &&
        (err.message.includes('NO_AUTH_TOKEN') ||
          (err instanceof EsiError &&
            (err.statusCode === 401 || err.statusCode === 403)))
      ) {
        log.info(
          'Skipped: ESI_ACCESS_TOKEN not set or missing scope esi-contracts.read_character_contracts.v1',
        );
      } else {
        throw err;
      }
    }
  } catch (err) {
    log.error('Request failed', { error: err });
    process.exit(1);
  } finally {
    await client.shutdown();
  }
}

main();

Released under the GPL-3.0-or-later license.