Skip to main content

Overview

The ArtifactAPILogic class provides an interface for retrieving detailed artifact information from both the Metropolitan Museum of Art (Met) API and a self-hosted backup source. It includes caching to optimize performance and reduce API calls.

Class Definition

class ArtifactAPILogic {
  final HashMap<String, ArtifactData?> _artifactCache = HashMap();
  ArtifactAPIService get service => GetIt.I.get<ArtifactAPIService>();
}

Properties

_artifactCache
HashMap<String, ArtifactData?>
Internal cache storing previously fetched artifact data by ID. Prevents redundant API calls.
service
ArtifactAPIService
The underlying service that handles HTTP requests to the Met API and self-hosted endpoints.

Methods

getArtifactByID()

Retrieves artifact data by its unique ID, with optional self-hosted fallback.
Future<ArtifactData?> getArtifactByID(
  String id, 
  {bool selfHosted = false}
) async
Parameters:
  • id - The artifact’s unique identifier from the Met Museum collection
  • selfHosted - If true, fetches from self-hosted source; if false, fetches from Met API (default: false)
Returns: Future<ArtifactData?> - The artifact data or null if not found Throws: Exception with localized error message if artifact cannot be found Caching Behavior:
  • First checks the internal cache for the artifact ID
  • If cached, returns immediately without making an API call
  • If not cached, fetches from the API and stores in cache
  • Cache persists for the lifetime of the app session
Example:
// Fetch from Met API
try {
  final artifact = await artifactLogic.getArtifactByID('545728');
  if (artifact != null) {
    print('Title: ${artifact.title}');
    print('Date: ${artifact.date}');
    print('Culture: ${artifact.culture}');
  }
} catch (e) {
  print('Error fetching artifact: $e');
}

// Fetch from self-hosted source
final selfHostedArtifact = await artifactLogic.getArtifactByID(
  '545728',
  selfHosted: true,
);

ArtifactAPIService

The underlying service that handles HTTP communication:
class ArtifactAPIService {
  final String _baseMETUrl = 'https://collectionapi.metmuseum.org/public/collection/v1';
  final String _baseSelfHostedUrl = 'https://www.wonderous.info/met';
  
  Future<ServiceResult<ArtifactData?>> getMetObjectByID(String id) async;
  Future<ServiceResult<ArtifactData?>> getSelfHostedObjectByID(String id) async;
}

API Endpoints

Met Museum API:
GET https://collectionapi.metmuseum.org/public/collection/v1/objects/{id}
Self-Hosted Backup:
GET https://www.wonderous.info/met/{id}.json

ArtifactData Structure

The returned artifact data includes:
class ArtifactData {
  final String objectId;         // Unique identifier
  final String title;            // Artifact name
  final String image;            // Primary image URL
  final String date;             // Date or period
  final String objectType;       // Type (e.g., "vessel", "statue")
  final String period;           // Historical period
  final String country;          // Country of origin
  final String medium;           // Materials used
  final String dimension;        // Physical dimensions
  final String classification;   // Classification category
  final String culture;          // Cultural attribution
  final int? objectBeginYear;    // Start year
  final int? objectEndYear;      // End year
}

ArtifactData Fields

objectId
String
The Met Museum’s unique identifier for the artifact.
title
String
The display name of the artifact.
image
String
URL to the primary high-resolution image.
date
String
Human-readable date or period (e.g., “ca. 1632”, “2nd century BCE”).
objectType
String
The type of object (e.g., “jar”, “textile”, “sculpture”).
culture
String
Cultural or civilizational attribution (e.g., “Egyptian”, “Roman”).
medium
String
Materials and techniques used (e.g., “bronze”, “gold and lapis lazuli”).
objectBeginYear
int?
Numeric start year for the artifact’s creation period (can be null).
objectEndYear
int?
Numeric end year for the artifact’s creation period (can be null).

Usage Examples

Basic Artifact Retrieval

// Access the global ArtifactAPILogic instance
ArtifactAPILogic get artifactLogic => GetIt.I.get<ArtifactAPILogic>();

// Fetch artifact details
final artifact = await artifactLogic.getArtifactByID('545728');
if (artifact != null) {
  print('Found: ${artifact.title}');
  print('From: ${artifact.culture}');
  print('Date: ${artifact.date}');
}

Displaying Artifact Information

