Comprehensive TypeScript SDK for the Star Wars Combine API v2.0
Features • Installation • Quick Start • Documentation • Examples
Page<T> Pagination - Every list endpoint returns Page<T> with hasMore, getNextPage(), and for await...of auto-paginationnpm install swcombine-sdk
# or
yarn add swcombine-sdk
# or
pnpm add swcombine-sdk
import { SWCombine, AccessType } from 'swcombine-sdk';
// Public mode (no auth)
const publicClient = new SWCombine();
// Token-only mode (use an existing token)
const tokenClient = new SWCombine({
token: process.env.SWC_ACCESS_TOKEN!,
});
// Full OAuth mode (required for OAuth flows and token refresh)
const fullClient = new SWCombine({
clientId: process.env.SWC_CLIENT_ID!,
clientSecret: process.env.SWC_CLIENT_SECRET!,
token: process.env.SWC_ACCESS_TOKEN, // Optional - string or OAuthToken object
redirectUri: 'http://localhost:3000/callback',
accessType: AccessType.Offline,
});
// Look up a character's UID from their handle (no auth required).
// Returns `{ uid, handle }`; use `character.get({ uid })` for the full profile.
const { uid, handle } = await publicClient.character.getByHandle({
handle: 'character-handle',
});
console.log(uid); // "1:12345"
console.log(handle); // "character-handle"
// For authenticated endpoints, provide an access token
const authenticatedClient = new SWCombine({
token: process.env.SWC_ACCESS_TOKEN!,
});
// Get character details
const character = await authenticatedClient.character.get({
uid: '1:12345',
});
// Get character messages — list() returns Page<T>
import { MessageMode } from 'swcombine-sdk';
const messages = await authenticatedClient.character.messages.list({
uid: '1:12345',
mode: MessageMode.Received,
});
console.log(messages.total); // total messages across all pages
console.log(messages.data.length); // items on this page
const firstMessageId = messages.data[0]?.attributes.uid;
if (firstMessageId) {
const fullMessage = await authenticatedClient.character.messages.get({
uid: '1:12345',
messageId: firstMessageId,
});
console.log(fullMessage.communication);
}
// Send a message
// IMPORTANT: use receiver handle(s), not UID(s), for `receivers`
await authenticatedClient.character.messages.create({
uid: '1:12345',
receivers: 'recipient_handle',
communication: 'Test message',
});
// Get faction information
const faction = await authenticatedClient.faction.get({
uid: '20:123',
});
All list() methods return a Page<T> with built-in pagination support. Single-item get() methods return the entity directly — no wrapper.
// get() returns the entity directly — no .data needed
const character = await client.character.get({ uid: '1:12345' });
console.log(character.name);
// list() returns Page<T> — access items via .data
const ships = await client.inventory.entities.list({
entityType: 'ships',
uid: '1:12345',
assignType: 'owner',
});
ships.data; // Ship[] — items on this page
ships.total; // total ships across all pages
ships.hasMore; // boolean — are there more pages?
// Fetch the next page (preserves your filters)
if (ships.hasMore) {
const nextPage = await ships.getNextPage();
}
// Auto-paginate through everything with for-await
for await (const ship of await client.inventory.entities.list({...})) {
console.log(ship.name); // yields every ship across all pages
}
Use the included helper script to get an access token:
# 1. Add your credentials to .env
echo "SWC_CLIENT_ID=your_client_id" >> .env
echo "SWC_CLIENT_SECRET=your_client_secret" >> .env
# 2. Run the OAuth helper
npm run get-token
# 3. Visit http://localhost:3000 in your browser
# 4. Authorize the app and copy the token to .env
OAuth-only methods require full OAuth mode (clientId + clientSecret):
client.auth.getAuthorizationUrl(...)client.auth.handleCallback(...)client.auth.revokeToken(...)client.refreshToken()import { SWCombine, AccessType, CharacterScopes, MessageScopes } from 'swcombine-sdk';
const client = new SWCombine({
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
redirectUri: 'http://localhost:3000/callback',
accessType: AccessType.Offline, // Get refresh token
});
// 1. Generate authorization URL
const authUrl = client.auth.getAuthorizationUrl({
scopes: [
CharacterScopes.READ,
CharacterScopes.STATS,
MessageScopes.READ,
MessageScopes.SEND,
],
state: 'random-csrf-token',
});
// 2. Redirect user to authUrl...
// 3. Handle callback
const result = await client.auth.handleCallback(req.query);
if (result.success) {
const token = result.token!;
console.log('Access Token:', token.accessToken);
console.log('Refresh Token:', token.refreshToken);
}
import {
CharacterScopes,
MessageScopes,
Scopes,
getAllScopes,
getReadOnlyScopes,
} from 'swcombine-sdk';
// Use constants with autocomplete
const scopes = [
CharacterScopes.READ, // TypeScript suggests all scopes
CharacterScopes.STATS,
MessageScopes.SEND,
Scopes.PersonalInventory.SHIPS.READ,
];
// Or use helpers
const readOnly = getReadOnlyScopes();
const everything = getAllScopes();
See OAuth Scopes Guide for all 170+ available scopes.
The SDK provides access to all SW Combine API v2.0 resources through a fluent, type-safe interface:
| Resource | Access | Description |
|---|---|---|
client.api |
Utilities | Hello world, permissions, rate limits, time conversion |
client.character |
Characters | Profile, messages, skills, privileges, credits, credit log |
client.faction |
Factions | Info, members, budgets, stockholders, credits, credit log |
client.galaxy |
Galaxy | Systems, sectors, planets, stations, cities |
client.inventory |
Inventory | Entity listing, management, tagging |
client.market |
Market | Vendor listings |
client.news |
News | GNS and Sim News feeds |
client.types |
Types | Entity types, classes, and detailed type info |
client.events |
Events | Personal, faction, inventory, and combat events |
client.location |
Location | Entity location lookups |
client.datacard |
Datacards | Datacard management and assignment |
Also includes a Timestamp utility for Combine Galactic Time (CGT) conversion and formatting.
For complete method signatures, parameters, and examples, see the API Reference Documentation.
The SW Combine API has a rate limit of 600 requests per hour. The SDK provides tools to monitor and handle rate limits:
// Check current rate limit status after any API call
const rateLimit = client.getRateLimitInfo();
if (rateLimit) {
console.log(`${rateLimit.remaining}/${rateLimit.limit} requests remaining`);
console.log(`Resets at: ${rateLimit.resetTime}`);
}
// Set up a callback to monitor rate limits in real-time
client.onRateLimitUpdate((info) => {
if (info.remaining < 100) {
console.warn(`Warning: Only ${info.remaining} API requests remaining!`);
}
});
// Or check via API endpoint for detailed per-endpoint limits
const limits = await client.api.rateLimits();
The SDK automatically handles rate limit errors with exponential backoff and respects the Retry-After header when provided.
import { SWCError } from 'swcombine-sdk';
try {
const character = await client.character.get({ uid: '1:12345' });
} catch (error) {
if (error instanceof SWCError) {
console.error('Status:', error.statusCode); // 404
console.error('Message:', error.message); // "Resource not found"
console.error('Type:', error.type); // "not_found"
console.error('Retryable:', error.retryable); // false
}
}
Full TypeScript support with intelligent type inference:
import { Page, Message, MessageListItem, MessageMode } from 'swcombine-sdk';
// Types are automatically inferred
const character = await client.character.get({ uid: '1:12345' });
// character: Character
// list() returns Page<T> with full type safety
const messages: Page<MessageListItem> = await client.character.messages.list({
uid: '1:12345',
mode: MessageMode.Received, // MessageMode.Sent | MessageMode.Received
});
const messageId = messages.data[0]?.attributes.uid;
if (messageId) {
const messageDetail: Message = await client.character.messages.get({
uid: '1:12345',
messageId,
});
console.log(messageDetail.communication);
}
// Send message: receivers must be handle(s), not UID(s)
await client.character.messages.create({
uid: '1:12345',
receivers: 'recipient_handle_1;recipient_handle_2',
communication: 'Hello there',
});
interface ClientConfig {
// Optional OAuth credentials
// If provided, both must be set together
clientId?: string;
clientSecret?: string;
// Optional authentication - string or full OAuthToken object
token?: string | OAuthToken;
// Optional OAuth settings
redirectUri?: string;
accessType?: AccessType; // AccessType.Online | AccessType.Offline
// Optional HTTP settings
baseURL?: string; // Default: https://www.swcombine.com/ws/v2.0/
timeout?: number; // Default: 30000 (30 seconds)
maxRetries?: number; // Default: 3
retryDelay?: number; // Default: 1000ms
debug?: boolean; // Default: false
}
interface OAuthToken {
accessToken: string;
refreshToken?: string;
expiresAt: number; // Timestamp in milliseconds
}
See the examples directory for complete working examples:
import { SWCombine } from 'swcombine-sdk';
const client = new SWCombine({
token: process.env.SWC_ACCESS_TOKEN!,
});
// Resolve a handle → UID, then fetch the full profile
const { uid, handle } = await client.character.getByHandle({ handle: 'character-name' });
const character = await client.character.get({ uid });
console.log(`${character.name} (${handle}, ${character.uid})`);
Interactive OAuth flow to obtain access tokens:
npm run get-token
Quickly get a character's UID from their handle:
npm run get-character-uid YourHandle
# Install dependencies
npm install
# Build
npm run build
# Run unit tests (fast, no API calls)
npm test
# Run unit tests in watch mode
npm run test:watch
# Lint
npm run lint
# Format code
npm run format
Note: An integration test suite exists under
tests/integration/but runs against the live SW Combine API and shares the global 600 req/hour rate limit. It is not intended for routine developer use — please don't run it as part of normal workflows. Maintainers should consulttests/integration/README.mdbefore running it against production.
MIT © Dreks Selmur aka JonMarkGo
Contributions are welcome! Please: