gen:api (react-query)
axios 혹은 fetch를 사용하는 환경에서 사용가능합니다.
swagger 의 json 을 조회하여 타입정의와 api class, react-query 관련 모듈을 생성합니다.
- (axios) Calendar.api.ts
- (fetch) Calendar.api.ts
- @types/data-contracts.ts
- (axios) Calendar.query.ts
- (fetch) Calendar.query.ts
import instance from "@apis/_axios/instance";
import { HttpClient, RequestParams } from "../@http-client";
import { CalenderListType } from '../@types/data-contracts'
export class CalendarApi<
SecurityDataType = unknown,
> extends HttpClient<SecurityDataType> {
/**
* No description
*
* @tags calendar
* @name BusinessCalendarList
* @request GET:/v1/business/{business_id}/calendar/
* @secure
*/
businessCalendarList = (variables: {
businessId: number;
query: {
/** 끝 날짜 */
end_date: string;
/** 시작 날짜 */
start_date: string;
};
params?: RequestParams;
}) =>
this.request<CalenderListType[], any>({
path: `/v1/business/${variables.businessId}/calendar/`,
method: "GET",
query: variables.query,
secure: true,
format: "json",
...variables.params,
});
}
export const calendarApi = new CalendarApi({ instance })
import { fetchExtended } from "@/configs/fetch";
import { HttpClient, RequestParams } from "../@http-client";
import {
CalendarListDataType,
CalenderListType,
} from "../@types/data-contracts";
export class CalendarApi<
SecurityDataType = unknown,
> extends HttpClient<SecurityDataType> {
/**
* No description
*
* @tags calendar
* @name BusinessCalendarList
* @request GET:/v1/business/{business_id}/calendar/
* @secure
*/
businessCalendarList = (variables: {
businessId: number;
query: {
/** 끝 날짜 */
end_date: string;
/** 시작 날짜 */
start_date: string;
};
params?: RequestParams;
}) =>
this.request<CalenderListType[], any>({
path: `/v1/business/${variables.businessId}/calendar/`,
method: "GET",
query: variables.query,
secure: true,
format: "json",
...variables.params,
next: {
...variables.params?.next,
tags: [
`/v1`,
`/v1/business`,
`/v1/business/${variables.businessId}`,
`/v1/business/${variables.businessId}/calendar`,
],
},
});
}
export const calendarApi = new CalendarApi({ customFetch: fetchExtended });
export interface CalendarListDataType {
readonly id: number;
/**
* * `S` - 정산
* * `M` - 미팅
* * `C` - 계약
* * `R` - 반환
* * `D` - 일정
*/
readonly scheduleType: CalendarListDataScheduleTypeEnumType;
readonly status: string;
readonly title: string;
/** @format time */
readonly time: string;
}
export interface CalenderListType {
/** @format date */
readonly date: string;
readonly count: number;
readonly data: CalendarListDataType[];
}
import { useQuery } from "@tanstack/react-query";
import { AxiosError } from "axios";
import {
Parameter,
QueryHookParams,
RequestFnReturn,
} from "../@types/react-query-type";
import { calendarApi } from "./Calendar.api";
/\*\*
- QUERY_KEYS
\*/
export const QUERY_KEY_CALENDAR_API = {
BUSINESS_CALENDAR_LIST: (
variables?: Parameter<typeof calendarApi.businessCalendarList>,
) =>
["BUSINESS_CALENDAR_LIST", variables].filter(
(key) => typeof key !== "undefined",
),
};
/\*\*
- No description
-
- @tags calendar
- @name BusinessCalendarList
- @request GET:/v1/business/{business_id}/calendar/
- @secure \*/
export const useBusinessCalendarListQuery = <
TData = RequestFnReturn<typeof calendarApi.businessCalendarList>,
>(
params: QueryHookParams<
typeof calendarApi.businessCalendarList,
AxiosError<any>,
TData
>,
) => {
const queryKey = QUERY_KEY_CALENDAR_API.BUSINESS_CALENDAR_LIST(
params.variables,
);
return useQuery({
queryKey,
queryFn: () => calendarApi.businessCalendarList(params.variables),
...params?.options,
});
};
import { useQuery } from "@tanstack/react-query";
import {
Parameter,
QueryHookParams,
RequestFnReturn,
} from "../@types/react-query-type";
import { calendarApi } from "./Calendar.api";
/**
* QUERY_KEYS
*/
export const QUERY_KEY_CALENDAR_API = {
BUSINESS_CALENDAR_LIST: (
variables?: Parameter<typeof calendarApi.businessCalendarList>,
) =>
["BUSINESS_CALENDAR_LIST", variables].filter(
(key) => typeof key !== "undefined",
),
BUSINESS_CALENDAR_DAY_LIST: (
variables?: Parameter<typeof calendarApi.businessCalendarDayList>,
) =>
["BUSINESS_CALENDAR_DAY_LIST", variables].filter(
(key) => typeof key !== "undefined",
),
};
/**
* No description
*
* @tags calendar
* @name BusinessCalendarList
* @request GET:/v1/business/{business_id}/calendar/
* @secure */
export const useBusinessCalendarListQuery = <
TData = RequestFnReturn<typeof calendarApi.businessCalendarList>,
>(
params: QueryHookParams<typeof calendarApi.businessCalendarList, any, TData>,
) => {
const queryKey = QUERY_KEY_CALENDAR_API.BUSINESS_CALENDAR_LIST(
params.variables,
);
return useQuery({
queryKey,
queryFn: () => calendarApi.businessCalendarList(params.variables),
...params?.options,
});
};
Installation
- npm
- yarn
- pnpm
npm i -D @toktokhan-dev/cli @toktokhan-dev/cli-plugin-gen-api-react-query
yarn add -D @toktokhan-dev/cli @toktokhan-dev/cli-plugin-gen-api-react-query
pnpm add -D @toktokhan-dev/cli @toktokhan-dev/cli-plugin-gen-api-react-query
Run Script
command 를 별도로 입력하지 않으면 대화형으로 실행되어 등록되어있는 스크립트 중 선택하여 사용이 가능합니다.
npx tokript
command 를 입력하면 해당 스크립트가 바로 실행됩니다.
npx tokript gen:api
자주 사용될 수 있는 스크립트는 협업간 편의성을 위해 package.json
에 등록하는걸 권장 드립니다.
{
...
"scripts": {
"gen:api": "tokript gen:api",
}
}
- yarn
- npm
- pnpm
yarn run gen:api
npm run gen:api
pnpm run gen:api
Configuration
tok-cli.config.ts
에서 config 정의가 가능합니다.
import { genApi } from '@toktokhan-dev/cli-plugin-gen-api-react-query'
const config: RootConfig<{
plugins: [typeof genApi]
}> = {
plugins: [genApi],
'gen:api': {
...
},
}
export default config
swaggerSchemaUrl
- Required:
true
- Type:
string
- Default:
- Cli Option:
-
config
{
'gen:api': {
swaggerSchemaUrl: 'src/pages',
...
},
}
output
- Required:
true
- Type:
string
- Default:
src/generated/swagger
- Cli Option:
-
config
{
'gen:api': {
output: 'src/generated/swagger',
...
},
}
httpClientType
- Required:
true
- Type:
axios | fetch
- Default:
axios
- Cli Option:
-
config
{
'gen:api': {
httpClientType: axios, // or fetch,
...
},
}
instancePath
- Required:
true
- Type:
string
- Default:
@apis/_axios/instance
- Cli Option:
-
config
{
'gen:api': {
instancePath: '@apis/_axios/instance, // or @/configs/fetch/fetch-extend',
...
},
}
includeReactQuery
- Required:
false
- Type:
boolean
- Default:
true
- Cli Option:
-
config
{
'gen:api': {
includeReactQuery: true,
...
},
}
includeReactInfiniteQuery
- Required:
true
- Type:
boolean
- Default:
true
- Cli Option:
-
config
{
'gen:api': {
includeReactSuspenseQuery: true,
...
},
}
paginationSets
- Required:
false
- Type:
Array<{
/** api 의 queryParams key 에 keywords 가 포함되어 있는 항목만 생성됩니다. 키워드 배열은 AND 연산으로써 사용됩니다. */
keywords: string[]
/** InfiniteQuery 의 nextPage 와 nextPageParam 을 구하는 함수를 작성하기 위해 사용됩니다. */
nextKey: string
initialPageParam?:
| string
| ((param: {
apiInstanceName: string
functionName: string
pagination: { keywords: string[]; nextKey: string }
}) => string)
/** InfiniteQuery 의 nextPage 를 구하는 함수를 커스텀하기 위해 사용됩니다. */
getNextPage?:
| string
| ((param: {
apiInstanceName: string
functionName: string
pagination: { keywords: string[]; nextKey: string }
}) => string)
/** InfiniteQuery 의 nextPageParam 을 구하는 함수를 커스텀하기 위해 사용됩니다. */
getNextPageParam?:
| string
| ((param: {
apiInstanceName: string
functionName: string
pagination: { keywords: string[]; nextKey: string }
}) => string)
}>
- Default:
[
{
keywords: ['cursor'],
nextKey: 'cursor',
},
] - Cli Option:
-
config
{
'gen:api': {
paginationSets: [
{
keywords: ['cursor'],
},
{
keywords: ['offset', "limit"],
},
],
...
},
}
paginationSets[index]:
-
keywords: api 의 요청 query parameter 타입에 keywords 가 모두 포함되어 있는 경우에만 infiniteQuery 가 생성됩니다.
예를들어 keywords 값이
["cursor"]
로 설정되어있고,api/boards?cursor={string}
의 경우 cursor 가 keywords 에 포함되어 있기 때문에 infiniteQuery 가 생성됩니다.예를들어 keywords 값이
["offset", "limit"]
로 설정되어있고,api/boards?limit={number}
의 경우 offset 과 limit 중 limit 만 포함되어 있기 때문에 infiniteQuery 가 생성되지 않습니다. -
nextKey: InfiniteQuery 의 nextPageParam 을 구하는 함수를 작성하기 위해 사용됩니다. 아래와 같은 경우에서만 문제없이 사용가능합니다.
- pagination 된 api 요청의 응답값이
next
값을 가지고 있어야합니다. next
값은 다음 페이지를 가져오기 위한 url 이어야합니다.next
값은 queryParam 을 포함하고 있어야합니다.
useInfiniteQuery({
...
queryFn: ({ pageParam }) => {
return contractApi.businessContractBoardList({
...params?.variables,
// 다음 페이지를 가져오기 위한 query 에 nextKey 값을 넘겨줍니다.
query: { ...params?.variables?.query, cursor: pageParam },
});
},
getNextPageParam: (lastPage) => {
const params = lastPage.next ? new URL(lastPage.next).searchParams : null;
// 다음 pageParam 을 가져오기 위한 url 에서 nextKey queryParam 을 가져옵니다.
const cursor = params ? params.get("cursor") : null;
return cursor;
},
...params?.options,
});만약 api spec 이 위의 요건을 충족하지 않는다면,
getNextPage
와getNextPageParam
함수를 사용하여 InfiniteQuery 의 nextPage 와 nextPageParam 을 커스텀할 수 있습니다. - pagination 된 api 요청의 응답값이
-
getNextPage: useInfiniteQuery 에 넘겨지는 queryFn 함수를 커스텀하기 위해 사용됩니다.
getNextPage
가 설정되어있지 않다면,nextKey
의 queryParam 값을 사용합니다.example1: string 으로 설정하기// tok-cli.config.ts
{
'gen:api': {
paginationSets: [
{
keywords: ['cursor'],
getNextPage: `({ pageParam }) => { console.log(pageParam) }`
},
],
...
},
}
// output
useInfiniteQuery({
...
queryFn: ({ pageParam }) => {
console.log(pageParam)
},
...
});example1: 함수로 설정하기// tok-cli.config.ts
{
'gen:api': {
paginationSets: [
{
keywords: ['cursor'],
getNextPage: ({
apiInstanceName,
functionName,
pagination
}) =>
`({ pageParam }) => ${apiInstanceName}.${functionName}({
...params?.variables,
query: { ...params?.variables?.query, next: pageParam },
});`
},
],
...
},
}
// output
useInfiniteQuery({
...
queryFn: ({ pageParam }) => goodsApi.getList({
...params?.variables,
query: { ...params?.variables?.query, next: pageParam },
}),
...
}); -
getNextPageParam: useInfiniteQuery 에 넘겨지는 getNextPageParam 함수를 커스텀하기 위해 사용됩니다.
getNextPageParam
이 설정되어있지 않다면,nextKey
의 queryParam 값을 사용합니다.example1: string 으로 설정하기// tok-cli.config.ts
{
'gen:api': {
paginationSets: [
{
keywords: ['cursor'],
getNextPageParam: `(lastPage) => { return lastPage.next }`
},
],
...
},
}
// output
useInfiniteQuery({
...
getNextPageParam: (lastPage) => {
return lastPage.next
},
...
});example1: 함수로 설정하기// tok-cli.config.ts
{
'gen:api': {
paginationSets: [
{
keywords: ['cursor'],
getNextPageParam: ({
apiInstanceName,
functionName,
pagination
}) =>
`(lastPage) => {
console.log("${pagenation.keywords.join(",")}:", "That's Keywords")
return lastPage.next
}
`
},
],
...
},
}
// output
useInfiniteQuery({
...
getNextPageParam: (lastPage) => {
console.log("cursor:", "That's Keywords")
return lastPage.offset
}
...
}); -
initialPageParam: InfiniteQuery 의 넘겨지는 initialPageParam 을 커스텀하기 위해 사용됩니다.