@globus/react-query
v1.4.1
Published
@globus/sdk + TanStack Query
Keywords
Readme
@globus/react-query
React Query hooks and helpers for interacting with the Globus platform.
Features
- Built using
@globus/sdkand@tanstack/react-query - Provides
queryKeyandqueryFnfor@globus/sdkservice methods - Type-safe hooks and helpers generated for each service method
- Supports both queries and mutations
- Handles authorization state automatically in hooks (via
@globus/react-auth-context)
Installation
npm install @globus/react-query
# or
yarn add @globus/react-queryUsage
The methods exposed by @globus/react-query are organized by service, similar to @globus/sdk. Each method has a generated hook for queries (e.g. useQuery) and mutations (e.g. useMutation), as well as helper functions for reusing queryOptions with other React Query hooks.
Queries (<resource>.<method>.useQuery)
For @globus/sdk methods that perform queries (e.g. getAll, get), you can use the generated query hook.
import { streams } from "@globus/react-query";
function ExampleComponent() {
const { data, isLoading, error } = streams.tunnels.getAll.useQuery();
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<ul>
{data.data.map((tunnel) => (
<li key={tunnel.id}>{tunnel.attributes.label}</li>
))}
</ul>
);
}Mutations (<resource>.<method>.useMutation)
For @globus/sdk methods that perform mutations (e.g. create, update, delete), you can use the generated mutation hook.
import { streams } from "@globus/react-query";
function CreateTunnelButton() {
const createTunnelMutation = streams.tunnels.create.useMutation({
onSuccess: (data) => {
console.log("Tunnel created:", data);
},
onError: (error) => {
console.error("Error creating tunnel:", error);
},
});
const handleCreateTunnel = () => {
createTunnelMutation.mutate(
{ request: { data: /* tunnel creation data */ } }
);
};
return (
<button onClick={handleCreateTunnel} disabled={createTunnelMutation.isPending}>
{createTunnelMutation.isPending ? "Creating..." : "Create Tunnel"}
</button>
);
}Helpers (<resource>.<method>.queryOptions)
The queryOptions helper provides the same queryKey and a queryFn as the generated hooks, but without the hook itself. This is useful for cases where you want to use other React Query hooks like useInfiniteQuery or useQueries.
Since this is not a hook, it will not automatically include the authorization manager or state that is normally provided by useGlobusAuth. If your usage requires authorization, you will need to manually include the manager in the payload when calling queryOptions (as seen below).
import { streams } from "@globus/react-query";
function ExampleComponent() {
const auth = useGlobusAuth();
const queryOptions = streams.tunnels.getAll.queryOptions({
manager: auth?.authorization,
});
const { data, isLoading, error } = useQuery(queryOptions);
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<ul>
{data.data.map((tunnel) => (
<li key={tunnel.id}>{tunnel.attributes.label}</li>
))}
</ul>
);
}Contributing + Implementation Details
@globus/sdkmethods should be wrapped using the factory functions in (src/factory.ts) to provide type-safe hooks and helpers.
/**
* Example wrapping a `@globus/sdk` service.
*/
import { tunnel } from "@globus/sdk/services/transfer/index";
import { createHooksAndHelpersForService } from "../factory";
export const tunnels = createHooksAndHelpersForService(
["streams", "tunnels"],
tunnel.next,
);
/**
* Example wrapping a single method from a `@globus/sdk` service, with a custom `method` implementation.
* You shouldn't need to do this, but it is possible if you need to customize the behavior of the `method` (e.g. to support a legacy method signature that doesn't match the expected `method` signature in the factory).
*/
import { streamAccessPoint } from "@globus/sdk/services/transfer/index";
import { createHooksAndHelpersForMethod } from "../factory";
export const accessPoints = {
getAll: createHooksAndHelpersForMethod({
name: "getAll",
keyPrefix: ["streams", "access-points"],
/**
* In this particular case, we're using "legacy" method signature as the `method`.
*/
method: (payload) => {
return streamAccessPoint.getAll({
query: payload?.request?.query,
manager: payload?.requst?.manager || payload?.options?.manager,
});
},
}),
};