Add Generic STAC Resource Fetching

Open
#35 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
42/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Stale
Tech stack
react, typescript
Domain
api, frontend

Research direction

Start by reviewing the existing useCollection and useItem hooks, then consider the standalone-hooks work referenced in issue #34. Define the scope for fetching catalogs, collections, items, and item collections, including type determination, errors, relative links, and the listed hook options. Done should include tests for each resource type, invalid STAC and href handling, plus hook documentation and examples.

Written by the indexing model from the issue text.

Description

Problem

Currently, stac-react assumes you're working with a structured STAC API with known endpoints (/collections, /search, etc.). There's no way to fetch an arbitrary STAC resource (catalog, collection, or item) from any URL without knowing its type in advance.

This is problematic when:

  • Following STAC links where the target type is unknown
  • Exploring static STAC catalogs
  • Working with STAC resources from various sources
  • Building generic STAC browsers/explorers

Current Behavior

You must know the resource type and use the specific hook:

// Must know it's a collection
const { collection } = useCollection('collection-id');

// Must know it's an item  
const { item } = useItem(itemUrl);

// No way to fetch "whatever is at this URL"

Desired Behavior

import { useStacValue } from '@developmentseed/stac-react';

function StacResourceViewer({ url }) {
  const { value, type, isLoading, error } = useStacValue(url);
  
  // value could be Catalog, Collection, Item, or ItemCollection
  // type indicates what was fetched: 'Catalog' | 'Collection' | 'Feature' | 'FeatureCollection'
  
  switch (type) {
    case 'Catalog':
      return <CatalogView catalog={value} />;
    case 'Collection':
      return <CollectionView collection={value} />;
    case 'Feature':
      return <ItemView item={value} />;
    case 'FeatureCollection':
      return <ItemCollectionView items={value} />;
  }
}

Use Cases from stac-map

stac-map has a useStacValue hook that:

const { value, error, items, table } = useStacValue({
  href: 'https://any-stac-url.com/resource.json',
  fileUpload,
  datetimeBounds,
  stacGeoparquetItemId,
});

// value is automatically typed based on what was fetched
// Works with URLs, file uploads, and geoparquet

Proposed Solution

Hook Signature
type StacValueType = 'Catalog' | 'Collection' | 'Feature' | 'FeatureCollection';

type StacValue = StacCatalog | StacCollection | StacItem | StacItemCollection;

type UseStacValueResult = {
  value?: StacValue;
  type?: StacValueType;
  isLoading: boolean;
  isFetching: boolean;
  error?: ApiErrorType;
  refetch: () => Promise<void>;
};

function useStacValue(
  url: string,
  options?: {
    headers?: Record<string, string>;
    method?: 'GET' | 'POST';
    body?: unknown;
  }
): UseStacValueResult;
Implementation
import { useQuery } from '@tanstack/react-query';

function useStacValue(url: string, options = {}): UseStacValueResult {
  const { data, error, isLoading, isFetching, refetch } = useQuery({
    queryKey: ['stac-value', url, options],
    queryFn: async () => {
      const response = await fetch(url, {
        method: options.method || 'GET',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json',
          ...options.headers,
        },
        body: options.body ? JSON.stringify(options.body) : undefined,
      });

      if (!response.ok) {
        throw new ApiError(
          response.statusText,
          response.status,
          await response.text(),
          response.url
        );
      }

      const json = await response.json();
      return {
        value: json,
        type: determineStacType(json),
      };
    },
    enabled: !!url,
    retry: false,
  });

  return {
    value: data?.value,
    type: data?.type,
    isLoading,
    isFetching,
    error,
    refetch,
  };
}

function determineStacType(json: any): StacValueType {
  if (json.type === 'Feature') return 'Feature';
  if (json.type === 'FeatureCollection') return 'FeatureCollection';
  if (json.type === 'Collection') return 'Collection';
  if (json.type === 'Catalog') return 'Catalog';
  
  // Fallback: try to infer from structure
  if (json.extent && json.license) return 'Collection';
  if (json.links && !json.features) return 'Catalog';
  
  throw new Error('Unable to determine STAC type from resource');
}

Advanced Features

Type Guards
export function isStacCatalog(value: StacValue): value is StacCatalog {
  return value.type === 'Catalog';
}

export function isStacCollection(value: StacValue): value is StacCollection {
  return value.type === 'Collection';
}

export function isStacItem(value: StacValue): value is StacItem {
  return value.type === 'Feature';
}

export function isStacItemCollection(value: StacValue): value is StacItemCollection {
  return value.type === 'FeatureCollection';
}
Link Resolution

Optionally resolve relative hrefs to absolute:

function makeHrefsAbsolute(value: StacValue, baseUrl: string): StacValue {
  if (!value.links) return value;
  
  return {
    ...value,
    links: value.links.map(link => ({
      ...link,
      href: new URL(link.href, baseUrl).toString(),
    })),
  };
}

Benefits

  • ✅ Generic STAC resource fetching
  • ✅ Type-safe handling of different STAC types
  • ✅ Simplifies link following in STAC catalogs
  • ✅ Enables building generic STAC explorers
  • ✅ Matches patterns used in other STAC tools

Breaking Changes

None - this is new functionality.

Related Issues

  • #34 - Add Standalone Hooks (Context-Free Mode)

Testing Requirements

  • Test with STAC catalogs
  • Test with STAC collections
  • Test with STAC items
  • Test with item collections
  • Test type determination logic
  • Test error handling for invalid STAC
  • Test with relative and absolute hrefs

Documentation Requirements

  • Document the useStacValue hook
  • Provide examples for each STAC type
  • Document type guards and utilities
  • Show link following patterns
Dominant language
TypeScript
Stars
34
Forks
4
PR merge metrics
No merged PRs in 30d

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from developmentseed/stac-react

All issues in developmentseed/stac-react

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.