This package provides a TypeScript client for the ICAT+ API.
pnpm install @edata-portal/icat-plus-api
You'll need to install @tanstack/react-query
as a peer dependency if you haven't already:
pnpm install @tanstack/react-query
Before using the API, you need to configure it with the base URL of the ICAT+ server and the authentication token.
import { IcatPlusAPIContext } from '@edata-portal/icat-plus-api';
function MyApp() {
const queryClient = new QueryClient();
return (
<IcatPlusAPIContext.Provider
value={{
baseUrl: 'https://icatplus.esrf.fr/',
sessionId: 'xxx', // OPTIONAL: You can omit this if you want to use routes without authentication
sessionIdExpirationTime: 'xxx' // OPTIONAL: expiration time of the session ID - the data will be refetch when this value changes to ensure any data fetched after the expiration is refreshed
onError: console.error, // OPTIONAL: A function that will be called with the message when an error occurs during a fetch
onExpiredSessionId: logout, // OPTIONAL: A function that will be called when the session ID has expired and user needs to re-authenticate
}}
>
// Your components
</IcatPlusAPIContext.Provider>
);
}
This package provides ICAT+ endpoints definitions through objects exported as constants. You can find them grouped by module under src/api/endpoints/modules
directory. and import them like this:
import { DATASET_LIST_ENDPOINT } from '@edata-portal/icat-plus-api';
Then depending on the HTTP method associated with the endpoint, you can use one of the available hooks:
This hook allows to make your component dependant on a certain endpoint. It will fetch the data and return it. The component will be suspended while the data is being fetched.
const datasets = useGetEndpoint({
endpoint: DATASET_LIST_ENDPOINT,
params: {
investigationIds: '123',
nested: true,
},
// skipFetch: false, // add this param if you want to skip fetching the data under certain conditions
// default: [] as Dataset[], // add this param if you want to provide a default value in case the data could not be fetched or the fetch was skipped
});
This hook allows to create a callback function that will create a promise to fetch the data.
const fetchDatasets = useAsyncFetchEndpoint(DATASET_LIST_ENDPOINT);
const onBtnClick = () => {
fetchDatasets({
investigationIds: '123',
nested: true,
}).then((datasets) => {
console.log(datasets);
});
};
This hook allows to get the full URL to the data, including authentication token if necessary. This is useful for images or downloading files.
const datasetsUrl = useEndpointURL(DATASET_LIST_ENDPOINT);
const url = datasetsUrl({
investigationIds: '123',
nested: true,
});
This hook allows to make multiple calls to the same endpoint with different params. It differs from multiple useGetEndpoint
because the data will be fetch in parallel, while each useGetEndpoint
will only be called after the previous one is done.
const datasetsUrl = useEndpointURL(DATASET_LIST_ENDPOINT);
const [datasets123, datasets456] = useMultiGetEndpoint({
endpoint: DATASET_LIST_ENDPOINT,
params: [
{
investigationIds: '123',
},
{
investigationIds: '456',
},
],
});
This hook allows to create a callback function that will create a promise to send data to the server.
const createSession = useMutateEndpoint({
endpoint: SESSION_CREATE_ENDPOINT,
});
createSession
.mutateAsync({
body: {
plugin: 'db',
username: 'user',
password: 'password',
},
})
.then((newUser) => {
console.log(newUser);
});
All components using useGetEndpoint
and useMultiGetEndpoint
will be suspended while the data is being fetched.
You can use React's Suspense
component to display a fallback while the data is being fetched.
Additionally, this library provides a specialized ProgressSuspense
component that will inform you of the progress while downloading large requests. This can be used as a replacement for the default Suspense
component if you want to render a progress bar.
This component is only able to track the progress of the request if the server provides the Content-Length
header in the response. If the server does not provide this header, the progress will be indeterminate.
Important to note, the progress initiates only after the server begins sending data, meaning it won't move until the server completes processing the request and starts sending the response. Due to this behavior, ProgressSuspense
may not be suitable for requests with extended processing times and minimal data transfer, but it is well-suited for use cases involving long data transfer times, such as loading large files.
function renderProgress(v: number) {
return <progress max={1} value={v}/>;
}
export function MyComponent() {
return (
<ProgressSuspense render={renderProgress}>
<MySuspendedComponent>
</ProgressSuspense>
);
}