#User Query Best Practice
13 messages · Page 1 of 1 (latest)
Hi @summer herald. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
The optimal way to fetch user data in Convex—especially if you want seamless page switching without loading flashes—is to leverage query caching and avoid unnecessary re-fetches.
Best Practices:
-
Query User Data on Each Page, but Cache It:
- It’s standard to query the current user’s data on each page, but to avoid loading states and redundant network requests, you should use a caching helper. The convex-helpers package provides a
useQuerywrapper that keeps queries alive and cached even when you navigate away, so when you return, the data is instantly available and only updates if it has changed. This makes page transitions feel instant and smooth. - Downsides: The query stays subscribed and re-executes in the background, so if the data updates very frequently or you have many such queries, it could be less efficient. But for user data, which changes infrequently, this is usually a good tradeoff Discord: Optimise Fetching data.
- It’s standard to query the current user’s data on each page, but to avoid loading states and redundant network requests, you should use a caching helper. The convex-helpers package provides a
-
Use a Custom Hook to Combine Auth and User Existence:
- You can create a custom hook that checks both authentication state and whether the user document is loaded, to avoid rendering before the user data is ready. This prevents race conditions and ensures your UI only renders when the user data is available Convex Docs: Waiting for current user to be stored.
import { useConvexAuth, useQuery } from "convex/react";
import { api } from "../convex/_generated/api";
export function useCurrentUser() {
const { isLoading, isAuthenticated } = useConvexAuth();
const user = useQuery(api.users.current);
return {
isLoading: isLoading || (isAuthenticated && user === null),
isAuthenticated: isAuthenticated && user !== null,
};
}
- Always Fetch User Data in Backend Functions:
- For security, always fetch the user’s data in your backend functions using the authenticated user’s ID, rather than trusting client-passed IDs. This ensures you’re always working with the correct user context Discord: Supabase Auth and Convex.
Summary:
Query the user’s data on each page, but use a caching helper to keep the data alive between page switches. This provides a seamless experience without unnecessary loading states. For most apps, this is the optimal and most efficient approach. If your user data changes very frequently, consider the tradeoffs of keeping queries alive.
For more details and a video explanation, see Magic Caching and the convex-helpers query caching docs Discord: Optimise Fetching data.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@summer herald Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@cyan stone How about checking isAuthenticated server side
Let me check the knowledge base for you again. One moment! :books:
To check if a user is authenticated on the server side in Convex, you should always validate the authentication state within your Convex backend functions (queries, mutations, actions). This is done by calling ctx.auth.getUserIdentity() in your function handler. If the result is null, the user is not authenticated.
Here’s a typical pattern:
import { mutation } from "./_generated/server";
import { ConvexError } from "convex/values";
export const myMutation = mutation({
args: { /* ... */ },
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity();
if (identity === null) {
throw new ConvexError("Unauthenticated call to mutation");
}
// Proceed with authenticated logic...
},
});
This approach ensures that only authenticated users can access or modify data, regardless of any client-side checks. You can also use this identity to look up the user in your database and enforce further authorization (e.g., roles, permissions) as needed. This is the recommended and secure way to enforce authentication in Convex backend functions, and you should do this for all public-facing functions to protect your data and logic from unauthorized access Securing Your Convex Backend Convex Docs: Authorization.
If you are using Next.js, you can also enforce authentication at the middleware level to protect routes before they reach your server components, but you should still check authentication in your Convex functions for full security Ensuring Authentication in Server Components.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@summer herald Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@cyan stone How slow should the query be
Let me check the knowledge base for you again. One moment! :books:
Convex recommends that your queries (and mutations) should generally finish in less than 100ms and work with less than a few hundred records. This guideline helps ensure your app remains snappy and responsive. If your queries are consistently taking longer than this, it's a sign you may need to optimize your function logic, use indexes, or reduce the amount of data processed per request.
"Keep sync engine functions light & fast. In general, your mutations and queries should be working with less than a few hundred records and should aim to finish in less than 100ms. It's nearly impossible to maintain a snappy, responsive app if your synchronous transactions involve a lot more work than this."
The Zen of Convex