Future<void> displayArtifactDetails(String artifactId) async {
  try {
    final artifact = await artifactLogic.getArtifactByID(artifactId);
    
    if (artifact == null) {
      print('Artifact not found');
      return;
    }
    
    print('━━━━━━━━━━━━━━━━━━━━━━━━━━');
    print('Title: ${artifact.title}');
    print('Type: ${artifact.objectType}');
    print('Culture: ${artifact.culture}');
    print('Period: ${artifact.period}');
    print('Date: ${artifact.date}');
    print('Medium: ${artifact.medium}');
    print('Dimensions: ${artifact.dimension}');
    print('Country: ${artifact.country}');
    print('Image: ${artifact.image}');
    print('━━━━━━━━━━━━━━━━━━━━━━━━━━');
  } catch (e) {
    print('Error: $e');
  }
}

Batch Retrieval with Caching

// Fetch multiple artifacts efficiently
Future<List<ArtifactData?>> fetchMultipleArtifacts(
  List<String> artifactIds,
) async {
  final results = <ArtifactData?>[];
  
  for (var id in artifactIds) {
    // Subsequent calls for the same ID will use cache
    final artifact = await artifactLogic.getArtifactByID(id);
    results.add(artifact);
  }
  
  return results;
}

// Example usage
final ids = ['545728', '39666', '308120'];
final artifacts = await fetchMultipleArtifacts(ids);
print('Fetched ${artifacts.whereType<ArtifactData>().length} artifacts');

Using Self-Hosted Source

// Fetch from self-hosted backup (faster, more reliable)
final artifact = await artifactLogic.getArtifactByID(
  '545728',
  selfHosted: true,
);

// Useful for displaying collectible details
Future<void> showCollectibleDetails(CollectibleData collectible) async {
  final artifactData = await artifactLogic.getArtifactByID(
    collectible.id,
    selfHosted: true, // Use self-hosted for better performance
  );
  
  if (artifactData != null) {
    print('${collectible.title}');
    print('Date: ${artifactData.date}');
    print('Culture: ${artifactData.culture}');
  }
}

Error Handling

// Handle potential errors
Future<ArtifactData?> safeGetArtifact(String id) async {
  try {
    return await artifactLogic.getArtifactByID(id);
  } catch (e) {
    print('Failed to fetch artifact $id: $e');
    return null;
  }
}

// With fallback to self-hosted
Future<ArtifactData?> getArtifactWithFallback(String id) async {
  try {
    // Try Met API first
    return await artifactLogic.getArtifactByID(id);
  } catch (e) {
    print('Met API failed, trying self-hosted...');
    try {
      return await artifactLogic.getArtifactByID(id, selfHosted: true);
    } catch (e2) {
      print('Both sources failed: $e2');
      return null;
    }
  }
}

Integration with Collectibles

// Get detailed info for a collectible
Future<void> exploreCollectible(String collectibleId) async {
  // Update collectible state
  collectiblesLogic.setState(collectibleId, CollectibleState.explored);
  
  // Fetch detailed artifact data
  final artifact = await artifactLogic.getArtifactByID(
    collectibleId,
    selfHosted: true,
  );
  
  if (artifact != null) {
    // Display artifact details in UI
    displayArtifactDetails(artifact);
  }
}

Cache Benefits

// First call makes an API request
final artifact1 = await artifactLogic.getArtifactByID('545728');
print('Fetched from API');

// Second call returns cached data instantly
final artifact2 = await artifactLogic.getArtifactByID('545728');
print('Returned from cache');

// Both variables reference the same data
assert(artifact1 == artifact2);

ServiceResult

The API service returns results wrapped in a ServiceResult object:
class ServiceResult<T> {
  final bool success;      // Whether the request succeeded
  final T? content;        // The parsed content (if successful)
  final String? error;     // Error message (if failed)
}

Registration

Both the logic and service are registered as singletons using GetIt:
void registerSingletons() {
  GetIt.I.registerLazySingleton<ArtifactAPILogic>(() => ArtifactAPILogic());
  GetIt.I.registerLazySingleton<ArtifactAPIService>(() => ArtifactAPIService());
}

API Reference

The Met Museum API documentation is available at:
  • ArtifactAPIService - HTTP service for API requests
  • ArtifactData - Data model for artifact information
  • CollectiblesLogic - Uses artifact API for collectible details
  • HttpClient - Underlying HTTP client utility
  • ServiceResult - Wrapper for service responses

Build docs developers (and LLMs) love