{"id":50762335,"url":"https://github.com/devlinduldulao/tanstack-query-and-heyapi-demo","last_synced_at":"2026-06-11T11:02:00.392Z","repository":{"id":333678272,"uuid":"1138147960","full_name":"devlinduldulao/tanstack-query-and-heyapi-demo","owner":"devlinduldulao","description":null,"archived":false,"fork":false,"pushed_at":"2026-04-23T22:17:17.000Z","size":8919,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-01T19:07:36.324Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/devlinduldulao.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-01-20T09:54:51.000Z","updated_at":"2026-04-25T14:11:57.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/devlinduldulao/tanstack-query-and-heyapi-demo","commit_stats":null,"previous_names":["devlinduldulao/tanstack-query-and-heyapi-demo"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/devlinduldulao/tanstack-query-and-heyapi-demo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devlinduldulao%2Ftanstack-query-and-heyapi-demo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devlinduldulao%2Ftanstack-query-and-heyapi-demo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devlinduldulao%2Ftanstack-query-and-heyapi-demo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devlinduldulao%2Ftanstack-query-and-heyapi-demo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/devlinduldulao","download_url":"https://codeload.github.com/devlinduldulao/tanstack-query-and-heyapi-demo/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devlinduldulao%2Ftanstack-query-and-heyapi-demo/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34195117,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-11T02:00:06.485Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-11T11:01:59.229Z","updated_at":"2026-06-11T11:02:00.385Z","avatar_url":"https://github.com/devlinduldulao.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Hey API — Generated Code Deep Dive\n\n\u003e This document explains every folder and file generated by [Hey API](https://heyapi.dev/) (`@hey-api/openapi-ts`) so you can confidently present them at a React conference.\n\n---\n\n## Table of Contents\n\n1. [What is Hey API?](#what-is-hey-api)\n2. [How Code Generation Works](#how-code-generation-works)\n3. [Folder Structure Overview](#folder-structure-overview)\n4. [Top-Level Files](#top-level-files)\n   - [`client.gen.ts`](#clientgents) — The singleton API client\n   - [`index.ts`](#indexts) — Public barrel export\n   - [`sdk.gen.ts`](#sdkgents) — SDK functions (the actual API calls)\n   - [`types.gen.ts`](#typesgents) — TypeScript types\n   - [`zod.gen.ts`](#zodgents) — Zod validation schemas\n5. [The `@tanstack/` Folder](#the-tanstack-folder)\n   - [`react-query.gen.ts`](#react-querygents) — TanStack Query hooks\n6. [The `client/` Folder](#the-client-folder)\n   - [`client.gen.ts`](#clientclientgents) — Axios client factory\n   - [`index.ts`](#clientindexts) — Client barrel export\n   - [`types.gen.ts`](#clienttypesgents) — Client infrastructure types\n   - [`utils.gen.ts`](#clientutilsgents) — Client utilities\n7. [The `core/` Folder](#the-core-folder)\n   - [`auth.gen.ts`](#coreauthgents) — Authentication helpers\n   - [`bodySerializer.gen.ts`](#corebodyserializergents) — Body serializers\n   - [`params.gen.ts`](#coreparamsgents) — Parameter mapping\n   - [`pathSerializer.gen.ts`](#corepathserializergents) — URL path serialization\n   - [`queryKeySerializer.gen.ts`](#corequerykeycerializergents) — Query key serialization\n   - [`serverSentEvents.gen.ts`](#coreserversenteventsgents) — SSE streaming support\n   - [`types.gen.ts`](#coretypesgents) — Core type definitions\n   - [`utils.gen.ts`](#coreutilsgents) — URL building and config merging\n8. [How Everything Connects](#how-everything-connects)\n9. [The Generation Config](#the-generation-config)\n10. [Usage Examples](#usage-examples)\n11. [How to Convince React Developers to Try It](#how-to-convince-react-developers-to-try-it)\n\n---\n\n## What is Hey API?\n\n**Hey API** (`@hey-api/openapi-ts`) is a code generation tool that reads an **OpenAPI specification** (Swagger) and produces a fully type-safe TypeScript client. Think of it as a bridge between your backend's API contract (the OpenAPI spec) and your frontend's TypeScript code.\n\n### Why does this matter?\n\nWithout Hey API, you'd manually:\n\n1. Write TypeScript types for every request and response\n2. Write fetch/axios calls for every endpoint\n3. Write TanStack Query hooks for every query and mutation\n4. Write Zod schemas if you need runtime validation\n5. Keep all of the above **in sync** with the backend every time it changes\n\nHey API automates all of that. One command, and your entire API layer is generated, type-safe, and ready to use.\n\n```\nOpenAPI Spec (swagger.yaml)  →  Hey API  →  TypeScript Client + Types + Hooks + Schemas\n```\n\n---\n\n## How Code Generation Works\n\nWhen you run:\n\n```bash\nnpm run openapi-ts\n```\n\nHey API reads `openapi-ts.config.ts` and:\n\n1. **Parses** the `swagger.yaml` file (your OpenAPI spec)\n2. **Generates** TypeScript code based on the enabled **plugins**\n3. **Post-processes** the output with `oxlint` and `oxfmt` (linting and formatting)\n4. **Writes** everything to `src/api/client/`\n\nThe key insight: **you never edit these files**. If the API changes, you update the OpenAPI spec and re-run the generator. Your entire API layer stays perfectly in sync.\n\n---\n\n## Folder Structure Overview\n\n```\nsrc/api/client/\n├── client.gen.ts              ← Singleton API client instance\n├── index.ts                   ← Barrel export (public API surface)\n├── sdk.gen.ts                 ← SDK functions (GET, POST, PUT, DELETE wrappers)\n├── types.gen.ts               ← TypeScript types for all models + request/response shapes\n├── zod.gen.ts                 ← Zod schemas for runtime validation\n│\n├── @tanstack/                 ← TanStack Query integration\n│   └── react-query.gen.ts     ← Query options, query keys, and mutation configs\n│\n├── client/                    ← HTTP client infrastructure (Axios-based)\n│   ├── client.gen.ts          ← createClient() factory function\n│   ├── index.ts               ← Client barrel export\n│   ├── types.gen.ts           ← Client config, options, and result types\n│   └── utils.gen.ts           ← Query serializer, auth, URL building, config merging\n│\n└── core/                      ← Low-level shared utilities\n    ├── auth.gen.ts            ← Auth token resolution (Bearer, Basic, API Key)\n    ├── bodySerializer.gen.ts  ← FormData, JSON, URLSearchParams serializers\n    ├── params.gen.ts          ← Parameter slot mapping (body, path, query, headers)\n    ├── pathSerializer.gen.ts  ← URL path parameter serialization\n    ├── queryKeySerializer.gen.ts  ← TanStack Query key serialization\n    ├── serverSentEvents.gen.ts    ← Server-Sent Events (SSE) streaming client\n    ├── types.gen.ts           ← Core type definitions (Client, Config, HttpMethod)\n    └── utils.gen.ts           ← URL construction, path param resolution\n```\n\n---\n\n## Top-Level Files\n\nThese are the files you interact with most directly. They're the \"public API\" of the generated client.\n\n### `client.gen.ts`\n\n**Purpose:** Creates and exports the singleton API client instance.\n\n**Importance:** ⭐⭐⭐⭐⭐ (Critical) — This is the single client instance that every SDK function uses by default.\n\n```ts\n// What it does\nexport const client = createClient(createConfig\u003cClientOptions2\u003e());\n```\n\n**Why it matters:**\n\n- It's the \"entry point\" to the HTTP layer. Every API call flows through this client.\n- You configure it once (base URL, auth headers, interceptors) and every generated SDK function uses it automatically.\n- You can override it per-request by passing a custom `client` option.\n\n**How you use it:**\n\n```ts\nimport { client } from \"@/api/client/client.gen\";\n\n// Configure the base URL (typically done once at app startup)\nclient.setConfig({ baseURL: \"https://fakerestapi.azurewebsites.net\" });\n```\n\n---\n\n### `index.ts`\n\n**Purpose:** Barrel export file — re-exports everything consumers need from one clean import path.\n\n**Importance:** ⭐⭐⭐⭐ (High) — Provides a clean public API surface.\n\n**Why it matters:**\n\n- Instead of importing from deeply nested paths, you import from `@/api/client`.\n- It re-exports all SDK functions (`getApiV1Activities`, `postApiV1Books`, etc.) and all types (`Activity`, `Book`, `Author`, etc.).\n- Think of it as the \"table of contents\" of your generated API.\n\n**How you use it:**\n\n```ts\n// Clean imports through the barrel\nimport { getApiV1Activities } from \"@/api/client\";\nimport type { Activity, Book } from \"@/api/client\";\n```\n\n---\n\n### `sdk.gen.ts`\n\n**Purpose:** Contains one typed function per API endpoint — your entire SDK.\n\n**Importance:** ⭐⭐⭐⭐⭐ (Critical) — This is where the actual API calls live.\n\nFor each endpoint in your OpenAPI spec, Hey API generates a function like:\n\n```ts\n// GET /api/v1/Activities → getApiV1Activities()\nexport const getApiV1Activities = \u003cThrowOnError extends boolean = false\u003e(\n  options?: Options\u003cGetApiV1ActivitiesData, ThrowOnError\u003e,\n) =\u003e\n  (options?.client ?? client).get\u003cGetApiV1ActivitiesResponses, unknown, ThrowOnError\u003e({\n    responseType: \"json\",\n    url: \"/api/v1/Activities\",\n    ...options,\n  });\n\n// GET /api/v1/Activities/{id} → getApiV1ActivitiesById()\nexport const getApiV1ActivitiesById = \u003cThrowOnError extends boolean = false\u003e(\n  options: Options\u003cGetApiV1ActivitiesByIdData, ThrowOnError\u003e,\n) =\u003e\n  (options.client ?? client).get\u003cGetApiV1ActivitiesByIdResponses, unknown, ThrowOnError\u003e({\n    responseType: \"json\",\n    url: \"/api/v1/Activities/{id}\",\n    ...options,\n  });\n```\n\n**Key design decisions:**\n\n1. **Each function maps to one endpoint** — `GET /api/v1/Books` → `getApiV1Books()`, `POST /api/v1/Books` → `postApiV1Books()`\n2. **Type-safe options** — The `options` parameter knows exactly what `path`, `body`, and `query` params each endpoint expects\n3. **Uses the singleton client** — Falls back to the shared `client` instance, but you can override with `options.client`\n4. **ThrowOnError generic** — Controls whether errors are thrown or returned in the response shape. By default, errors are returned (not thrown), making error handling explicit.\n\n**What gets generated for each HTTP method:**\n\n| OpenAPI Endpoint                 | Generated Function            | Required Params    |\n| -------------------------------- | ----------------------------- | ------------------ |\n| `GET /api/v1/Activities`         | `getApiV1Activities()`        | None (optional)    |\n| `POST /api/v1/Activities`        | `postApiV1Activities()`       | `body` (Activity)  |\n| `GET /api/v1/Activities/{id}`    | `getApiV1ActivitiesById()`    | `path.id` (number) |\n| `PUT /api/v1/Activities/{id}`    | `putApiV1ActivitiesById()`    | `path.id` + `body` |\n| `DELETE /api/v1/Activities/{id}` | `deleteApiV1ActivitiesById()` | `path.id`          |\n\n---\n\n### `types.gen.ts`\n\n**Purpose:** TypeScript type definitions for every model, request, and response in your API.\n\n**Importance:** ⭐⭐⭐⭐⭐ (Critical) — The type safety foundation of everything.\n\nThis file contains three categories of types:\n\n#### 1. Model Types (from `components.schemas` in OpenAPI)\n\n```ts\nexport type Activity = {\n  id?: number;\n  title?: string | null;\n  dueDate?: string;\n  completed?: boolean;\n};\n\nexport type Book = {\n  id?: number;\n  title?: string | null;\n  description?: string | null;\n  pageCount?: number;\n  excerpt?: string | null;\n  publishDate?: string;\n};\n```\n\nThese map directly to the `schemas` section of your OpenAPI spec. Every property, its type, and its optionality are derived from the spec.\n\n#### 2. Request Data Types (what you send)\n\n```ts\n// Describes what GET /api/v1/Activities/{id} expects\nexport type GetApiV1ActivitiesByIdData = {\n  body?: never; // No body for GET requests\n  path: { id: number }; // Required path parameter\n  query?: never; // No query params\n  url: \"/api/v1/Activities/{id}\";\n};\n\n// Describes what PUT /api/v1/Activities/{id} expects\nexport type PutApiV1ActivitiesByIdData = {\n  body?: Activity; // Optional request body\n  path: { id: number }; // Required path parameter\n  query?: never; // No query params\n  url: \"/api/v1/Activities/{id}\";\n};\n```\n\n**Why this is powerful:** TypeScript will catch bugs at compile time. If you pass `{ id: \"abc\" }` to a function expecting `{ id: number }`, you get a red squiggly _before_ you even run the code.\n\n#### 3. Response Types (what you get back)\n\n```ts\nexport type GetApiV1ActivitiesResponses = {\n  200: Array\u003cActivity\u003e; // Success returns Activity[]\n};\n\nexport type GetApiV1ActivitiesResponse = GetApiV1ActivitiesResponses[keyof GetApiV1ActivitiesResponses];\n// Resolves to: Array\u003cActivity\u003e\n```\n\nThe response types use a mapped object pattern (`{ 200: ... }`) that maps HTTP status codes to their response shapes, then extracts the union.\n\n---\n\n### `zod.gen.ts`\n\n**Purpose:** Zod schemas for runtime validation of API data.\n\n**Importance:** ⭐⭐⭐⭐ (High) — Adds runtime safety on top of compile-time safety.\n\n**Why TypeScript types alone aren't enough:**\nTypeScript types only exist at **compile time**. Once your code is running in the browser, types are gone — JavaScript doesn't know about them. If the API returns unexpected data (wrong types, missing fields, extra fields), TypeScript can't help you.\n\nZod schemas validate data at **runtime**:\n\n```ts\n// Model schema — validates an Activity object at runtime\nexport const zActivity = z.object({\n  id: z.int().min(-2147483648).max(2147483647).optional(),\n  title: z.string().nullish(),\n  dueDate: z.iso.datetime().optional(),\n  completed: z.boolean().optional(),\n});\n\n// Response schema — validates the full response\nexport const zGetApiV1ActivitiesResponse = z.array(zActivity);\n\n// Path parameter schema — validates path params\nexport const zGetApiV1ActivitiesByIdPath = z.object({\n  id: z.int().min(-2147483648).max(2147483647),\n});\n\n// Request body schema — validates request bodies\nexport const zPostApiV1ActivitiesBody = zActivity;\n```\n\n**Use cases:**\n\n1. **Form validation** — Use the Zod schemas with `react-hook-form`'s `zodResolver` to validate user input before sending it to the API\n2. **Response validation** — Pass schemas to the client's `responseValidator` to verify API responses at runtime\n3. **Request validation** — Pass schemas to `requestValidator` to catch invalid requests before they leave the browser\n\n---\n\n## The `@tanstack/` Folder\n\n### `react-query.gen.ts`\n\n**Purpose:** Pre-configured TanStack Query `queryOptions` and `UseMutationOptions` for every endpoint.\n\n**Importance:** ⭐⭐⭐⭐⭐ (Critical) — This is the **bridge between Hey API and React**. It's what makes the generated client feel like a React-native library.\n\nThis file generates three things per GET endpoint and one thing per mutation endpoint:\n\n#### For GET Endpoints: Query Key + Query Options\n\n```ts\n// 1. Query Key Factory — deterministic, serializable key for caching\nexport const getApiV1ActivitiesQueryKey = (options?: Options\u003cGetApiV1ActivitiesData\u003e) =\u003e\n  createQueryKey(\"getApiV1Activities\", options);\n\n// 2. Query Options Factory — plug directly into useQuery/useSuspenseQuery\nexport const getApiV1ActivitiesOptions = (options?: Options\u003cGetApiV1ActivitiesData\u003e) =\u003e\n  queryOptions\u003c\n    GetApiV1ActivitiesResponse, // Data type\n    AxiosError\u003cDefaultError\u003e, // Error type\n    GetApiV1ActivitiesResponse, // Select type\n    ReturnType\u003ctypeof getApiV1ActivitiesQueryKey\u003e // Query key type\n  \u003e({\n    queryFn: async ({ queryKey, signal }) =\u003e {\n      const { data } = await getApiV1Activities({\n        ...options,\n        ...queryKey[0],\n        signal, // Automatic request cancellation!\n        throwOnError: true, // Let TanStack Query handle errors\n      });\n      return data;\n    },\n    queryKey: getApiV1ActivitiesQueryKey(options),\n  });\n```\n\n**Why `queryOptions()` is powerful:**\n\n- `queryOptions()` is a TanStack Query v5 utility that bundles `queryKey` + `queryFn` + types together\n- You get **full type inference** — `data` in `useSuspenseQuery(getApiV1ActivitiesOptions())` is typed as `Array\u003cActivity\u003e` automatically\n- The `signal` parameter enables **automatic request cancellation** when a component unmounts\n- The `queryKey` includes the request parameters, so `getApiV1ActivitiesById({ path: { id: 1 } })` and `getApiV1ActivitiesById({ path: { id: 2 } })` are cached separately\n\n#### For POST/PUT/DELETE Endpoints: Mutation Options\n\n```ts\nexport const postApiV1ActivitiesMutation = (\n  options?: Partial\u003cOptions\u003cPostApiV1ActivitiesData\u003e\u003e,\n): UseMutationOptions\u003cPostApiV1ActivitiesResponse, AxiosError\u003cDefaultError\u003e, Options\u003cPostApiV1ActivitiesData\u003e\u003e =\u003e ({\n  mutationFn: async (fnOptions) =\u003e {\n    const { data } = await postApiV1Activities({\n      ...options,\n      ...fnOptions,\n      throwOnError: true,\n    });\n    return data;\n  },\n});\n```\n\n**How you use these in React:**\n\n```tsx\n// Queries — just pass the options factory to useSuspenseQuery\nconst { data: activities } = useSuspenseQuery(getApiV1ActivitiesOptions());\nconst { data: activity } = useSuspenseQuery(getApiV1ActivitiesByIdOptions({ path: { id: 1 } }));\n\n// Mutations — spread the mutation factory into useMutation\nconst mutation = useMutation({\n  ...postApiV1ActivitiesMutation(),\n  onSuccess: () =\u003e toast.success(\"Created!\"),\n});\n\n// Prefetching in route loaders — same options factory\nvoid queryClient.ensureQueryData(getApiV1ActivitiesOptions());\n```\n\n#### The `createQueryKey` Helper\n\n```ts\nconst createQueryKey = \u003cTOptions extends Options\u003e(\n  id: string,\n  options?: TOptions,\n  infinite?: boolean,\n  tags?: ReadonlyArray\u003cstring\u003e,\n): [QueryKey\u003cTOptions\u003e[0]] =\u003e {\n  const params: QueryKey\u003cTOptions\u003e[0] = {\n    _id: id,\n    baseURL: options?.baseURL || (options?.client ?? client).getConfig().baseURL,\n  } as QueryKey\u003cTOptions\u003e[0];\n  // ... includes body, headers, path, query if present\n  return [params];\n};\n```\n\nThis is how Hey API creates **deterministic query keys**. Every unique combination of endpoint + parameters gets a unique key, which TanStack Query uses for caching, deduplication, and invalidation.\n\n---\n\n## The `client/` Folder\n\nThis folder contains the HTTP client infrastructure — the Axios-based HTTP engine that powers every API call.\n\n### `client/client.gen.ts`\n\n**Purpose:** The `createClient()` factory function — creates a configured Axios-powered HTTP client.\n\n**Importance:** ⭐⭐⭐⭐⭐ (Critical) — This is the HTTP engine.\n\nThis is a large file (~150 lines) that creates a client object with methods for every HTTP verb. Key features:\n\n- **Wraps Axios** — Uses `axios.create()` internally but adds a typed, ergonomic API on top\n- **Auth handling** — Automatically injects auth tokens via the `security` config before each request\n- **Request/Response validation** — Supports `requestValidator` and `responseValidator` hooks\n- **Body serialization** — Handles JSON, FormData, and URLSearchParams automatically\n- **Path parameter interpolation** — Replaces `{id}` in URLs with actual values\n- **SSE support** — Built-in Server-Sent Events streaming via the `sse` namespace\n- **Error handling** — Configurable `throwOnError` behavior\n\n```ts\n// The client exposes methods matching HTTP verbs\nreturn {\n  buildUrl: _buildUrl,\n  delete: makeMethodFn(\"DELETE\"),\n  get: makeMethodFn(\"GET\"),\n  getConfig,\n  head: makeMethodFn(\"HEAD\"),\n  patch: makeMethodFn(\"PATCH\"),\n  post: makeMethodFn(\"POST\"),\n  put: makeMethodFn(\"PUT\"),\n  request,            // Generic request method\n  setConfig,          // Runtime config updates\n  sse: { ... },       // SSE streaming methods\n  instance,           // Raw Axios instance (escape hatch)\n} as Client;\n```\n\n### `client/index.ts`\n\n**Purpose:** Barrel export for the client infrastructure.\n\n**Importance:** ⭐⭐⭐ (Medium) — Organizational convenience.\n\nRe-exports `createClient`, `createConfig`, types, and serializers so other generated files can import from `\"./client\"` instead of deep paths.\n\n### `client/types.gen.ts`\n\n**Purpose:** TypeScript types for the client infrastructure itself.\n\n**Importance:** ⭐⭐⭐⭐ (High) — Defines the shape of every request and configuration.\n\nKey types:\n\n| Type             | What It Defines                                                         |\n| ---------------- | ----------------------------------------------------------------------- |\n| `Config`         | Client configuration (baseURL, headers, auth, serializers, validators)  |\n| `RequestOptions` | Per-request options (body, path, query, url, method)                    |\n| `ClientOptions`  | Simplified options (baseURL, throwOnError)                              |\n| `RequestResult`  | Return type — either success response or error, based on `ThrowOnError` |\n| `Client`         | The full client interface with all HTTP method functions                |\n| `Options`        | The user-facing options type that SDK functions accept                  |\n| `TDataShape`     | Base shape for request data (body, headers, path, query, url)           |\n\nThe `RequestResult` type is particularly clever:\n\n```ts\n// If ThrowOnError is true → returns Promise\u003cAxiosResponse\u003cTData\u003e\u003e\n// If ThrowOnError is false → returns Promise\u003cSuccessResponse | ErrorResponse\u003e\nexport type RequestResult\u003cTData, TError, ThrowOnError extends boolean\u003e = ThrowOnError extends true\n  ? Promise\u003cAxiosResponse\u003cTData\u003e\u003e\n  : Promise\u003cSuccessResponse | ErrorResponse\u003e;\n```\n\nThis conditional type means TypeScript narrows the return type based on your `throwOnError` setting.\n\n### `client/utils.gen.ts`\n\n**Purpose:** Utility functions for query serialization, auth, URL building, header merging, and config creation.\n\n**Importance:** ⭐⭐⭐⭐ (High) — The \"plumbing\" that makes everything work.\n\nKey utilities:\n\n| Function                  | What It Does                                                        |\n| ------------------------- | ------------------------------------------------------------------- |\n| `createQuerySerializer()` | Serializes query params following OpenAPI style/explode rules       |\n| `setAuthParams()`         | Injects auth tokens into headers, query, or cookies                 |\n| `buildUrl()`              | Constructs the final request URL with path params and query string  |\n| `mergeConfigs()`          | Deep merges client configurations                                   |\n| `mergeHeaders()`          | Intelligently merges header objects (handles Axios header keywords) |\n| `createConfig()`          | Creates an initial client configuration                             |\n\n---\n\n## The `core/` Folder\n\nLow-level, framework-agnostic utilities. These are shared primitives that the client layer builds on.\n\n### `core/auth.gen.ts`\n\n**Purpose:** Token resolution for different auth schemes.\n\n**Importance:** ⭐⭐⭐ (Medium)\n\n```ts\n// Supports Bearer, Basic, and raw API Key tokens\nexport const getAuthToken = async (auth: Auth, callback) =\u003e {\n  const token = typeof callback === \"function\" ? await callback(auth) : callback;\n  if (auth.scheme === \"bearer\") return `Bearer ${token}`;\n  if (auth.scheme === \"basic\") return `Basic ${btoa(token)}`;\n  return token;\n};\n```\n\nThis file handles the three common auth patterns: **Bearer tokens** (OAuth/JWT), **Basic auth** (username:password), and **API Keys**. The `callback` parameter means you can provide either a static token string or an async function (like Azure MSAL's `acquireTokenSilent`).\n\n### `core/bodySerializer.gen.ts`\n\n**Purpose:** Serializers for different request body content types.\n\n**Importance:** ⭐⭐⭐ (Medium)\n\nProvides three built-in serializers:\n\n| Serializer                      | When It's Used                                           |\n| ------------------------------- | -------------------------------------------------------- |\n| `jsonBodySerializer`            | Default — `JSON.stringify()` with BigInt support         |\n| `formDataBodySerializer`        | File uploads — converts objects to `FormData`            |\n| `urlSearchParamsBodySerializer` | Form submissions — converts objects to `URLSearchParams` |\n\n### `core/params.gen.ts`\n\n**Purpose:** Maps flat user-facing parameters into the correct request slots (body, path, query, headers).\n\n**Importance:** ⭐⭐⭐ (Medium)\n\nThis is the system that lets Hey API support \"flat\" parameter styles. Instead of always requiring nested objects, it can map simple key-value pairs to the right place in the request.\n\n### `core/pathSerializer.gen.ts`\n\n**Purpose:** Serializes path parameters following OpenAPI serialization rules.\n\n**Importance:** ⭐⭐⭐ (Medium)\n\nHandles the different OpenAPI path parameter styles:\n\n| Style              | Example Input       | Example Output      |\n| ------------------ | ------------------- | ------------------- |\n| `simple` (default) | `{ id: 5 }`         | `/activities/5`     |\n| `label`            | `{ id: 5 }`         | `/activities/.5`    |\n| `matrix`           | `{ id: 5 }`         | `/activities/;id=5` |\n| Arrays             | `{ ids: [1,2,3] }`  | `/activities/1,2,3` |\n| Objects            | `{ filter: {a:1} }` | `/activities/a,1`   |\n\n### `core/queryKeySerializer.gen.ts`\n\n**Purpose:** Serializes query key values into a deterministic JSON-friendly format for TanStack Query.\n\n**Importance:** ⭐⭐⭐ (Medium)\n\nTanStack Query uses query keys for caching and deduplication. These keys must be **deterministic** and **serializable**. This file handles edge cases like:\n\n- `BigInt` → converted to string\n- `Date` → converted to ISO string\n- `URLSearchParams` → sorted and converted to object\n- `Headers` → normalized to plain object\n- Deeply nested objects → recursively serialized\n\n### `core/serverSentEvents.gen.ts`\n\n**Purpose:** Built-in Server-Sent Events (SSE) client with retry logic.\n\n**Importance:** ⭐⭐ (Low for this project, High if you use streaming APIs)\n\nProvides an async generator-based SSE client that:\n\n- Parses the SSE text protocol (`event:`, `data:`, `id:`, `retry:`)\n- Supports automatic reconnection with exponential backoff\n- Respects server-sent `retry` delays\n- Supports request interceptors and response transformers\n\n```ts\n// Usage example (if your API had SSE endpoints)\nconst { stream } = await client.sse.get({ url: \"/events\" });\nfor await (const event of stream) {\n  console.log(event);\n}\n```\n\n### `core/types.gen.ts`\n\n**Purpose:** Core type definitions shared across the generated code.\n\n**Importance:** ⭐⭐⭐⭐ (High)\n\nDefines the `Client` interface shape, `Config` type with all configuration options (auth, serializers, validators, transformers), `HttpMethod` union type, and utility types like `OmitNever`.\n\n### `core/utils.gen.ts`\n\n**Purpose:** URL construction utilities — builds the final request URL from base URL + path params + query string.\n\n**Importance:** ⭐⭐⭐ (Medium)\n\nKey functions:\n\n- `defaultPathSerializer()` — Replaces `{param}` placeholders in URL templates with actual values\n- `getUrl()` — Combines base URL + path + query string into the final URL\n\n```ts\n// \"/api/v1/Activities/{id}\" + { path: { id: 5 } }\n// → \"https://example.com/api/v1/Activities/5\"\n```\n\n---\n\n## How Everything Connects\n\nHere's the flow when you write `useSuspenseQuery(getApiV1ActivitiesByIdOptions({ path: { id: 1 } }))`:\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│  Your React Component                                           │\n│  useSuspenseQuery(getApiV1ActivitiesByIdOptions({ path: {id:1}}))│\n└──────────────────────────┬──────────────────────────────────────┘\n                           │\n                           ▼\n┌─────────────────────────────────────────────────────────────────┐\n│  @tanstack/react-query.gen.ts                                   │\n│  getApiV1ActivitiesByIdOptions() returns:                       │\n│  - queryKey: [{ _id: \"getApiV1ActivitiesById\", path: {id:1} }] │\n│  - queryFn: calls getApiV1ActivitiesById() from sdk.gen.ts     │\n└──────────────────────────┬──────────────────────────────────────┘\n                           │\n                           ▼\n┌─────────────────────────────────────────────────────────────────┐\n│  sdk.gen.ts                                                     │\n│  getApiV1ActivitiesById() calls:                                │\n│  client.get({ url: \"/api/v1/Activities/{id}\", path: {id:1} })  │\n└──────────────────────────┬──────────────────────────────────────┘\n                           │\n                           ▼\n┌─────────────────────────────────────────────────────────────────┐\n│  client/client.gen.ts                                           │\n│  1. Resolves auth (core/auth.gen.ts)                            │\n│  2. Serializes body (core/bodySerializer.gen.ts)                │\n│  3. Builds URL: \"/api/v1/Activities/1\" (core/utils.gen.ts)      │\n│  4. Sends request via Axios                                     │\n│  5. Validates + transforms response                             │\n│  6. Returns typed { data: Activity }                            │\n└─────────────────────────────────────────────────────────────────┘\n```\n\n### The Layer Cake\n\n| Layer            | Files                          | Role                             |\n| ---------------- | ------------------------------ | -------------------------------- |\n| **React Layer**  | `@tanstack/react-query.gen.ts` | Hooks, query keys, query options |\n| **SDK Layer**    | `sdk.gen.ts`                   | One typed function per endpoint  |\n| **Client Layer** | `client/*.gen.ts`              | HTTP engine (Axios wrapper)      |\n| **Core Layer**   | `core/*.gen.ts`                | Serializers, auth, utilities     |\n| **Type Layer**   | `types.gen.ts`, `zod.gen.ts`   | Type safety (compile + runtime)  |\n\nEach layer only depends on the layers below it. Your React components only import from the top two layers.\n\n---\n\n## The Generation Config\n\n```ts\n// openapi-ts.config.ts\nimport { defaultPlugins, defineConfig } from \"@hey-api/openapi-ts\";\n\nexport default defineConfig({\n  input: \"./swagger.yaml\", // OpenAPI spec\n  plugins: [\n    ...defaultPlugins, // types.gen.ts + sdk.gen.ts\n    \"@hey-api/client-axios\", // Axios-based client (client/ folder)\n    \"@tanstack/react-query\", // TanStack Query hooks (@tanstack/ folder)\n    \"zod\", // Zod schemas (zod.gen.ts)\n  ],\n  output: { postProcess: [\"oxlint\", \"oxfmt\"], path: \"src/api/client\" }, // Output dir + formatting\n});\n```\n\n| Plugin                  | What It Generates                                          |\n| ----------------------- | ---------------------------------------------------------- |\n| `defaultPlugins`        | `types.gen.ts`, `sdk.gen.ts`, `client.gen.ts`, `index.ts`  |\n| `@hey-api/client-axios` | The `client/` folder — Axios HTTP client infrastructure    |\n| `@tanstack/react-query` | The `@tanstack/` folder — Query options + mutation configs |\n| `zod`                   | `zod.gen.ts` — Runtime validation schemas                  |\n\n---\n\n## Usage Examples\n\n### Fetching Data in a Route Loader (Prefetch Pattern)\n\n```tsx\nimport { createFileRoute } from \"@tanstack/react-router\";\nimport { getApiV1ActivitiesOptions } from \"@/api/client/@tanstack/react-query.gen\";\n\nexport const Route = createFileRoute(\"/activities/\")({\n  loader: ({ context: { queryClient } }) =\u003e {\n    // Prefetch without blocking — React shows pendingComponent immediately\n    void queryClient.ensureQueryData(getApiV1ActivitiesOptions());\n  },\n  pendingComponent: () =\u003e \u003cdiv\u003eLoading...\u003c/div\u003e,\n  component: ActivitiesPage,\n});\n```\n\n### Using Data in a Component\n\n```tsx\nimport { useSuspenseQuery } from \"@tanstack/react-query\";\nimport { getApiV1ActivitiesByIdOptions } from \"@/api/client/@tanstack/react-query.gen\";\n\nfunction ActivityDetails({ id }: { id: number }) {\n  // Fully typed — data is Activity, not any\n  const { data: activity } = useSuspenseQuery(getApiV1ActivitiesByIdOptions({ path: { id } }));\n\n  return \u003ch1\u003e{activity.title}\u003c/h1\u003e;\n}\n```\n\n### Creating Data with a Mutation\n\n```tsx\nimport { useMutation } from \"@tanstack/react-query\";\nimport { postApiV1ActivitiesMutation } from \"@/api/client/@tanstack/react-query.gen\";\nimport type { Activity } from \"@/api/client/types.gen\";\n\nfunction CreateActivityForm() {\n  const mutation = useMutation({\n    ...postApiV1ActivitiesMutation(),\n    onSuccess: (data) =\u003e {\n      toast.success(`Created activity: ${data.title}`);\n    },\n  });\n\n  const handleSubmit = (values: Activity) =\u003e {\n    mutation.mutate({ body: values });\n  };\n}\n```\n\n### Validating Form Input with Generated Zod Schemas\n\n```tsx\nimport { useForm } from \"react-hook-form\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { zActivity } from \"@/api/client/zod.gen\";\nimport type { Activity } from \"@/api/client/types.gen\";\n\nfunction ActivityForm() {\n  const form = useForm\u003cActivity\u003e({\n    resolver: zodResolver(zActivity),\n    defaultValues: { title: \"\", completed: false },\n  });\n}\n```\n\n### Invalidating Cache After Mutations\n\n```tsx\nimport { getApiV1ActivitiesQueryKey } from \"@/api/client/@tanstack/react-query.gen\";\n\n// Invalidate all activities queries\nqueryClient.invalidateQueries({\n  queryKey: getApiV1ActivitiesQueryKey(),\n});\n\n// Invalidate a specific activity\nqueryClient.invalidateQueries({\n  queryKey: getApiV1ActivitiesByIdQueryKey({ path: { id: 1 } }),\n});\n```\n\n---\n\n## Summary Table\n\n| File                           | What                                              | Why                      | You Import From It?                 |\n| ------------------------------ | ------------------------------------------------- | ------------------------ | ----------------------------------- |\n| `types.gen.ts`                 | TS types for all models + requests + responses    | Compile-time type safety | ✅ Yes — types                      |\n| `zod.gen.ts`                   | Zod schemas for all models + requests + responses | Runtime validation       | ✅ Yes — form validation            |\n| `sdk.gen.ts`                   | One function per API endpoint                     | Type-safe API calls      | ⚠️ Rarely — use query hooks instead |\n| `client.gen.ts`                | Singleton Axios client                            | HTTP engine config       | ✅ Yes — `client.setConfig()`       |\n| `index.ts`                     | Barrel re-exports                                 | Clean import paths       | ✅ Yes — convenience                |\n| `@tanstack/react-query.gen.ts` | Query options + mutation configs                  | React integration        | ✅ Yes — primary way to use the API |\n| `client/client.gen.ts`         | `createClient()` factory                          | HTTP engine internals    | ❌ No — internal                    |\n| `client/types.gen.ts`          | Client config + options types                     | Infrastructure types     | ❌ No — internal                    |\n| `client/utils.gen.ts`          | URL building, auth, serialization                 | HTTP plumbing            | ❌ No — internal                    |\n| `core/*.gen.ts`                | Auth, serializers, SSE, URL utils                 | Low-level primitives     | ❌ No — internal                    |\n\n**The golden rule:** Your React components should only import from `types.gen.ts`, `zod.gen.ts`, `@tanstack/react-query.gen.ts`, and occasionally `client.gen.ts` for configuration. Everything in `client/` and `core/` is internal infrastructure.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevlinduldulao%2Ftanstack-query-and-heyapi-demo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdevlinduldulao%2Ftanstack-query-and-heyapi-demo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevlinduldulao%2Ftanstack-query-and-heyapi-demo/lists"}