Graphql Client
Next-gql provides a wrapper around urql.
Define client query
// @/client/gql/queries-mutationsimport { gql } from '@urql/core';
export const MY_TASKS = gql` query MyTasks { me { id tasks { id } } }`;
Setup cache for query objects.
Normalized cache is worth a read to understand why we define createTask and deleteTask mutation functions. TLDR: automatically add and remove entities from the cache. from the cache.
/* eslint-disable @typescript-eslint/no-explicit-any */'use client';
import { type MyTasksQuery, type Task } from '@/client/gql/generated/graphql';import schema from '@/client/gql/generated/schema.json';import { MY_TASKS } from '@/client/gql/queries-mutations';import { createCacheExchange, type Cache, type CacheExchangeOptions,} from '@enalmada/next-gql/client/urql/cacheExchange';
const userDefinedConfig: CacheExchangeOptions = { schema, updates: { Mutation: { createTask(result: { createTask: Task }, _args: any, cache: Cache) { cache.updateQuery({ query: MY_TASKS }, (data: MyTasksQuery | null) => { if (result && data?.me?.tasks) { const updatedTasks = [...data.me.tasks, result.createTask]; return { ...data, me: { ...data.me, tasks: updatedTasks } }; } return data; }); }, deleteTask(_result: any, args: { id: string }, cache: Cache) { cache.updateQuery({ query: MY_TASKS }, (data: MyTasksQuery | null) => { if (data?.me?.tasks) { const updatedTasks = data.me.tasks.filter((task) => task.id !== args.id); return { ...data, me: { ...data.me, tasks: updatedTasks } }; } return data; }); }, }, },};
export const cacheExchange = createCacheExchange(userDefinedConfig);
Setup provider in layout (or any wrapper providing cookies, isLoggedIn status)
import React from 'react';import { cookies } from 'next/headers';import { UrqlWrapper as NextGqlProvider } from '@enalmada/next-gql/client/urql/UrqlWrapper';import { cacheExchange } from '@/client/gql/cacheExchange';
export async function Layout({ children }: { children: React.ReactNode }) { const cookie = cookies().toString(); const url = process.env.URL + '/api/graphql';
return ( <NextGqlProvider url={url} isLoggedIn={boolean} cookie={cookie} cacheExchange={cacheExchange} > {children} </NextGqlProvider> );}
use queries
import { useQuery } from '@enalmada/next-gql';import { type MyTasksQuery, type Task } from '@/client/gql/generated/graphql';import { MY_TASKS } from '@/client/gql/queries-mutations';
const [{ data, error }] = useQuery<MyTasksQuery>({ query: MY_TASKS });
Notes:
- types are kept in sync with codegen
- it is important to have a root
loading.tsx
which provides a final fallback suspense. Without this, infinite querying can happen.