Error Handling
Learn how to handle errors effectively with the JustTCG JavaScript/TypeScript SDK.
Error Types
The SDK surfaces errors in two primary ways: thrown exceptions for critical SDK issues and an error property on the response object for API issues.
1. Thrown Exceptions (SDK-level)
Critical errors that happen at the SDK level will be thrown as JavaScript Error
objects.
- • Authentication failures (401)
- • Invalid parameter values
- • Network connectivity issues
- • SDK configuration errors
2. Response Error Property (API-level)
API-level validation errors are returned in the response object.
- • Invalid API keys (401/403)
- • Rate limit exceeded (429)
- • Missing required parameters
- • Invalid lookup parameters
Handling Thrown Exceptions
SDK-level errors must be wrapped in a try...catch
block:
try {
const client = new JustTCG();
const response = await client.v1.games.list();
// Process successful response
console.log('Games:', response.data);
} catch (error) {
// Handle SDK-level errors
if (error instanceof Error) {
console.error('SDK Error:', error.message);
// Handle specific error types
if (error.message.includes('Authentication')) {
console.error('Please check your API key');
} else if (error.message.includes('Invalid parameter')) {
console.error('Please check your request parameters');
}
}
}
Common SDK Errors
Authentication Error
Authentication error: API key is missing
Solution: Set the JUSTTCG_API_KEY environment variable or provide the key directly
Invalid Parameter Error
Invalid parameter value: orderBy must be one of 'price', '24h', '7d', '30d', '90d'
Solution: Check your parameter values against the API documentation
Handling Response Errors
API-level errors are returned in the response object and should be checked after each call:
const client = new JustTCG();
const response = await client.v1.sets.list();
// Check for API-level errors
if (response.error) {
console.error('API Error:', response.error);
console.error('Error Code:', response.code);
// Handle specific error codes
switch (response.code) {
case 'INVALID_REQUEST':
console.error('Invalid request parameters');
break;
case 'RATE_LIMIT_EXCEEDED':
console.error('Rate limit exceeded, please wait');
break;
case 'UNAUTHORIZED':
console.error('Invalid API key');
break;
default:
console.error('Unknown API error');
}
return; // Exit early on error
}
// Process successful response
console.log('Sets:', response.data);
Common API Error Codes
INVALID_REQUEST
Missing or invalid parametersRATE_LIMIT_EXCEEDED
Too many requestsUNAUTHORIZED
Invalid API keyNOT_FOUND
Resource not foundComprehensive Error Handling
For production applications, you should implement comprehensive error handling that covers both error types:
import { JustTCG } from 'justtcg-js';
class RobustAPIClient {
private client: JustTCG;
constructor() {
this.client = new JustTCG();
}
async safeApiCall<T>(apiCall: () => Promise<T>): Promise<{
success: boolean;
data?: T;
error?: string;
code?: string;
}> {
try {
const result = await apiCall();
// Check for API-level errors
if (result && typeof result === 'object' && 'error' in result && result.error) {
return {
success: false,
error: result.error,
code: result.code
};
}
return {
success: true,
data: result
};
} catch (error) {
// Handle SDK-level errors
if (error instanceof Error) {
return {
success: false,
error: error.message
};
}
return {
success: false,
error: 'Unknown error occurred'
};
}
}
async getGames() {
const result = await this.safeApiCall(() => this.client.v1.games.list());
if (result.success) {
console.log('Games:', result.data?.data);
return result.data;
} else {
console.error('Failed to get games:', result.error);
return null;
}
}
async getCard(cardId: string) {
const result = await this.safeApiCall(() =>
this.client.v1.cards.get({ cardId })
);
if (result.success) {
return result.data;
} else {
console.error(`Failed to get card ${cardId}:`, result.error);
return null;
}
}
}
// Usage
const apiClient = new RobustAPIClient();
const games = await apiClient.getGames();
const card = await apiClient.getCard('some-card-id');
Retry Logic and Resilience
For production applications, implement retry logic to handle temporary failures:
class ResilientAPIClient {
private client: JustTCG;
private maxRetries: number = 3;
private retryDelay: number = 1000;
constructor() {
this.client = new JustTCG();
}
async withRetry<T>(apiCall: () => Promise<T>): Promise<T | null> {
for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
try {
const result = await apiCall();
// Check for API-level errors
if (result && typeof result === 'object' && 'error' in result && result.error) {
// Don't retry on API errors (they won't change)
if (result.code === 'RATE_LIMIT_EXCEEDED') {
// Wait longer for rate limit errors
await this.delay(this.retryDelay * 2);
continue;
}
throw new Error(result.error);
}
return result;
} catch (error) {
console.error(`Attempt ${attempt} failed:`, error);
if (attempt === this.maxRetries) {
console.error('All retry attempts failed');
return null;
}
// Wait before retrying (exponential backoff)
await this.delay(this.retryDelay * attempt);
}
}
return null;
}
private delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
async getGames() {
return this.withRetry(() => this.client.v1.games.list());
}
}
// Usage
const resilientClient = new ResilientAPIClient();
const games = await resilientClient.getGames();
Error Logging and Monitoring
Implement proper error logging and monitoring for production applications:
interface ErrorLog {
timestamp: Date;
error: string;
code?: string;
context: string;
severity: 'low' | 'medium' | 'high' | 'critical';
}
class MonitoredAPIClient {
private client: JustTCG;
private errorLogs: ErrorLog[] = [];
constructor() {
this.client = new JustTCG();
}
private logError(error: string, code?: string, context: string = 'API Call', severity: ErrorLog['severity'] = 'medium') {
const logEntry: ErrorLog = {
timestamp: new Date(),
error,
code,
context,
severity
};
this.errorLogs.push(logEntry);
// Log to console in development
if (process.env.NODE_ENV === 'development') {
console.error(`[${severity.toUpperCase()}] ${context}:`, error);
}
// In production, send to your logging service
if (process.env.NODE_ENV === 'production') {
this.sendToLoggingService(logEntry);
}
}
private sendToLoggingService(logEntry: ErrorLog) {
// Send to your preferred logging service (e.g., Sentry, LogRocket, etc.)
console.log('Sending to logging service:', logEntry);
}
async getGames() {
try {
const response = await this.client.v1.games.list();
if (response.error) {
this.logError(response.error, response.code, 'Get Games', 'medium');
return null;
}
return response;
} catch (error) {
this.logError(
error instanceof Error ? error.message : 'Unknown error',
undefined,
'Get Games',
'high'
);
return null;
}
}
getErrorLogs(): ErrorLog[] {
return this.errorLogs;
}
getErrorStats() {
const total = this.errorLogs.length;
const bySeverity = this.errorLogs.reduce((acc, log) => {
acc[log.severity] = (acc[log.severity] || 0) + 1;
return acc;
}, {} as Record<string, number>);
return { total, bySeverity };
}
}
// Usage
const monitoredClient = new MonitoredAPIClient();
const games = await monitoredClient.getGames();
// Check error statistics
const stats = monitoredClient.getErrorStats();
console.log('Error statistics:', stats);
Best Practices
1. Always Handle Both Error Types
Use try-catch for SDK errors and check response.error for API errors.
2. Implement Retry Logic
Retry transient errors with exponential backoff, but don't retry API validation errors.
3. Log Errors Appropriately
Log errors with sufficient context for debugging, but avoid logging sensitive information.
4. Monitor API Usage
Check usage metadata to avoid hitting rate limits and plan limits.
5. Provide User-Friendly Messages
Transform technical error messages into user-friendly notifications when appropriate.