SWAGGER / OPENAPI → TANSTACK QUERY
스펙을 넣으면 타입 안전한 훅이 나옵니다.
Swagger 스펙만 지정하고 명령어 하나만 실행하면 완전한 타입의 TanStack Query 코드가 컨트롤러 단위 폴더로 생성됩니다. 여러분이 직접 만든 axios 인스턴스를 그대로 사용합니다.
npm i -D swagger-to-tanstack-query- Swagger 2.0
- OpenAPI 3.x
- TanStack Query v5
- 내 axios 그대로
- MIT
An OpenAPI getContact operation from swagger.json transforming into a typed getContact query function in contact/apis.ts.
모든 것이 타입으로 정의되고, 손으로 쓸 코드는 없습니다.
컨트롤러 단위 출력
OpenAPI 태그마다 폴더 하나씩 생성되며, 각 폴더는 types, apis, queries, mutations를 모두 갖춘 독립 단위입니다.
TanStack Query v5
GET / HEAD는 queryOptions로, POST / PUT / PATCH / DELETE는 useXxx mutation 훅으로 생성됩니다.
내 axios 그대로
baseURL, 인증, interceptor는 모두 여러분의 인스턴스에 그대로 둡니다. 생성된 코드는 그 인스턴스를 import할 뿐입니다.
응답 엔벨롭 벗기기
{ data, message, … } 전체가 아니라 내부 payload만 반환합니다. 제네릭 엔벨롭(envelope)을 통해 타입 안전하게 동작합니다.
타입이 적용된 에러
모든 훅의 error가 AxiosError<YourErrorType> 타입으로 지정되며, 훅 단위로 적용됩니다.
스펙에 충실한 타입
$ref, allOf / oneOf / anyOf, enum, nullable, 배열, 맵, binary → Blob까지 빠짐없이 반영합니다.
헤더 파라미터 & 업로드
in: header 파라미터는 axios config로 전달하고, multipart/form-data는 FormData로 조립합니다.
문서 보존
스펙의 summary와 @deprecated가 생성된 코드의 JSDoc으로 그대로 이어집니다.
Swagger 2.0 & OpenAPI 3.x
두 스펙 형식 모두 파싱해 동일한 타입 출력으로 정규화합니다.
안전한 식별자
예약어와 wire 이름 불일치(page-size, delete)도 올바르게 처리합니다.
스펙에서 훅까지, 세 단계면 됩니다.
- 01
설정 파일 추가
swagger-to-tanstack-query.config.json이 여러분의 스펙과 axios 인스턴스를 가리키도록 합니다.
swagger-to-tanstack-query.config.json json { "url": "https://api.example.com/v3/api-docs", "output": "./src/api", "client": { "path": "@/lib/axios", "name": "axiosInstance" }, "response": { "dataField": "data", "envelope": { "path": "@/lib/axios", "name": "CommonResponse" } }, "error": { "path": "@/lib/axios", "name": "ApiError" } } - 02
명령어 한 번 실행
타입이 완비된 클라이언트 전체를 컨트롤러 단위 폴더로 생성합니다.
terminal bash $ npm run codegen swagger-to-tanstack-query spec : https://api.example.com/v3/api-docs output : ./src/api client : axiosInstance from "@/lib/axios" generating... done. 13 controllers, 65 files. - 03
훅 사용하기
생성된 queryOptions를 useQuery에 바로 넣어 사용합니다.
ContactName.tsx tsx import { useQuery } from "@tanstack/react-query"; import { contactQueries } from "@/api/contact"; const { data } = useQuery( contactQueries.getContact({ contactId: 1 }), ); // ^? Detail | undefined
컨트롤러마다 폴더 하나씩.
contact 컨트롤러의 실제 생성 결과입니다 — types, api 함수, queryOptions, mutation 훅, 그리고 barrel 파일까지.
// contact/types.ts
/** Common API response envelope */
export interface Detail {
id?: number;
name: string;
status?: "ACTIVE" | "ARCHIVED" | "DELETED";
tags?: Array<Tag>;
}// contact/apis.ts
import { axiosInstance as client } from "@/lib/axios";
import type { CommonResponse } from "@/lib/axios";
import type { Detail, Create } from "./types";
/** Get contact details */
export const getContact = ({ contactId }: { contactId: number }) =>
client.get<CommonResponse<Detail>>(`/api/v1/contacts/${contactId}`).then((res) => res.data.data);
/** Create a contact */
export const createContact = ({ body }: { body: Create }) =>
client.post<CommonResponse<Create>>(`/api/v1/contacts`, body).then((res) => res.data.data);// contact/queries.ts
import { queryOptions } from "@tanstack/react-query";
import type { AxiosError } from "axios";
import type { ApiError } from "@/lib/axios";
import * as apis from "./apis";
export const contactQueries = {
getContact: (args: { contactId: number }) =>
queryOptions<Awaited<ReturnType<typeof apis.getContact>>, AxiosError<ApiError>>({
queryKey: ["contact", "getContact", args],
queryFn: () => apis.getContact(args),
}),
};// contact/mutations.ts
import { useMutation } from "@tanstack/react-query";
import type { UseMutationOptions } from "@tanstack/react-query";
import type { AxiosError } from "axios";
import type { ApiError } from "@/lib/axios";
import * as apis from "./apis";
import type { Create } from "./types";
/** Create a contact */
export const useCreateContact = (
options?: Omit<
UseMutationOptions<Awaited<ReturnType<typeof apis.createContact>>, AxiosError<ApiError>, { body: Create }>,
"mutationFn"
>,
) =>
useMutation({
mutationFn: (vars: { body: Create }) => apis.createContact(vars),
...options,
});// contact/index.ts
export * from "./types";
export * from "./apis";
export * from "./queries";
export * from "./mutations";