@everystack/query
v0.2.0
Published
React Query hooks for PostgREST-compatible APIs
Readme
@everystack/query
React Query hooks for PostgREST-compatible APIs. Optional companion to @everystack/api — the client works standalone, this package adds React Query integration.
Install
pnpm add @everystack/query @tanstack/react-querySetup
import { createHooks } from '@everystack/query';
const { useResource, useResourceList, useMutation } = createHooks({
baseUrl: 'https://myapp.com/api',
headers: { 'X-Custom': 'value' },
getToken: () => localStorage.getItem('token'),
});Wrap your app with React Query's QueryClientProvider:
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<MyApp />
</QueryClientProvider>
);
}Hooks
useResource
Fetch a single resource by filter. Returns a React Query result.
function PostPage({ slug }: { slug: string }) {
const { data, isLoading, error } = useResource('posts', {
slug: `eq.${slug}`,
});
if (isLoading) return <Text>Loading...</Text>;
if (error) return <Text>Error: {error.message}</Text>;
return <Text>{data.title}</Text>;
}The filters parameter uses PostgREST syntax — each key is a column, each value is operator.value:
useResource('users', { id: 'eq.123' });
useResource('posts', { status: 'eq.published', authorId: 'eq.abc' });Conditional fetching with enabled:
const { data } = useResource('profiles', { userId: `eq.${userId}` }, {
enabled: !!userId,
});useResourceList
Fetch a list of resources with filters, ordering, pagination, and column selection.
function PostList() {
const { data, isLoading } = useResourceList('posts', {
filters: { status: 'eq.published' },
order: 'createdAt.desc',
limit: 20,
offset: 0,
select: 'id,title,author(name)',
});
return data?.map(post => <PostCard key={post.id} post={post} />);
}useMutation
Create, update, or delete resources. Returns a React Query mutation result.
function CreatePost() {
const { mutateAsync, isPending } = useMutation('posts', 'POST');
async function handleSubmit(values: PostForm) {
const result = await mutateAsync({
title: values.title,
content: values.content,
});
// result is the created record
}
return <Button onPress={handleSubmit} disabled={isPending}>Create</Button>;
}// Update
const updatePost = useMutation('posts', 'PATCH');
await updatePost.mutateAsync({ title: 'Updated' });
// Delete
const deletePost = useMutation('posts', 'DELETE');
await deletePost.mutateAsync({ id: '123' });Query Keys
Query keys are automatically derived from the table name and parameters, enabling automatic cache invalidation:
useResource('posts', { id: 'eq.1' })→['posts', { id: 'eq.1' }]useResourceList('posts', { limit: 20 })→['posts', 'list', { limit: 20 }]
Invalidate with React Query's queryClient:
queryClient.invalidateQueries({ queryKey: ['posts'] });Peer Dependencies
| Package | Version |
|---------|---------|
| @tanstack/react-query | >=5.0.0 |
Part of everystack — a self-hosted application stack for Expo apps on AWS.
License
MIT
