#Next.js Prefetch Authenticated Queries

4 messages · Page 1 of 1 (latest)

old agate
#

Using Next.js and Clerk, how can I set up Next.js prefetching to be able to prefetch this query? I have a next/link in a server component pointing to this route but it doesn't seem to be prefetching.

// convex/customers.ts
export const listByUser = query({
  args: {},
  handler: async (ctx) => {
    const identity = await requireAuth(ctx);

    const customers = await ctx.db
      .query("customers")
      .withIndex("userId", (q) => q.eq("userId", identity.userId as string))
      .order("desc")
      .collect();
    return customers;
  },
});
// app/customers/page.tsx
import Link from "next/link";
import { PlusCircle } from "lucide-react";
import { preloadQuery } from "convex/nextjs";
import { api } from "@/convex/_generated/api";
import { Button } from "@/components/ui/button";
import { CustomersTable } from "@/components/customers/customers-table";
import { TableSkeleton } from "@/components/skeletons/table-skeleton";
import { getAuthToken } from "@/lib/auth";

export default async function Page() {
  const token = await getAuthToken();
  const preloadedCustomers = await preloadQuery(
    api.customers.listByUser,
    {},
    { token },
  );

  return (
    <div className="p-5">
      <div className="mb-6 flex items-center justify-between">
        <h1 className="text-3xl font-bold tracking-tight">Customers</h1>
        <Button asChild>
          <Link href="/customers/new">
            <PlusCircle className="mr-2 h-4 w-4" />
            Add Customer
          </Link>
        </Button>
      </div>
      {preloadedCustomers === undefined ? (
        <TableSkeleton />
      ) : (
        <CustomersTable preloadedCustomers={preloadedCustomers} />
      )}
    </div>
  );
}
dim patrolBOT
#

Hi @old agate. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

To prefetch authenticated Convex queries in a Next.js app using Clerk, you need to ensure that:

  1. You pass the Clerk JWT token to preloadQuery in your server component.
  2. Your getAuthToken implementation correctly retrieves the Clerk JWT token using the "convex" template.
  3. The route you want to prefetch is actually being prefetched by Next.js (note: Next.js only prefetches static routes by default, and dynamic data prefetching requires additional setup).

1. Passing the Clerk JWT Token

Your code is correct in that you are retrieving the token and passing it to preloadQuery:

const token = await getAuthToken();
const preloadedCustomers = await preloadQuery(
  api.customers.listByUser,
  {},
  { token },
);

The recommended way to implement getAuthToken with Clerk is:

import { auth } from "@clerk/nextjs/server";

export async function getAuthToken() {
  return (await (await auth()).getToken({ template: "convex" })) ?? undefined;
}

This ensures that the Convex query is authenticated on the server side, as required for protected queries like yours that use requireAuth(ctx) in the handler.
See: Server-side authentication

2. Next.js Prefetching Behavior

  • Next.js <Link prefetch /> only prefetches the JavaScript and static data for the linked page. It does not execute server-side code (like your preloadQuery) until the user actually navigates to the page.
  • If you want to prefetch Convex data before navigation, you would need to implement a custom client-side prefetching strategy (e.g., using a hover event to trigger a client-side fetch and cache).
#

In summary:
Your server component code is correct for fetching authenticated data on page load. However, Next.js's built-in prefetching for <Link /> does not trigger server-side data fetching (including preloadQuery) ahead of navigation. The data will only be fetched when the user navigates to /customers.

If you need true data prefetching before navigation, you would need to implement a custom solution, as this is not handled by Next.js's default prefetching mechanism. This limitation is not specific to Convex or Clerk, but to how Next.js handles route prefetching.

References:

If you have further questions about custom prefetching strategies, let me know!

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: