Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Create a docs site #162

Merged
merged 7 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"recommendations": ["astro-build.astro-vscode"],
"unwantedRecommendations": []
}
335 changes: 1 addition & 334 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# OpenAPI React Query Codegen

> Node.js library that generates [React Query (also called TanStack Query)](https://tanstack.com/query) hooks based on an OpenAPI specification file.
> Code generator for creating [React Query (also known as TanStack Query)](https://tanstack.com/query) hooks based on your OpenAPI schema.

[![npm version](https://badge.fury.io/js/%407nohe%2Fopenapi-react-query-codegen.svg)](https://badge.fury.io/js/%407nohe%2Fopenapi-react-query-codegen)

Expand All @@ -10,336 +10,3 @@
- Generates custom functions that use React Query's `ensureQueryData` and `prefetchQuery` functions
- Generates query keys and functions for query caching
- Generates pure TypeScript clients generated by [@hey-api/openapi-ts](https://github.com/hey-api/openapi-ts)

## Installation

```
$ npm install -D @7nohe/openapi-react-query-codegen
```

Register the command to the `scripts` property in your package.json file.

```json
{
"scripts": {
"codegen": "openapi-rq -i ./petstore.yaml -c @hey-api/client-fetch"
}
}
```

You can also run the command without installing it in your project using the npx command.

```bash
$ npx --package @7nohe/openapi-react-query-codegen openapi-rq -i ./petstore.yaml -c @hey-api/client-fetch
```

## Usage

```
$ openapi-rq --help

Usage: openapi-rq [options]

Generate React Query code based on OpenAPI

Options:
-V, --version output the version number
-i, --input <value> OpenAPI specification, can be a path, url or string content (required)
-o, --output <value> Output directory (default: "openapi")
-c, --client <value> HTTP client to generate (choices: "@hey-api/client-fetch", "@hey-api/client-axios", default: "@hey-api/client-fetch")
--format <value> Process output folder with formatter? (choices: "biome", "prettier")
--lint <value> Process output folder with linter? (choices: "biome", "eslint")
--operationId Use operation ID to generate operation names?
--serviceResponse <value> Define shape of returned value from service calls (choices: "body", "response", default: "body")
--enums <value> Generate JavaScript objects from enum definitions? (choices: "javascript", "typescript")
--useDateType Use Date type instead of string for date types for models, this will not convert the data to a Date object
--debug Run in debug mode?
--noSchemas Disable generating JSON schemas
--schemaType <value> Type of JSON schema [Default: 'json'] (choices: "form", "json")
--pageParam <value> Name of the query parameter used for pagination (default: "page")
--nextPageParam <value> Name of the response parameter used for next page (default: "nextPage")
--initialPageParam <value> Initial page value to query (default: "initialPageParam")
-h, --help display help for command
```

### Example Usage

#### Command

```
$ openapi-rq -i ./petstore.yaml
```

#### Output directory structure

```
- openapi
- queries
- index.ts <- main file that exports common types, variables, and queries. Does not export suspense or prefetch hooks
- common.ts <- common types
- ensureQueryData.ts <- generated ensureQueryData functions
- queries.ts <- generated query hooks
- suspenses.ts <- generated suspense hooks
- prefetch.ts <- generated prefetch functions learn more about prefetching in in link below
- requests <- output code generated by @hey-api/openapi-ts
```

- [Prefetching docs](https://tanstack.com/query/latest/docs/framework/react/guides/advanced-ssr#prefetching-and-dehydrating-data)

#### In your app

##### Using the generated hooks

```tsx
// App.tsx
import { useFindPets } from "../openapi/queries";
function App() {
const { data } = useFindPets();

return (
<div className="App">
<h1>Pet List</h1>
<ul>{data?.map((pet) => <li key={pet.id}>{pet.name}</li>)}</ul>
</div>
);
}

export default App;
```

##### Using the generated typescript client

```tsx
import { useQuery } from "@tanstack/react-query";
import { findPets } from "../openapi/requests/services.gen";
import { useFindPetsKey } from "../openapi/queries";

function App() {
// You can still use the auto-generated query key
const { data } = useQuery({
queryKey: [useFindPetsKey],
queryFn: () => {
// Do something here
return findPets();
},
});

return <div className="App">{/* .... */}</div>;
}

export default App;
```

##### Using Suspense Hooks

```tsx
// App.tsx
import { useFindPetsSuspense } from "../openapi/queries/suspense";
function ChildComponent() {
const { data } = useFindPetsSuspense({
query: { tags: [], limit: 10 },
});

return <ul>{data?.map((pet, index) => <li key={pet.id}>{pet.name}</li>)}</ul>;
}

function ParentComponent() {
return (
<>
<Suspense fallback={<>loading...</>}>
<ChildComponent />
</Suspense>
</>
);
}

function App() {
return (
<div className="App">
<h1>Pet List</h1>
<ParentComponent />
</div>
);
}

export default App;
```

##### Using Mutation hooks

```tsx
// App.tsx
import { useAddPet } from "../openapi/queries";

function App() {
const { mutate } = useAddPet();

const handleAddPet = () => {
mutate({ body: { name: "Fluffy" } });
};

return (
<div className="App">
<h1>Add Pet</h1>
<button onClick={handleAddPet}>Add Pet</button>
</div>
);
}

export default App;
```

##### Invalidating queries after mutation

Invalidating queries after a mutation is important to ensure the cache is updated with the new data. This is done by calling the `queryClient.invalidateQueries` function with the query key used by the query hook.

Learn more about invalidating queries [here](https://tanstack.com/query/latest/docs/framework/react/guides/query-invalidation).

To ensure the query key is created the same way as the query hook, you can use the query key function exported by the generated query hooks.

```tsx
import {
useFindPetsByStatus,
useAddPet,
UseFindPetsByStatusKeyFn,
} from "../openapi/queries";

// App.tsx
function App() {
const [status, setStatus] = React.useState(["available"]);
const { data } = useFindPetsByStatus({ status });
const { mutate } = useAddPet({
onSuccess: () => {
queryClient.invalidateQueries({
// Call the query key function to get the query key
// This is important to ensure the query key is created the same way as the query hook
// This insures the cache is invalidated correctly and is typed correctly
queryKey: [UseFindPetsByStatusKeyFn({
status
})],
});
},
});

return (
<div className="App">
<h1>Pet List</h1>
<ul>{data?.map((pet) => <li key={pet.id}>{pet.name}</li>)}</ul>
<button
onClick={() => {
mutate({ name: "Fluffy", status: "available" });
}}
>
Add Pet
</button>
</div>
);
}

export default App;
```

##### Using Infinite Query hooks

This feature will generate a function in infiniteQueries.ts when the name specified by the `pageParam` option exists in the query parameters and the name specified by the `nextPageParam` option exists in the response.

The `initialPageParam` option can be specified to set the intial page to load, defaults to 1. The `nextPageParam` supports dot notation for nested values (i.e. `meta.next`).

Example Schema:

```yml
paths:
/paginated-pets:
get:
description: |
Returns paginated pets from the system that the user has access to
operationId: findPaginatedPets
parameters:
- name: page
in: query
description: page number
required: false
schema:
type: integer
format: int32
- name: tags
in: query
description: tags to filter by
required: false
style: form
schema:
type: array
items:
type: string
- name: limit
in: query
description: maximum number of results to return
required: false
schema:
type: integer
format: int32
responses:
'200':
description: pet response
content:
application/json:
schema:
type: object
properties:
pets:
type: array
items:
$ref: '#/components/schemas/Pet'
nextPage:
type: integer
format: int32
minimum: 1
```

Usage of Generated Hooks:

```ts
import { useFindPaginatedPetsInfinite } from "@/openapi/queries/infiniteQueries";

const { data, fetchNextPage } = useFindPaginatedPetsInfinite({
query: { tags: [], limit: 10 }
});
```

## Development

### Install dependencies

```bash
pnpm install
```

### Run tests
```bash
pnpm test
```

### Run linter
```bash
pnpm lint
```

### Run linter and fix
```bash
pnpm lint:fix
```

### Update snapshots
```bash
pnpm snapshot
```

### Build example and validate generated code

```bash
npm run build && pnpm --filter @7nohe/react-app generate:api && pnpm --filter @7nohe/react-app test:generated
```

## License

MIT
5 changes: 2 additions & 3 deletions biome.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/1.7.2/schema.json",
"$schema": "https://biomejs.dev/schemas/1.9.3/schema.json",
"organizeImports": {
"enabled": true
},
Expand All @@ -12,8 +12,7 @@
"examples/nextjs-app/openapi",
"examples/nextjs-app/.next",
"examples/tanstack-router-app/openapi",
"examples/tanstack-router-app/src/routeTree.gen.ts",
".vscode"
"examples/tanstack-router-app/src/routeTree.gen.ts"
]
},
"linter": {
Expand Down
21 changes: 21 additions & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# build output
dist/
# generated types
.astro/

# dependencies
node_modules/

# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*


# environment variables
.env
.env.production

# macOS-specific files
.DS_Store
Loading