### **PR Type** Enhancement ___ ### **Description** - Introduce React Apollo & React Query example projects - Configure GraphQL codegen and generate typed hooks - Implement `AuthProvider` for Nhost session management - Add pages (Home, SignIn, SignUp, Profile) and components ___ ### Diagram Walkthrough ```mermaid flowchart LR CG["Codegen Config"] -- generates --> GT["GraphQL Types & Hooks"] AP["AuthProvider"] -- provides --> APP["App (Router)"] GC["Apollo/Query Client"] -- used by --> APP APP -- routes --> Home["Home Page"] Home -- queries/mutations --> GT ``` <details> <summary><h3> File Walkthrough</h3></summary> <table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Dependencies</strong></td><td><details><summary>2 files</summary><table> <tr> <td><strong>graphql.ts</strong><dd><code>Add generated GraphQL types and operations</code> </dd></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-f0ffc6fbc739aa455cf3967c263549e2acf4922dd63d9d2fd33ef9975d958b47">+6994/-0</a></td> </tr> <tr> <td><strong>graphql.ts</strong><dd><code>Add generated GraphQL types and operations</code> </dd></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-08b9f52995757f95a31a6d4d7bafb1f1a473ae338f9d11f343956048d13a0b58">+6944/-0</a></td> </tr> </table></details></td></tr><tr><td><strong>Configuration changes</strong></td><td><details><summary>4 files</summary><table> <tr> <td><strong>apolloClient.ts</strong><dd><code>Setup Apollo client with Nhost auth link</code> </dd></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-f0211d31ead8e27f200356bf615f34eee1e96ec6b068039a36b1506d32c35692">+53/-0</a> </td> </tr> <tr> <td><strong>queryHooks.ts</strong><dd><code>Create React Query authenticated fetcher</code> </dd></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-45c18c55c9756b413c5d4b9a71bd04a53d78706eb9a2a611f2fd0a1ab7ce6b51">+33/-0</a> </td> </tr> <tr> <td><strong>vite.config.ts</strong><dd><code>Add Vite config for React Apollo example</code> </dd></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-146d3d4bcf230a225998f3c68de6ffa9f19e16b85bb5ca882608d76e7b086566">+7/-0</a> </td> </tr> <tr> <td><strong>vite.config.ts</strong><dd><code>Add Vite config for React Query example</code> </dd></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-22a14bf686832a47057d73f552546a1d56a1f34cd34666acf741940feca0605f">+7/-0</a> </td> </tr> </table></details></td></tr><tr><td><strong>Enhancement</strong></td><td><details><summary>14 files</summary><table> <tr> <td><strong>AuthProvider.tsx</strong><dd><code>Implement Nhost AuthProvider for React Apollo</code> </dd></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-71a439d2114fc41855bdb2041134bbc4de64f886f06f3f50ed2fd31cc61c84a5">+175/-0</a> </td> </tr> <tr> <td><strong>AuthProvider.tsx</strong><dd><code>Implement Nhost AuthProvider for React Query</code> </dd></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-34697170eae721899cacb8faca334b60793b405b3f2ee327fecd4d6944338ed1">+175/-0</a> </td> </tr> <tr> <td><strong>Home.tsx</strong><dd><code>Add Home page with Apollo queries</code> </dd></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-407aac78333f6d47a86500b3e0c2311f1967ce50ba1a7ef4f022b8a50c013160">+203/-0</a> </td> </tr> <tr> <td><strong>Home.tsx</strong><dd><code>Add Home page with React Query hooks</code> </dd></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-c91862e4a735090baf145ed64f347a460446a25b8d5061b6b928b88ddb146008">+204/-0</a> </td> </tr> <tr> <td><strong>SignUp.tsx</strong><dd><code>Add SignUp page and form</code> </dd></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-9a841d153068ba2ba45e3b00c3979eca2048037670945ff079bce6645780f447">+127/-0</a> </td> </tr> <tr> <td><strong>SignUp.tsx</strong><dd><code>Add SignUp page and form</code> </dd></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-53b2663ba20acd8379c5dbbb671dd0c1badbb24955c7b9b7d9115843bd9ea859">+127/-0</a> </td> </tr> <tr> <td><strong>SignIn.tsx</strong><dd><code>Add SignIn page and form</code> </dd></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-fd0d7f5b422c5c3ec70fdeb95da10479b90132a571de56307d9b44fc69aaf27b">+120/-0</a> </td> </tr> <tr> <td><strong>SignIn.tsx</strong><dd><code>Add SignIn page and form</code> </dd></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-ec047013e51fbbbc17b641287e763558c5182325be0fe86d44e439eeca6e9ad1">+120/-0</a> </td> </tr> <tr> <td><strong>Profile.tsx</strong><dd><code>Add Profile page display</code> </dd></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-53e4cf70b4b84819e216a602241d5379b9402a2412af122b5a870ec0bc8cd6a1">+66/-0</a> </td> </tr> <tr> <td><strong>Profile.tsx</strong><dd><code>Add Profile page display</code> </dd></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-13fbb13999bb0001387c3d9a5015974ffece9575c1e6240a21157a07090b3374">+66/-0</a> </td> </tr> <tr> <td><strong>Navigation.tsx</strong><dd><code>Add navigation bar component</code> </dd></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-eb031d8883524edeab44c5b665a2429cffb1bac75d8f0dc1dc7bc3c42aa9c068">+85/-0</a> </td> </tr> <tr> <td><strong>Navigation.tsx</strong><dd><code>Add navigation bar component</code> </dd></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-3d34860dbf6645315068dd62f3be52745294be2729e4dc718e40ad0125669204">+85/-0</a> </td> </tr> <tr> <td><strong>ProtectedRoute.tsx</strong><dd><code>Add protected route wrapper</code> </dd></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-55c80a4fab944fa1c3210cce0fd0092776a01046ee9893c88c69d0c5f5f5458b">+26/-0</a> </td> </tr> <tr> <td><strong>ProtectedRoute.tsx</strong><dd><code>Add protected route wrapper</code> </dd></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-bfc4913eb77b8cbe8115f7833bfdf1aad595496f3160dbf43eabf829f0d94f5a">+26/-0</a> </td> </tr> </table></details></td></tr><tr><td><strong>Additional files</strong></td><td><details><summary>39 files</summary><table> <tr> <td><strong>examples_guides_checks.yaml</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-56c96ece925a38bd3e84d3fff001760c68d40744013d2b3458ad445a686a0c36">+94/-0</a> </td> </tr> <tr> <td><strong>project.nix</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-f6086e59795d16f4c73cb0e5f90b986e288deb5f176c80c5c035f1703ff86760">+1/-3</a> </td> </tr> <tr> <td><strong>Makefile</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-85a3083c78e211e9eb36d741342bcbc85a1a0c375060f45c5426b560196de27f">+17/-0</a> </td> </tr> <tr> <td><strong>package.json</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-4095856b4a3f808fcab300063ba57b9e84d2cd2b80e244b69d48d13ddadf161d">+13/-0</a> </td> </tr> <tr> <td><strong>project.nix</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-65c8830b4c3074e59267275284f270f4dfa95ce2722d7634e3e3c77f66f8235e">+105/-0</a> </td> </tr> <tr> <td><strong>README.md</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-b79af2df973af3966472409744a06f7e930008c43216cee8ea91af504bf569fe">+391/-0</a> </td> </tr> <tr> <td><strong>biome.json</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-bee8a9ce71bf1a5641c7bc5aa57f0ca5b4ab5e6a1fc0c08067cb02caa48048a9">+7/-0</a> </td> </tr> <tr> <td><strong>codegen-wrapper.sh</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-3d8141868656cb3c65352257558703f27b1f73b417f8a3223a595d31ffda1b4a">+27/-0</a> </td> </tr> <tr> <td><strong>codegen.ts</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-88f3c353c9bfd474e259a4e6a70b4425032b1d6b3c228046c36333b22bdb027e">+44/-0</a> </td> </tr> <tr> <td><strong>index.html</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-e26ca0f00bc4456ef2ec04d470a864c72252bff2a9d34aa068bd6fcee9bd837c">+13/-0</a> </td> </tr> <tr> <td><strong>package.json</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-219f91ff32e8ae8ff4fb20d06f91c0d4501d50dcaf34a4012aa0829c61eca41b">+36/-0</a> </td> </tr> <tr> <td><strong>schema.graphql</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-1aef57e8c82d9f02e7c40f665ffbc384489beff7b299a8d30f1b2a00ba54676b">+10143/-0</a></td> </tr> <tr> <td><strong>App.tsx</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-3939716d6e04d989c77f1b141c7893b8cad49325d77146df613ea763da566029">+56/-0</a> </td> </tr> <tr> <td><strong>index.css</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-42378718fb21f3858c4d3ced50bff70946a0a63d6f5afabf5d29e01b9d7e58cb">+552/-0</a> </td> </tr> <tr> <td><strong>queries.graphql</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-a72744935ef245ce03e31a475ece30582d3fa9d8295a1338d2fdf3d64f52875a">+28/-0</a> </td> </tr> <tr> <td><strong>main.tsx</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-fe10a290150d971b54595c18a0e3b9486ddd237611225ec4fae333c893403a96">+33/-0</a> </td> </tr> <tr> <td><strong>Home.css</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-34b39976cc1741190f571fe587e77290bfec3f7248b8dab7cfe2ef3832f39ad0">+217/-0</a> </td> </tr> <tr> <td><strong>vite-env.d.ts</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-76ec290ea25ad1dd3d6a40aceb23964e6af759e9d223d0227ea7a9b4c27a4c1a">+11/-0</a> </td> </tr> <tr> <td><strong>tsconfig.json</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-1c4ff207f3f497bf6eb395d765c67c43197a8141faf8e0b78975749d7ef8de7b">+6/-0</a> </td> </tr> <tr> <td><strong>tsconfig.node.json</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-2c47cac1784e69cc8ee9de2b8654939b7699e627e6996135af736d086471cbda">+4/-0</a> </td> </tr> <tr> <td><strong>README.md</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-f8cffe1a6c5ab9062008bdc81cbdbc19ff73c414e852e6cc630288c935cb85c5">+493/-0</a> </td> </tr> <tr> <td><strong>biome.json</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-f3c8d8d56e583f7496332f58d85b8f2fc179364af6d7b6db2050cf8f3019449d">+7/-0</a> </td> </tr> <tr> <td><strong>codegen-wrapper.sh</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-697fec800dc8ea79d58ac3650f21d252340f04c760df18c060c24523a7324967">+31/-0</a> </td> </tr> <tr> <td><strong>codegen.ts</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-9b2b4b1be5005479ea56b7dbb26f8f34e9b39ab005c4df5e5547caef4bb8176e">+52/-0</a> </td> </tr> <tr> <td><strong>index.html</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-6a4d60e607c923818133e58b28b06f9b749a492a500a6b2899e1f2fd991ad03b">+13/-0</a> </td> </tr> <tr> <td><strong>package.json</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-e80d8d01acf5048bc7c1767cf248b07e67df6a13914ac02d43c8b180b0178ddc">+35/-0</a> </td> </tr> <tr> <td><strong>schema.graphql</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-938b6a319b1d5f5234f3e80b49c3d9c429a45569fb777ef9310bdbdeb3f485e2">+10143/-0</a></td> </tr> <tr> <td><strong>App.tsx</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-9a06f558adc00bb736ae330d10f276dd360d3b2e08814d424392470d6b19af3b">+56/-0</a> </td> </tr> <tr> <td><strong>index.css</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-8fe80a812b88da1a00debcd607d5a7300a01558103403d6beba1c8a514d1cae0">+552/-0</a> </td> </tr> <tr> <td><strong>QueryProvider.tsx</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-5e3488d3b7deca24d0d26f4af88d201e7678662e6531f4e4cb511f99d32854d5">+27/-0</a> </td> </tr> <tr> <td><strong>queries.graphql</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-6506f25586ed4d14db727ca7ee5be4e299c29dbbfcdbcbc4f81e49a4ae9b5fb5">+28/-0</a> </td> </tr> <tr> <td><strong>main.tsx</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-2d90aa9771205fab3d46a5d28686eae47e929a9ec6070dc3d564d37d6324768f">+22/-0</a> </td> </tr> <tr> <td><strong>Home.css</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-ef3665d0c09e727cc06d1d429d983c3767f8b65add4bdbe5b095d3816ebbce1d">+217/-0</a> </td> </tr> <tr> <td><strong>vite-env.d.ts</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-c931c9d2529208b5808e22f0a18c44b7cff5e88d1592b8f472f46bcdeda844b5">+11/-0</a> </td> </tr> <tr> <td><strong>tsconfig.json</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-097ce507ecd096e978eb5013155ceb912fe10b0b6de8ee4a0f13ff08565e1cae">+6/-0</a> </td> </tr> <tr> <td><strong>tsconfig.node.json</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-12b373ca0bdd9a2bc826e660ccdccc9573bd0ef54d592f2ebce0473e9568dde8">+4/-0</a> </td> </tr> <tr> <td><strong>flake.nix</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-206b9ce276ab5971a2489d75eb1b12999d4bf3843b7988cbe8d687cfde61dea0">+7/-40</a> </td> </tr> <tr> <td><strong>js.nix</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-bdae1ae0f0031ce2c2e0356df4460e40363922c9658b25c773ebfe29fe052cf9">+4/-1</a> </td> </tr> <tr> <td><strong>pnpm-workspace.yaml</strong></td> <td><a href="https://github.com/nhost/nhost/pull/3460/files#diff-18ae0a0fab29a7db7aded913fd05f30a2c8f6c104fadae86c9d217091709794c">+1/-0</a> </td> </tr> </table></details></td></tr></tr></tbody></table> </details> ___
React Query with Nhost SDK
This guide demonstrates how to integrate GraphQL queries and mutations with React using TanStack Query (React Query) and the Nhost SDK.
Setup
1. Install Dependencies
npm install @tanstack/react-query @nhost/nhost-js graphql
# or
yarn add @tanstack/react-query @nhost/nhost-js graphql
# or
pnpm add @tanstack/react-query @nhost/nhost-js graphql
For development, add React Query DevTools:
npm install -D @tanstack/react-query-devtools
2. Generate Types with GraphQL CodeGen
Install GraphQL CodeGen:
npm install -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-operations @graphql-codegen/schema-ast
Set up codegen.ts:
import type { CodegenConfig } from "@graphql-codegen/cli";
const config: CodegenConfig = {
schema: [
{
"https://local.graphql.local.nhost.run/v1": {
headers: {
"x-hasura-admin-secret": "nhost-admin-secret",
},
},
},
],
documents: ["src/**/*.ts"],
ignoreNoDocuments: true,
generates: {
"./src/lib/graphql/__generated__/graphql.ts": {
documents: ["src/lib/graphql/**/*.graphql"],
plugins: [
"typescript",
"typescript-operations",
"typescript-react-query",
],
config: {
scalars: {
UUID: "string",
uuid: "string",
timestamptz: "string",
jsonb: "Record<string, any>",
bigint: "number",
bytea: "Buffer",
citext: "string",
},
exposeQueryKeys: true,
exposeFetcher: true,
fetcher: {
func: "../queryHooks#useAuthenticatedFetcher",
isReactHook: true,
},
useTypeImports: true,
reactQueryVersion: 5,
},
},
"./schema.graphql": {
plugins: ["schema-ast"],
config: {
includeDirectives: true,
},
},
},
};
export default config;
Integration Guide
1. Create an Auth Provider
Create an authentication context to manage the user session:
// src/lib/nhost/AuthProvider.tsx
import {
createContext,
useContext,
useEffect,
useState,
useMemo,
type ReactNode,
} from "react";
import { createClient, type NhostClient } from "@nhost/nhost-js";
import { type Session } from "@nhost/nhost-js/auth";
interface AuthContextType {
user: Session["user"] | null;
session: Session | null;
isAuthenticated: boolean;
isLoading: boolean;
nhost: NhostClient;
}
const AuthContext = createContext<AuthContextType | null>(null);
export const AuthProvider = ({ children }: { children: ReactNode }) => {
const [user, setUser] = useState<Session["user"] | null>(null);
const [session, setSession] = useState<Session | null>(null);
const [isLoading, setIsLoading] = useState<boolean>(true);
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
// Create the nhost client
const nhost = useMemo(
() =>
createClient({
region: import.meta.env.VITE_NHOST_REGION || "local",
subdomain: import.meta.env.VITE_NHOST_SUBDOMAIN || "local",
}),
[],
);
useEffect(() => {
setIsLoading(true);
const currentSession = nhost.getUserSession();
setUser(currentSession?.user || null);
setSession(currentSession);
setIsAuthenticated(!!currentSession);
setIsLoading(false);
const unsubscribe = nhost.sessionStorage.onChange((currentSession) => {
setUser(currentSession?.user || null);
setSession(currentSession);
setIsAuthenticated(!!currentSession);
});
return () => {
unsubscribe();
};
}, [nhost]);
const value: AuthContextType = {
user,
session,
isAuthenticated,
isLoading,
nhost,
};
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
};
export const useAuth = (): AuthContextType => {
const context = useContext(AuthContext);
if (!context) {
throw new Error("useAuth must be used within an AuthProvider");
}
return context;
};
2. Create Query Provider
Set up React Query with the Nhost client:
// src/lib/graphql/QueryProvider.tsx
import { type ReactNode } from "react";
import { QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { QueryClient } from "@tanstack/react-query";
interface QueryProviderProps {
children: ReactNode;
}
export function QueryProvider({ children }: QueryProviderProps) {
// Create the query client
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 10 * 1000, // 10 seconds
refetchOnWindowFocus: true,
retry: 1,
},
},
});
return (
<QueryClientProvider client={queryClient}>
{children}
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}
3. Create Authenticated Fetcher Hook
Create a utility to make authenticated GraphQL requests with the Nhost client:
// src/lib/graphql/queryHooks.ts
import { useCallback } from "react";
import { useAuth } from "../nhost/AuthProvider";
// This wrapper returns a fetcher function that uses the authenticated nhost client
export const useAuthenticatedFetcher = <TData, TVariables>(
document: string | { query: string; variables?: TVariables },
) => {
const { nhost } = useAuth();
return useCallback(
async (variables?: TVariables): Promise<TData> => {
// Handle both string format or document object format
const query = typeof document === "string" ? document : document.query;
const documentVariables =
typeof document === "object" ? document.variables : undefined;
const mergedVariables = variables || documentVariables;
const resp = await nhost.graphql.request<TData>({
query,
variables: mergedVariables as Record<string, unknown>,
});
if (!resp.body.data) {
throw new Error(
`Response does not contain data: ${JSON.stringify(resp.body)}`,
);
}
return resp.body.data;
},
[nhost],
);
};
4. Set Up Your App Providers
Wrap your application with the Auth and Query providers:
// src/main.tsx
import React from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App";
import { AuthProvider } from "./lib/nhost/AuthProvider";
import { QueryProvider } from "./lib/graphql/QueryProvider";
// Root component that sets up providers
const Root = () => (
<React.StrictMode>
<AuthProvider>
<QueryProvider>
<App />
</QueryProvider>
</AuthProvider>
</React.StrictMode>
);
const rootElement = document.getElementById("root");
if (!rootElement) throw new Error("Root element not found");
createRoot(rootElement).render(<Root />);
5. Define GraphQL Operations
Create a GraphQL file with your queries and mutations:
# src/lib/graphql/operations.graphql
query GetNinjaTurtlesWithComments {
ninjaTurtles {
id
name
description
createdAt
comments {
id
comment
createdAt
user {
id
email
displayName
}
}
}
}
mutation AddComment($ninjaTurtleId: uuid!, $comment: String!) {
insert_comments_one(
object: { ninjaTurtleId: $ninjaTurtleId, comment: $comment }
) {
id
}
}
6. Generate TypeScript Types
Run the code generator:
npx graphql-codegen
You can also add a script to your package.json:
{
"scripts": {
"codegen": "graphql-codegen --config codegen.ts"
}
}
Then run:
npm run codegen
# or
yarn codegen
# or
pnpm codegen
7. Use in Components
Finally, you can use the generated React Query hooks in your components:
// src/pages/Home.tsx
import { type JSX } from "react";
import {
useGetNinjaTurtlesWithCommentsQuery,
useAddCommentMutation,
} from "../lib/graphql/__generated__/graphql";
import { useState } from "react";
import { useAuth } from "../lib/nhost/AuthProvider";
import { Navigate } from "react-router-dom";
import { useQueryClient } from "@tanstack/react-query";
export default function Home(): JSX.Element {
const { isLoading } = useAuth();
const [activeCommentId, setActiveCommentId] = useState<string | null>(null);
const [commentText, setCommentText] = useState("");
const queryClient = useQueryClient();
// Query for data
const {
data,
isLoading: loading,
error,
} = useGetNinjaTurtlesWithCommentsQuery();
// Mutation hook
const { mutate: addComment } = useAddCommentMutation({
onSuccess: () => {
setCommentText("");
setActiveCommentId(null);
// Invalidate and refetch the ninja turtles query to get updated data
queryClient.invalidateQueries({
queryKey: ["GetNinjaTurtlesWithComments"],
});
},
});
if (isLoading) {
return <div>Loading...</div>;
}
const handleAddComment = (turtleId: string) => {
if (!commentText.trim()) return;
addComment({
ninjaTurtleId: turtleId,
comment: commentText,
});
};
if (loading) return <div>Loading ninja turtles...</div>;
if (error) return <div>Error: {error.message}</div>;
// Access the data
const ninjaTurtles = data?.ninjaTurtles || [];
return (
<div>
<h1>Ninja Turtles</h1>
{ninjaTurtles.map((turtle) => (
<div key={turtle.id}>
<h2>{turtle.name}</h2>
<p>{turtle.description}</p>
{/* Comments section */}
<div>
<h3>Comments ({turtle.comments.length})</h3>
{turtle.comments.map((comment) => (
<div key={comment.id}>
<p>{comment.comment}</p>
<small>
By{" "}
{comment.user?.displayName ||
comment.user?.email ||
"Anonymous"}
</small>
</div>
))}
{activeCommentId === turtle.id ? (
<div>
<textarea
value={commentText}
onChange={(e) => setCommentText(e.target.value)}
placeholder="Add your comment..."
/>
<div>
<button onClick={() => setActiveCommentId(null)}>
Cancel
</button>
<button onClick={() => handleAddComment(turtle.id)}>
Submit
</button>
</div>
</div>
) : (
<button onClick={() => setActiveCommentId(turtle.id)}>
Add a comment
</button>
)}
</div>
</div>
))}
</div>
);
}
Appendix: Known GraphQL CodeGen Issues
When using GraphQL Code Generator with both useTypeImports: true and a custom fetcher that's a React hook, there's a known bug (issue #824) that causes incorrect import statements in the generated code.
The problem occurs because when useTypeImports is enabled, the generator incorrectly adds the type keyword to the import statement for your custom fetcher function:
import type { useAuthenticatedFetcher } from "../queryHooks";
Since useAuthenticatedFetcher is a React hook that needs to be executed at runtime (not just used as a type), this causes TypeScript errors because the function can't be called when imported as a type.
To fix this issue, you need to modify the generated file to remove the type keyword from the import statement. This can be done with a post-processing wrapper script (codegen-wrapper.sh):
#!/bin/bash
# due to bug https://github.com/dotansimha/graphql-code-generator-community/issues/824
# Exit immediately if a command exits with a non-zero status.
set -e
echo "Running GraphQL code generator..."
# Run the original codegen command
pnpm graphql-codegen --config codegen.ts
# Path to the generated file
GENERATED_FILE="src/lib/graphql/__generated__/graphql.ts"
echo "Fixing import in $GENERATED_FILE..."
if [ -f "$GENERATED_FILE" ]; then
sed -i -e 's/import type { useAuthenticatedFetcher }/import { useAuthenticatedFetcher }/g' "$GENERATED_FILE"
echo "Successfully removed \"type\" from useAuthenticatedFetcher import."
else
echo "Error: Generated file not found at $GENERATED_FILE"
exit 1
fi
echo "All tasks completed successfully."