|
3 | 3 |
|
4 | 4 | # React Query |
5 | 5 |
|
6 | | -[Basketry generator](https://github.com/basketry/basketry) for generating React Query hooks. This parser can be coupled with any Basketry parser. |
| 6 | +[Basketry generator](https://basketry.io) for generating React Query queryOptions and hooks. This generator can be coupled with any Basketry parser. |
7 | 7 |
|
8 | 8 | ## Quick Start |
9 | 9 |
|
10 | | -// TODO |
| 10 | +### Installation |
| 11 | + |
| 12 | +```bash |
| 13 | +npm install @basketry/react-query |
| 14 | +``` |
| 15 | + |
| 16 | +### Getting Started |
| 17 | + |
| 18 | +1. **Create a Basketry configuration file** (`basketry.config.json`): |
| 19 | + |
| 20 | + ```json |
| 21 | + { |
| 22 | + "source": "openapi.json", |
| 23 | + "parser": "@basketry/openapi-3", |
| 24 | + "generators": ["@basketry/react-query"], |
| 25 | + "output": "./src/generated/react-query", |
| 26 | + "options": { |
| 27 | + "basketry": { |
| 28 | + "command": "npx basketry" |
| 29 | + }, |
| 30 | + "typescript": { |
| 31 | + "includeVersion": false |
| 32 | + }, |
| 33 | + "reactQuery": { |
| 34 | + "typesModule": "@your-api/types", // Path to generated TypeScript types |
| 35 | + "clientModule": "@your-api/http-client-sdk" // Path to generated HTTP client |
| 36 | + } |
| 37 | + } |
| 38 | + } |
| 39 | + ``` |
| 40 | + |
| 41 | +2. **Run Basketry** to generate the React Query hooks: |
| 42 | + |
| 43 | + ```bash |
| 44 | + npx basketry |
| 45 | + ``` |
| 46 | + |
| 47 | +3. **Set up your React Query provider** in your app: |
| 48 | + |
| 49 | + ```typescript |
| 50 | + import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; |
| 51 | + // Name of provider will depend on the name of the API service in your OpenAPI spec. |
| 52 | + import { BasketryExampleProvider } from './src/generated/context'; |
| 53 | + |
| 54 | + const queryClient = new QueryClient(); |
| 55 | + const httpClient = fetch; // or your custom fetch implementation |
| 56 | + |
| 57 | + function App() { |
| 58 | + return ( |
| 59 | + <QueryClientProvider client={queryClient}> |
| 60 | + <BasketryExampleProvider httpClient={httpClient}> |
| 61 | + {/* Your app components */} |
| 62 | + </BasketryExampleProvider> |
| 63 | + </QueryClientProvider> |
| 64 | + ); |
| 65 | + } |
| 66 | + ``` |
| 67 | + |
| 68 | +4. **Use the generated hooks** in your components: |
| 69 | + |
| 70 | + ```typescript |
| 71 | + import { useQuery } from '@tanstack/react-query'; |
| 72 | + import { getWidgetsQueryOptions } from './src/generated'; |
| 73 | + |
| 74 | + function WidgetList() { |
| 75 | + const { data, isLoading } = useQuery(getWidgetsQueryOptions()); |
| 76 | + |
| 77 | + if (isLoading) return <div>Loading...</div>; |
| 78 | + return <div>{data?.map(widget => <div key={widget.id}>{widget.name}</div>)}</div>; |
| 79 | + } |
| 80 | + ``` |
| 81 | +
|
| 82 | +### Basic Usage |
| 83 | +
|
| 84 | +This generator produces React Query compatible code with queryOptions functions that provide maximum flexibility: |
| 85 | +
|
| 86 | +```typescript |
| 87 | +// Using query options with React Query hooks |
| 88 | +import { useQuery, useSuspenseQuery } from '@tanstack/react-query'; |
| 89 | +import { getWidgetsQueryOptions } from './petstore'; // generated code |
| 90 | + |
| 91 | +function WidgetList() { |
| 92 | + // Basic usage |
| 93 | + const { data } = useQuery(getWidgetsQueryOptions()); |
| 94 | + |
| 95 | + // With parameters |
| 96 | + const { data: filtered } = useQuery( |
| 97 | + getWidgetsQueryOptions({ status: 'active' }) |
| 98 | + ); |
| 99 | + |
| 100 | + // With custom options |
| 101 | + const { data: cached } = useQuery({ |
| 102 | + ...getWidgetsQueryOptions(), |
| 103 | + staleTime: 5 * 60 * 1000, // 5 minutes |
| 104 | + }); |
| 105 | + |
| 106 | + return <div>{/* render widgets */}</div>; |
| 107 | +} |
| 108 | +``` |
| 109 | +
|
| 110 | +### Mutations |
| 111 | +
|
| 112 | +```typescript |
| 113 | +import { useMutation } from '@tanstack/react-query'; |
| 114 | +import { createWidgetMutationOptions } from './petstore'; // generated code |
| 115 | + |
| 116 | +function CreateWidget() { |
| 117 | + const mutation = useMutation(createWidgetMutationOptions()); |
| 118 | + |
| 119 | + const handleSubmit = (data: CreateWidgetInput) => { |
| 120 | + mutation.mutate(data, { |
| 121 | + onSuccess: (widget) => { |
| 122 | + console.log('Created widget:', widget); |
| 123 | + }, |
| 124 | + }); |
| 125 | + }; |
| 126 | + |
| 127 | + return <form>{/* form fields */}</form>; |
| 128 | +} |
| 129 | +``` |
| 130 | +
|
| 131 | +### Infinite Queries (Pagination) |
| 132 | +
|
| 133 | +For services with Relay-style pagination: |
| 134 | +
|
| 135 | +```typescript |
| 136 | +import { useInfiniteQuery } from '@tanstack/react-query'; |
| 137 | +import { getWidgetsInfiniteQueryOptions } from './petstore'; // generated code |
| 138 | + |
| 139 | +function InfiniteWidgetList() { |
| 140 | + const { |
| 141 | + data, |
| 142 | + fetchNextPage, |
| 143 | + hasNextPage, |
| 144 | + } = useInfiniteQuery(getWidgetsInfiniteQueryOptions()); |
| 145 | + |
| 146 | + return ( |
| 147 | + <div> |
| 148 | + {data?.pages.map(page => |
| 149 | + page.edges.map(({ node }) => ( |
| 150 | + <Widget key={node.id} data={node} /> |
| 151 | + )) |
| 152 | + )} |
| 153 | + <button onClick={() => fetchNextPage()} disabled={!hasNextPage}> |
| 154 | + Load More |
| 155 | + </button> |
| 156 | + </div> |
| 157 | + ); |
| 158 | +} |
| 159 | +``` |
| 160 | +
|
| 161 | +## Configuration |
| 162 | +
|
| 163 | +Add to your `basketry.config.json`: |
| 164 | +
|
| 165 | +```json |
| 166 | +``` |
| 167 | +
|
| 168 | +## Features |
| 169 | +
|
| 170 | +- **React Query Compatible**: Generates queryOptions and mutationOptions functions |
| 171 | +- **Type-Safe**: Full TypeScript support with proper type inference |
| 172 | +- **Flexible**: Use with any React Query hook (useQuery, useSuspenseQuery, etc.) |
| 173 | +- **SSR Ready**: Service getters work outside React components |
| 174 | +- **Backward Compatible**: Legacy hooks are deprecated but still available |
| 175 | +- **Relay Pagination**: Built-in support for cursor-based pagination |
| 176 | +- **Error Handling**: Automatic error aggregation with CompositeError |
11 | 177 |
|
12 | 178 | --- |
13 | 179 |
|
14 | | -## For contributors: |
| 180 | +## For contributors |
15 | 181 |
|
16 | 182 | ### Run this project |
17 | 183 |
|
18 | | -1. Install packages: `npm ci` |
19 | | -1. Build the code: `npm run build` |
20 | | -1. Run it! `npm start` |
| 184 | +1. Install packages: `npm ci` |
| 185 | +1. Build the code: `npm run build` |
| 186 | +1. Run it! `npm start` |
21 | 187 |
|
22 | 188 | Note that the `lint` script is run prior to `build`. Auto-fixable linting or formatting errors may be fixed by running `npm run fix`. |
23 | 189 |
|
24 | 190 | ### Create and run tests |
25 | 191 |
|
26 | | -1. Add tests by creating files with the `.test.ts` suffix |
27 | | -1. Run the tests: `npm t` |
28 | | -1. Test coverage can be viewed at `/coverage/lcov-report/index.html` |
| 192 | +1. Add tests by creating files with the `.test.ts` suffix |
| 193 | +1. Run the tests: `npm test` |
| 194 | +1. Test coverage can be viewed at `/coverage/lcov-report/index.html` |
29 | 195 |
|
30 | 196 | ### Publish a new package version |
31 | 197 |
|
|
0 commit comments