@nexus-state/query
v0.1.5
Published
Powerful data fetching and caching for Nexus State
Downloads
347
Maintainers
Readme
@nexus-state/query
Powerful data fetching and caching for Nexus State — with SSR prefetch, automatic caching, and optimistic updates
🚀 Quick Start (SSR Prefetch)
import { prefetchQuery } from '@nexus-state/query/react';
import { useQuery } from '@nexus-state/query/react';
// Server (Next.js getServerSideProps)
export async function getServerSideProps(context) {
await prefetchQuery({
queryKey: ['user', context.params.id],
queryFn: () => fetchUser(context.params.id),
});
return { props: {} };
}
// Client (data already cached!)
function Page() {
const { data: user } = useQuery({
queryKey: ['user', id],
queryFn: () => fetchUser(id),
});
return <div>{user.name}</div>;
}Like TanStack Query, but built on Nexus State's atomic architecture
🎯 Why Nexus State Query?
Comparison with Alternatives
| Feature | @nexus-state/query | TanStack Query | SWR | |---------|-------------------|----------------|-----| | Bundle size | 8KB | 13KB | 6KB | | SSR prefetch | ✅ Built-in | ⚠️ Complex | ⚠️ Complex | | Atomic integration | ✅ Shares atoms | ❌ Separate | ❌ Separate | | Fine-grained updates | ✅ Per-atom | ⚠️ Per-query | ⚠️ Per-key | | Multi-framework | ✅ React/Vue/Svelte | ⚠️ React-focused | ⚠️ React-focused | | DevTools | ✅ Redux DevTools | ✅ Own DevTools | ❌ |
✅ Choose Nexus State Query if you need:
- SSR prefetch with automatic hydration
- Automatic caching with configurable stale time
- Optimistic updates for mutations
- Integration with Nexus State atoms
- Multi-framework support (React, Vue, Svelte)
❌ Use alternatives if:
- React-only project → TanStack Query (more plugins)
- Simple fetch → @nexus-state/async (lighter, 2KB)
- Minimal bundle → SWR (6KB)
📦 Installation
npm install @nexus-state/queryFor React:
npm install @nexus-state/query @nexus-state/react📖 Core Features
Queries
import { useQuery, useQueries, useSuspenseQuery } from '@nexus-state/query/react';Mutations
import { useMutation } from '@nexus-state/query/react';Prefetch
import { prefetchQuery, usePrefetch } from '@nexus-state/query/react';Cache Management
import { getQueryData, setQueryData, invalidateQuery } from '@nexus-state/query/react';🔌 React API
useQuery
import { useQuery } from '@nexus-state/query/react';
function UserProfile({ userId }) {
const { data, isLoading, error, refetch } = useQuery({
queryKey: ['user', userId],
queryFn: async () => {
const res = await fetch(`/api/users/${userId}`);
return res.json();
},
staleTime: 5 * 60 * 1000, // 5 minutes
retry: 3,
});
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <div>{data.name}</div>;
}Options:
enabled— Enable/disable query (default:true)staleTime— Time in ms before data is staleretry— Number of retry attempts (default:3)useErrorBoundary— Throw errors to React Error Boundarysuspense— Enable React Suspense mode
Result:
data— The fetched dataisLoading— Initial loading stateisSuccess— Query succeededisError— Query failedisIdle— Query hasn't run yetisFetching— Currently fetchingerror— Error if failedrefetch()— Manually refetchstatus— 'idle' | 'loading' | 'success' | 'error'
useSuspenseQuery
import { useSuspenseQuery } from '@nexus-state/query/react';
function UserProfile({ userId }) {
const { data } = useSuspenseQuery({
queryKey: ['user', userId],
queryFn: () => fetchUser(userId),
});
return <div>{data.name}</div>;
}
// Wrap with Suspense
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<UserProfile userId={1} />
</Suspense>
);
}useMutation (with Optimistic Updates)
import { useMutation, useQueryClient } from '@nexus-state/query/react';
function AddTodo() {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: createTodo,
onMutate: async (newTodo) => {
// Cancel outgoing refetches
await queryClient.cancelQueries({ queryKey: 'todos' });
// Snapshot previous value
const previousTodos = queryClient.getQueryData('todos');
// Optimistically update
queryClient.setQueryData('todos', (old) => [...old, newTodo]);
return { previousTodos };
},
onError: (err, newTodo, context) => {
// Rollback on error
queryClient.setQueryData('todos', context.previousTodos);
},
onSettled: () => {
// Always refetch after mutation
queryClient.invalidateQueries({ queryKey: 'todos' });
},
});
return <button onClick={() => mutation.mutate({ text: 'New' })}>Add</button>;
}🌐 SSR Patterns
Next.js (getServerSideProps)
export async function getServerSideProps(context) {
await prefetchQuery({
queryKey: ['user', context.params.id],
queryFn: () => fetchUser(context.params.id),
});
return { props: {} };
}Remix (loaders)
export async function loader({ params }) {
await prefetchQuery({
queryKey: ['user', params.id],
queryFn: () => fetchUser(params.id),
});
return json({});
}Nuxt (asyncData)
<script setup>
export default {
async asyncData({ app }) {
await prefetchQuery({
queryKey: 'user',
queryFn: fetchUser,
});
}
}
</script>🔥 Prefetch Strategies
Manual Prefetch
import { usePrefetch, prefetchQuery } from '@nexus-state/query/react';
function Button() {
const prefetch = usePrefetch();
return (
<button
onMouseEnter={() => {
prefetchQuery({
queryKey: ['user'],
queryFn: fetchUser,
});
}}
>
Load User
</button>
);
}Prefetch on Hover (Custom Implementation)
import { prefetchQuery } from '@nexus-state/query/react';
function UserLink({ userId }) {
const handleMouseEnter = () => {
prefetchQuery({
queryKey: ['user', userId],
queryFn: () => fetchUser(userId),
});
};
return (
<a href={`/users/${userId}`} onMouseEnter={handleMouseEnter}>
{userId}
</a>
);
}Prefetch on Viewport (Custom Implementation)
import { useEffect, useRef } from 'react';
import { prefetchQuery } from '@nexus-state/query/react';
function Card({ itemId }) {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
prefetchQuery({
queryKey: ['item', itemId],
queryFn: () => fetchItem(itemId),
});
}
},
{ threshold: 0.5 }
);
if (ref.current) {
observer.observe(ref.current);
}
return () => observer.disconnect();
}, [itemId]);
return <div ref={ref}>...</div>;
}🔧 Cache Management
Get/Set Query Data
import { getQueryData, setQueryData } from '@nexus-state/query/react';
// Get cached data
const user = getQueryData(['user', id]);
// Update cached data
setQueryData(['user', id], (old) => ({
...old,
name: 'Updated',
}));Invalidate Queries
import { invalidateQuery } from '@nexus-state/query/react';
// Invalidate specific query
await invalidateQuery(['user', id]);
// Invalidate all queries
await invalidateQuery();📚 Async vs Query: When to Use Which?
| Scenario | @nexus-state/async | @nexus-state/query | |----------|-------------------|-------------------| | Simple fetch with loading | ✅ | ⚠️ Overkill | | SSR prefetch | ❌ | ✅ | | Automatic caching | ❌ | ✅ | | Background refetch | ❌ | ✅ | | Optimistic updates | ❌ | ✅ | | Mutations | ❌ | ✅ | | Bundle size | 2KB | 8KB |
Use @nexus-state/async when: You need basic async state without caching, prefetch, or SSR.
Use @nexus-state/query when: You need caching, SSR prefetch, mutations, or optimistic updates.
📖 @nexus-state/async docs: npm
🔗 Migration from TanStack Query
// TanStack Query
import { useQuery } from '@tanstack/react-query';
// @nexus-state/query (almost identical!)
import { useQuery } from '@nexus-state/query/react';
// Same API:
const { data, isLoading, error, refetch } = useQuery({
queryKey: ['user', id],
queryFn: () => fetchUser(id),
staleTime: 5 * 60 * 1000,
retry: 3,
});Key differences:
- Query client is optional (uses Nexus State store by default)
- Smaller bundle size (8KB vs 13KB)
- Built-in SSR prefetch utilities
- Multi-framework support (Vue, Svelte coming soon)
📦 Related Packages
| Package | Description | npm | |---------|-------------|-----| | @nexus-state/core | Core concepts | Install | | @nexus-state/react | React integration | Install | | @nexus-state/async | Simple async state | Install | | @nexus-state/persist | Persistence | Install |
🔗 See Also
- Core: @nexus-state/core — Foundation (atoms, stores)
- Framework integration:
- @nexus-state/react — React hooks
- @nexus-state/vue — Vue composables
- @nexus-state/svelte — Svelte stores
- Related:
- @nexus-state/async — Simple async state (lighter alternative)
- @nexus-state/persist — LocalStorage persistence
Full ecosystem: Nexus State Packages
📄 License
MIT
