Skip to content

Commit

Permalink
Merge branch 'master' into v1.7.0-integration
Browse files Browse the repository at this point in the history
  • Loading branch information
markerikson committed Dec 10, 2021
2 parents 29d6f6d + cecda16 commit aad6d32
Show file tree
Hide file tree
Showing 62 changed files with 10,699 additions and 996 deletions.
6 changes: 4 additions & 2 deletions .codesandbox/ci.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@
"packages": [
"packages/toolkit",
"packages/rtk-query-graphql-request-base-query",
"packages/action-listener-middleware"
"packages/action-listener-middleware",
"packages/rtk-query-codegen-openapi"
],
"publishDirectory": {
"@reduxjs/toolkit": "packages/toolkit",
"@rtk-query/graphql-request-base-query": "packages/rtk-query-graphql-request-base-query",
"@rtk-incubator/action-listener-middleware": "packages/action-listener-middleware"
"@rtk-incubator/action-listener-middleware": "packages/action-listener-middleware",
"@rtk-query/codegen-openapi": "packages/rtk-query-codegen-openapi"
}
}
45 changes: 45 additions & 0 deletions .github/workflows/test-codegen.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions

name: RTK-Query OpenAPI Codegen Tests
defaults:
run:
working-directory: ./packages/rtk-query-codegen-openapi

on: [push, pull_request]

jobs:
changes:
name: Check for changes
runs-on: ubuntu-latest
outputs:
codegen: ${{ steps.filter.outputs.codegen }}
steps:
- uses: actions/checkout@v2
- uses: dorny/paths-filter@v2
id: filter
with:
filters: |
codegen:
- 'packages/rtk-query-codegen-openapi/**'
build:
needs: changes
if: ${{ needs.changes.outputs.codegen == 'true' }}

runs-on: ubuntu-latest

strategy:
matrix:
node-version: [12.x]

steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node }}
cache: 'yarn'

- run: yarn install
- run: yarn test
17 changes: 17 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,24 @@ defaults:
working-directory: ./packages/toolkit

jobs:
changes:
name: Check for changes
runs-on: ubuntu-latest
outputs:
toolkit: ${{ steps.filter.outputs.toolkit }}
steps:
- uses: actions/checkout@v2
- uses: dorny/paths-filter@v2
id: filter
with:
filters: |
toolkit:
- 'packages/toolkit/**'
build:
needs: changes
if: ${{ needs.changes.outputs.toolkit == 'true' }}

name: Lint, Test, Build & Pack on Node ${{ matrix.node }}

runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion docs/rtk-query/comparison.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ RTK Query has some unique API design aspects and capabilities that are worth con
- You can easily invalidate entities or patch existing query data (via `util.updateQueryData`) from middleware.
- RTK Query enables [streaming cache updates](./usage/streaming-updates.mdx), such as updating the initial fetched data as messages are received over a websocket, and has built in support for [optimistic updates](./usage/manual-cache-updates.mdx#optimistic-updates) as well.
- RTK Query ships a very tiny and flexible fetch wrapper: [`fetchBaseQuery`](./api/fetchBaseQuery.mdx). It's also very easy to [swap our client with your own](./usage/customizing-queries.mdx), such as using `axios`, `redaxios`, or something custom.
- RTK Query has [a (currently experimental) code-gen tool](https://github.com/rtk-incubator/rtk-query-codegen) that will take an OpenAPI spec or GraphQL schema and give you a typed API client, as well as provide methods for enhancing the generated client after the fact.
- RTK Query has [a (currently experimental) code-gen tool](https://github.com/reduxjs/redux-toolkit/tree/master/packages/rtk-query-codegen-openapi) that will take an OpenAPI spec or GraphQL schema and give you a typed API client, as well as provide methods for enhancing the generated client after the fact.

## Tradeoffs

Expand Down
174 changes: 123 additions & 51 deletions docs/rtk-query/usage/code-generation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,70 +14,142 @@ RTK Query's API and architecture is oriented around declaring API endpoints up f

We have early previews of code generation capabilities available as separate tools.

## GraphQL

We provide a [Plugin for GraphQL Codegen](https://www.graphql-code-generator.com/docs/plugins/typescript-rtk-query). You can find the documentation to that on the graphql-codegen homepage.

For a full example on how to use it, you can see [this example project](https://github.com/reduxjs/redux-toolkit/tree/master/examples/query/react/graphql-codegen).

## OpenAPI

We have a first version of a code generator from OpenAPI schemas over at [`rtk-incubator/rtk-query-codegen`](https://github.com/rtk-incubator/rtk-query-codegen).
We provide a package for RTK Query code generation from OpenAPI schemas. It is published as `@rtk-query/codegen-openapi` and you can find the source code at [`packages/rtk-query-codegen-openapi`](https://github.com/reduxjs/redux-toolkit/tree/master/packages/rtk-query-codegen-openapi).

### Usage

Create an empty api using `createApi` like

```ts no-transpile title="src/store/emptyApi.ts"

// Or from '@reduxjs/toolkit/query' if not using the auto-generated hooks
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'

// initialize an empty api service that we'll inject endpoints into later as needed
export const emptySplitApi = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: () => ({}),
})
```

Generate a config file (json, js or ts) with contents like

```ts no-transpile title="openapi-config.ts"

import { ConfigFile } from '@rtk-query/codegen-openapi'

const config: ConfigFile = {
schemaFile: 'https://petstore3.swagger.io/api/v3/openapi.json',
apiFile: './src/store/emptyApi.ts',
apiImport: 'emptySplitApi',
outputFile: './src/store/petApi.ts',
exportName: 'petApi',
hooks: true,
}

You can create an api by running:
export default config
```

and then call the code generator:

```bash
curl -o petstore.json https://petstore3.swagger.io/api/v3/openapi.json
npx @rtk-incubator/rtk-query-codegen-openapi --hooks petstore.json > petstore-api.generated.ts
npx @rtk-query/codegen-openapi openapi-config.ts
```

We recommend placing these generated types in one file that you do not modify (so you can constantly re-generate it when your API definition changes) and creating a second file to enhance it with additional info:
### Programmatic usage

```ts title="petstore-api.ts"
// file: petstore-api.generated.ts noEmit
export * from 'petstore-api.generated'
```ts no-transpile title="src/store/petApi.ts"

// file: petstoreApi.ts
import { api as generatedApi } from './petstore-api.generated'
import { generateEndpoints } from '@rtk-query/codegen-openapi'

export const api = generatedApi.enhanceEndpoints({
addTagTypes: ['Pet'],
endpoints: {
// basic notation: just specify properties to be overridden
getPetById: {
providesTags: (result, error, arg) => [{ type: 'Pet', id: arg.petId }],
},
findPetsByStatus: {
providesTags: (result) =>
// is result available?
result
? // successful query
[
{ type: 'Pet', id: 'LIST' },
...result.map((pet) => ({ type: 'Pet' as const, id: pet.id })),
]
: // an error occurred, but we still want to refetch this query when `{ type: 'Pet', id: 'LIST' }` is invalidated
[{ type: 'Pet', id: 'LIST' }],
const api = await generateEndpoints({
apiFile: './fixtures/emptyApi.ts',
schemaFile: resolve(__dirname, 'fixtures/petstore.json'),
filterEndpoints: ['getPetById', 'addPet'],
hooks: true,
})
```

### Config file options

#### Simple usage

```ts no-transpile
interface SimpleUsage {
apiFile: string
schemaFile: string
apiImport?: string
exportName?: string
argSuffix?: string
responseSuffix?: string
hooks?: boolean
outputFile: string
filterEndpoints?:
| string
| RegExp
| EndpointMatcherFunction
| Array<string | RegExp | EndpointMatcherFunction>
endpointOverrides?: EndpointOverrides[]
}

export type EndpointMatcherFunction = (
operationName: string,
operationDefinition: OperationDefinition
) => boolean
```
#### Filtering endpoints
If you only want to include a few endpoints, you can use the `filterEndpoints` config option to filter your endpoints.
```ts no-transpile title="openapi-config.ts"
const filteredConfig: ConfigFile = {
// ...
// should only have endpoints loginUser, placeOrder, getOrderById, deleteOrder
filterEndpoints: ['loginUser', /Order/],
}
```

#### Endpoint overrides

If an endpoint is generated as a mutation instead of a query or the other way round, you can override that:

```ts no-transpile title="openapi-config.ts"
const withOverride: ConfigFile = {
// ...
endpointOverrides: [
{
pattern: 'loginUser',
type: 'mutation',
},
// alternate notation: callback that gets passed in `endpoint` - you can freely modify the object here
addPet: (endpoint) => {
endpoint.invalidatesTags = (result) =>
result ? [{ type: 'Pet', id: result.id }] : []
],
}
```

#### Multiple output files

```ts no-transpile title="openapi-config.ts"
const config: ConfigFile = {
schemaFile: 'https://petstore3.swagger.io/api/v3/openapi.json',
apiFile: './src/store/emptyApi.ts',
outputFiles: {
'./src/store/user.ts': {
filterEndpoints: [/user/i],
},
updatePet: {
invalidatesTags: (result, error, arg) => [
{ type: 'Pet', id: arg.pet.id },
],
'./src/store/order.ts': {
filterEndpoints: [/order/i],
},
deletePet: {
invalidatesTags: (result, error, arg) => [{ type: 'Pet', id: arg.petId }],
'./src/store/pet.ts': {
filterEndpoints: [/pet/i],
},
},
})

export const {
useGetPetByIdQuery,
useFindPetsByStatusQuery,
useAddPetMutation,
useUpdatePetMutation,
useDeletePetMutation,
} = api
}
```

## GraphQL

There is a _very_ early WIP PR that [implements code generation based on a GraphQL spec](https://github.com/phryneas/graphql-code-generator/pull/1), and an open issue on the GraphQL Generator repo asking [if an RTK Query generator would be potentially useful](https://github.com/dotansimha/graphql-code-generator/issues/6085).
2 changes: 1 addition & 1 deletion docs/rtk-query/usage/customizing-queries.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ export const api = createApi({
getPosts: build.query<PostsResponse, void>({
query: () => ({ url: 'posts' }),
}),
getPost: build.query<PostsResponse, string>({
getPost: build.query<Post, string>({
query: (id) => ({ url: `post/${id}` }),
extraOptions: { maxRetries: 8 }, // You can override the retry behavior on each endpoint
}),
Expand Down
Loading

0 comments on commit aad6d32

Please sign in to comment.