GraphQL with Next.js: Server Components and Route Handlers

Advanced
14 min

GraphQL with Next.js: Server Components and Route Handlers

Next.js blurs the line between client and server: data fetching moves into Server Components, mutations can run in Server Actions, and the API itself can live in the same project. After this lesson you will be able to query a GraphQL API from Server Components with caching, host a GraphQL endpoint in a Route Handler, and use Apollo Client only where interactivity requires it.

Fetching in Server Components

Server Components run on the server and can await data directly. A plain fetch to the GraphQL endpoint is all that is needed, and Next.js caching options apply to it like any other request:

tsx
// lib/gql.ts export async function gql<T>(query: string, variables?: Record<string, unknown>, revalidate = 60): Promise<T> { const res = await fetch(process.env.GRAPHQL_URL!, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ query, variables }), next: { revalidate }, }); const { data, errors } = await res.json(); if (errors?.length) throw new Error(errors[0].message); return data as T; } // app/posts/page.tsx export default async function PostsPage() { const { posts } = await gql<{ posts: { id: string; title: string }[] }>( `query Posts { posts(first: 20) { id title } }` ); return <ul>{posts.map((p) => <li key={p.id}>{p.title}</li>)}</ul>; }

next: { revalidate: 60 } caches the response for a minute; cache: "no-store" fetches on every request for personalised data. Nothing GraphQL-specific reaches the browser.

Hosting the API in a Route Handler

If the frontend and API belong to the same team, the GraphQL server can be a Route Handler. GraphQL Yoga works with the Web Request/Response types that Route Handlers use:

typescript
// app/api/graphql/route.ts import { createYoga, createSchema } from "graphql-yoga"; import { typeDefs, resolvers } from "@/graphql/schema"; const { handleRequest } = createYoga({ schema: createSchema({ typeDefs, resolvers }), graphqlEndpoint: "/api/graphql", fetchAPI: { Response }, }); export { handleRequest as GET, handleRequest as POST, handleRequest as OPTIONS };

Open /api/graphql in the browser to get GraphiQL. Apollo Server can be used the same way through startServerAndCreateNextHandler from @as-integrations/next. Route Handlers are stateless, so build DataLoaders in the context function and avoid the in-memory PubSub; subscriptions need a long-lived process.

Client Components with Apollo

Interactive parts such as a live search box still benefit from a client cache. The @apollo/client-integration-nextjs package wires Apollo Client into the App Router, including streaming SSR:

tsx
// app/ApolloWrapper.tsx "use client"; import { HttpLink } from "@apollo/client"; import { ApolloNextAppProvider, ApolloClient, InMemoryCache } from "@apollo/client-integration-nextjs"; function makeClient() { return new ApolloClient({ cache: new InMemoryCache(), link: new HttpLink({ uri: "/api/graphql" }) }); } export function ApolloWrapper({ children }: { children: React.ReactNode }) { return <ApolloNextAppProvider makeClient={makeClient}>{children}</ApolloNextAppProvider>; }

Wrap the relevant layout with ApolloWrapper, then use useQuery and useMutation from @apollo/client/react in Client Components exactly as in a plain React app. The package also exports registerApolloClient for Server Components that must use Apollo.

Mutations with Server Actions

A form submission does not need Apollo either. A Server Action can call the mutation with the same gql helper (using cache: "no-store"), then call revalidatePath("/posts") so the Server Component re-renders with fresh data. The browser never sees the token used to call the API.

| Need | Use | |---|---| | Render data on the server, SEO, cached pages | Server Component with fetch | | Form submission, then refresh the page | Server Action calling the mutation | | Live interactivity, optimistic UI, polling | Client Component with Apollo hooks |

Quick Quiz
Question 1 of 2

How do you cache a GraphQL response fetched in a Server Component for 60 seconds?

Key Takeaways

  • Server Components fetch GraphQL with plain fetch; next.revalidate and cache: "no-store" control caching.
  • A Route Handler can host the API with GraphQL Yoga or Apollo Server's Next.js integration; keep it stateless.
  • @apollo/client-integration-nextjs provides ApolloNextAppProvider for Client Components that need a cache.
  • Server Actions run mutations on the server and revalidatePath refreshes the page.
  • Reserve client-side GraphQL for genuinely interactive UI.

Next lesson: Testing Resolvers and Operations — unit-test resolver functions, execute operations against a test server, and mock the schema.

GraphQL with Next.js: Server Components and Route Handlers - GraphQL | CodeYourCraft | CodeYourCraft