convex-react-query
Instead of polling you subscribe to receive update from server-side query
functions in a Convex deployment. Convex is a database with server-side
(db-side? like stored procedures) functions that update reactively.
New results for all relevant subscriptions are pushed to the client where they
update at the same time so data is never stale and there's no need to call
queryClient.invalidateQueries()
.
Setup
See ./src/example.tsx for a real example. The general
pattern:
- Create a ConvexClient and ConvexQueryClient. Set the global default
queryKeyHashFn
to convexQueryClient.hashFn()
and queryFn
to
convexQueryClient.queryFn()
. Connect the ConvexQueryClient to the React
Query QueryClient.
const convexClient = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL);
const convexQueryClient = new ConvexQueryClient(convexClient);
const queryClient = new QueryClient({
defaultOptions: {
queries: {
queryKeyHashFn: convexQueryClient.hashFn(),
queryFn: convexQueryClient.queryFn(),
},
},
});
convexQueryClient.connect(queryClient);
- Use
useQuery()
with the convexQuery
options factory function called with
an api
object imported from ../convex/_generated/server
and the arguments
for this query function. These two form the query key.
const { isPending, error, data } = useQuery({
...convexQuery(api.repos.get, { repo: "made/up" }),
gcTime: 10000,
});
staleTime
is set to Infinity
beacuse this data is never stale; it's
proactively updated whenever the query result updates on the server. (see
tkdodo's post
for more about this) If you like, customize the gcTime
to the length of time a
query subscription should remain active after all useQuery()
hooks using it
have unmounted.
Differences from using TanStack Query with fetch
New query results are pushed from the server, so a staleTime
of Infinity
should be used.
Your app will remain subscribed to a query until the gcTime
has elapsed. Tune
this for your app: it's usually a good tradeoff to use a value of at least a
couple seconds.
Example
To run this example:
Mutations
If you wrap your app in a ConvexProvider
you'll be able to use convex hooks
like useConvexMutation
:
<ConvexProvider client={convex}>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</ConvexProvider>
You can use this mutation function directly or wrap it in a TanStack Query
useMutation
:
const mutationFn = useConvexMutation(api.board.createColumn);
const { mutate } = useMutation({ mutationFn });
Authentication (example TODO)
TanStack Query isn't opionated about auth; an auth code might be a an element of
a query key like any other. With Convex it's not necessary to add an additional
key for an auth code; auth is an implicit argument to all Convex queries and
these queries will be retried when authentication info changes.
Convex auth is typically done via JWT: some query functions will fail if
requested before calling convexReactClinet.setAuth()
with a function that
provides the token.
Auth setup looks just like it's recommended in
Convex docs, which make use of components that
use native convex hooks. For Clerk, this might look like this: a ClerkProvider
for auth, a ConvexProviderWithClerk
for the convex client, and a
QueryClient
.
<ClerkProvider publishableKey="pk_test_...">
<ConvexProviderWithClerk client={convex} useAuth={useAuth}>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</ConvexProviderWithClerk>
</ClerkProvider>
See the Convex Auth docs for setup instructions.
TODO
- auth
- paginated queries
- cleanup / unsubscribe in useEffect; something with hot reloading may not be
working right
Contributing
After cloning this repo run npm i
to install dependencies. This package uses
tshy to publish an ESM. If there's ever demand
for a CJS build we can add "cjs" to the "dialects" section of "tshy" config in
the package.json.
To publish an alpha release, update the version in package.json to something
like 0.0.0-alpha.1
and run npm publish --tag alpha
.
To publish a regular release, update the version in package.json to something
like 0.1.2
and run npm publish
.