Files
markone/packages/web/src/api.ts

56 lines
1.7 KiB
TypeScript
Raw Normal View History

2025-05-07 15:47:08 +01:00
import { type DefaultError, type UseMutationOptions, type queryOptions, useQuery } from "@tanstack/react-query"
import { useNavigate } from "@tanstack/react-router"
import { useEffect } from "react"
class BadRequestError extends Error {}
class InternalError extends Error {}
class UnauthenticatedError extends Error {}
type QueryKey = ["bookmarks", ...ReadonlyArray<unknown>]
async function fetchApi<TData>(route: string, init?: RequestInit): Promise<[TData | void, Response]> {
const response = await fetch(`${import.meta.env.VITE_API_URL}/api${route}`, {
...init,
credentials: "include",
})
switch (response.status) {
case 200:
return [await response.json(), response]
case 204:
return [undefined, response]
case 400:
throw new BadRequestError()
case 401:
throw new UnauthenticatedError()
default:
throw new InternalError()
}
}
function useAuthenticatedQuery<TData>(queryKey: QueryKey, fn: () => Promise<TData>) {
const query = useQuery({
queryKey,
queryFn: () => fn(),
retry: (_, error) => !(error instanceof UnauthenticatedError),
})
const navigate = useNavigate()
useEffect(() => {
if (query.error && query.error instanceof UnauthenticatedError) {
navigate({ to: "/login", replace: true })
}
}, [query.error, navigate])
return query
}
function mutationOptions<TData = unknown, TError = DefaultError, TVariables = void, TContext = unknown>(
options: UseMutationOptions<TData, TError, TVariables, TContext>,
): UseMutationOptions<TData, TError, TVariables, TContext> {
return options
}
export { BadRequestError, InternalError, UnauthenticatedError, fetchApi, useAuthenticatedQuery, mutationOptions }
export type { QueryKey }