Skip to content

feat(stac-api): accept callable options resolved at call time#56

Closed
alukach wants to merge 4 commits into
mainfrom
feat/callable-options
Closed

feat(stac-api): accept callable options resolved at call time#56
alukach wants to merge 4 commits into
mainfrom
feat/callable-options

Conversation

@alukach
Copy link
Copy Markdown
Member

@alukach alukach commented May 14, 2026

What I'm changing

The PR enables dynamic options to accompany requests to a STAC API. My current use case is to pass in a callable options parameter that fetches the latest authorization header (developmentseed/stac-manager#71), which allows us to maintain the same STAC API instance while the underlying auth token is refreshed within my application.

How I did it

StacApiProvider/useStacApi/StacApi now accept options as either the existing static GenericObject (backward compatible) or a function of type OptionsGetter invoked per request. The function form lets consumers rotate values (auth headers, custom headers, signal, etc.) without rebuilding the StacApi instance and re-firing the landing-page probe.

Internally, useStacApi ref-stabilizes whatever options form was passed and excludes it from the react-query queryKey, so identity changes across renders no longer cause client rebuilds. Static options no longer need to be memoized.

Note

It wasn't really clear to me why options were ever part of the queryKey. It's worth considering if they need to be there for any reason that I'm not seeing.

The constructor signature stays positional (baseUrl, searchMode, options?) — the only change to the existing API surface is widening the options type to GenericObject | OptionsGetter.

alukach and others added 2 commits May 5, 2026 10:03
`StacApiProvider`/`useStacApi`/`StacApi` now accept `options` as either
the existing static `GenericObject` (backward compatible) or a function
of type `OptionsGetter` invoked per request. The function form lets
consumers rotate values (auth headers, custom headers, signal, etc.)
without rebuilding the StacApi instance and re-firing the landing-page
probe.

Internally, `useStacApi` ref-stabilizes whatever options form was
passed and excludes it from the react-query queryKey, so identity
changes across renders no longer cause client rebuilds. Static options
no longer need to be memoized.

The library takes no opinions on auth scheme or URL scoping — `options`
applies to every request `StacApi.fetch` issues. Consumers needing
secret-scoping (e.g. don't send a bearer to asset hrefs) should gate at
their call sites.

The constructor signature stays positional `(baseUrl, searchMode,
options?)` — the only change to the existing API surface is widening
the `options` type to `GenericObject | OptionsGetter`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@alukach alukach marked this pull request as ready for review May 19, 2026 16:33
@alukach alukach requested review from AliceR and danielfdsilva and removed request for AliceR May 19, 2026 16:33
@alukach
Copy link
Copy Markdown
Member Author

alukach commented May 20, 2026

/cc @pantierra

@danielfdsilva
Copy link
Copy Markdown
Member

I think I'm not fully grasping the goal here. Passing a callable options that gets executed every time seem excessive.
I agree that the options shouldn't be part of the query key (unless anything there may affect the response, but I don't think it is the case).

IMO it would make more sense for the query to return the search mode and url and the stac api be constructed outside. Then if the options change the stac api is updated and there's no need of a new request. Am I missing something really big here?

Something like:

function useStacApi(url: string, options?: GenericObject): StacApiHook {
  const { data, isSuccess, isLoading, isError } = useQuery({
    queryKey: generateStacApiQueryKey(url),
    queryFn: async () => {
      const response = await fetch(url, {
        headers: {
          ...options?.headers,
        },
      });
      const stacData = await handleStacResponse<{ links?: Link[] }>(response);

      const doesPost = stacData.links?.find(
        ({ rel, method }: Link) => rel === 'search' && method === 'POST'
      );

      return {
        mode: doesPost ? SearchMode.POST : SearchMode.GET,
        url: response.url
      }
    },
    staleTime: Infinity,
  });


  return useMemo(() => {
    if (isSuccess) {
      return {
        stacApi: new StacApi(data.url, data.mode, options),
        isLoading,
        isError
      }
    }
    
    return {
      stacApi: undefined,
      isLoading,
      isError
    }

  }, [data, isSuccess, isLoading, isError, options])
}

If the headers change the query won't run again. We need to make sure that's ok.

@alukach
Copy link
Copy Markdown
Member Author

alukach commented May 21, 2026

@danielfdsilva Good suggestion. Closing in favor of #57

@alukach alukach closed this May 21, 2026
@alukach alukach deleted the feat/callable-options branch May 21, 2026 21:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants