Skip to main content
api-integration Use when integrating Apidog + OpenAPI specifications with your React app. Covers MCP server setup, type generation, and query layer integration. Use when setting up API clients, generating types from OpenAPI, or integrating with Apidog MCP.
설치로 이동 Skills Marketplace 커뮤니티가 만든 AI 스킬을 발견하고 탐색하세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/MadAppGang/claude-code --skill api-integration명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
Zip 다운로드 다운로드 중... 관련 직업
SOC
name api-integration description Use when integrating Apidog + OpenAPI specifications with your React app. Covers MCP server setup, type generation, and query layer integration. Use when setting up API clients, generating types from OpenAPI, or integrating with Apidog MCP. updated "2026-01-20T00:00:00.000Z" keywords api, openapi, apidog, mcp, type-generation, http-client, axios, zod
API Integration (Apidog + MCP)
Integrate OpenAPI specifications with your frontend using Apidog MCP for single source of truth.
Goal
The AI agent always uses the latest API specification to generate types and implement features correctly.
Architecture
Apidog (or Backend)
→ OpenAPI 3.0/3.1 Spec
→ MCP Server (apidog-mcp-server)
→ AI Agent reads spec
→ Generate TypeScript types
→ TanStack Query hooks
→ React Components
Process
1. Expose OpenAPI from Apidog
Option A: Remote URL
Export OpenAPI spec from Apidog
Host at a URL (e.g., https://api.example.com/openapi.json)
Option B: Local File
Export OpenAPI spec to file
Place in project (e.g., ./api-spec/openapi.json)
2. Wire MCP Server
{
"mcpServers"
:
{
"API specification"
:
{
"command"
:
"npx"
,
"args"
:
[
"-y"
,
"apidog-mcp-server@latest"
,
"--oas=https://api.example.com/openapi.json"
]
}
}
}
{
"mcpServers" : {
"API specification" : {
"command" : "npx" ,
"args" : [
"-y" ,
"apidog-mcp-server@latest" ,
"--oas=./api-spec/openapi.json"
]
}
}
}
{
"mcpServers" : {
"Main API" : {
"command" : "npx" ,
"args" : [ "-y" , "apidog-mcp-server@latest" , "--oas=https://api.main.com/openapi.json" ]
} ,
"Auth API" : {
"command" : "npx" ,
"args" : [ "-y" , "apidog-mcp-server@latest" , "--oas=https://api.auth.com/openapi.json" ]
}
}
}
3. Generate Types & Client Create /src/api directory for all API-related code:
/src/api/
├── types.ts # Generated from OpenAPI
├── client.ts # HTTP client (axios/fetch)
├── queries/ # TanStack Query hooks
│ ├── users.ts
│ ├── posts.ts
│ └── ...
└── mutations/ # TanStack Mutation hooks
├── users.ts
├── posts.ts
└── ...
Option A: Hand-Written Types (Lightweight)
import { z } from 'zod'
export const UserSchema = z.object ({
id : z.string (),
name : z.string (),
email : z.string ().email (),
createdAt : z.string ().datetime (),
})
export type User = z.infer <typeof UserSchema >
export const CreateUserSchema = UserSchema .omit ({ id : true , createdAt : true })
export type CreateUserDTO = z.infer <typeof CreateUserSchema >
Option B: Code Generation (Recommended for large APIs)
pnpm add -D openapi-typescript
npx openapi-typescript https://api.example.com/openapi.json -o src/api/types.ts
pnpm add -D orval
npx orval --input https://api.example.com/openapi.json --output src/api
4. Create HTTP Client
import axios from 'axios'
import createAuthRefreshInterceptor from 'axios-auth-refresh'
export const apiClient = axios.create ({
baseURL : import .meta .env .VITE_API_URL ,
headers : {
'Content-Type' : 'application/json' ,
},
})
apiClient.interceptors .request .use ((config ) => {
const token = localStorage .getItem ('accessToken' )
if (token) {
config.headers .Authorization = `Bearer ${token} `
}
return config
})
const refreshAuth = async (failedRequest : any ) => {
try {
const refreshToken = localStorage .getItem ('refreshToken' )
const response = await axios.post ('/auth/refresh' , { refreshToken })
const { accessToken } = response.data
localStorage .setItem ('accessToken' , accessToken)
failedRequest.response .config .headers .Authorization = `Bearer ${accessToken} `
return Promise .resolve ()
} catch (error) {
localStorage .removeItem ('accessToken' )
localStorage .removeItem ('refreshToken' )
window .location .href = '/login'
return Promise .reject (error)
}
}
createAuthRefreshInterceptor (apiClient, refreshAuth, {
statusCodes : [401 ],
pauseInstanceWhileRefreshing : true ,
})
5. Build Query Layer Feature-based query organization:
import { queryOptions } from '@tanstack/react-query'
import { apiClient } from '../client'
import { User , UserSchema } from '../types'
export const usersKeys = {
all : ['users' ] as const ,
lists : () => [...usersKeys.all , 'list' ] as const ,
list : (filters : string ) => [...usersKeys.lists (), { filters }] as const ,
details : () => [...usersKeys.all , 'detail' ] as const ,
detail : (id : string ) => [...usersKeys.details (), id] as const ,
}
async function fetchUsers ( ): Promise <User []> {
const response = await apiClient.get ('/users' )
return z.array (UserSchema ).parse (response.data )
}
async function fetchUser (id : string ): Promise <User > {
const response = await apiClient.get (`/users/${id} ` )
return UserSchema .parse (response.data )
}
export function usersListQueryOptions ( ) {
return queryOptions ({
queryKey : usersKeys.lists (),
queryFn : fetchUsers,
staleTime : 30_000 ,
})
}
export function userQueryOptions (id : string ) {
return queryOptions ({
queryKey : usersKeys.detail (id),
queryFn : () => fetchUser (id),
staleTime : 60_000 ,
})
}
export function useUsers ( ) {
return useQuery (usersListQueryOptions ())
}
export function useUser (id : string ) {
return useQuery (userQueryOptions (id))
}
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { apiClient } from '../client'
import { CreateUserDTO , User , UserSchema } from '../types'
import { usersKeys } from '../queries/users'
async function createUser (data : CreateUserDTO ): Promise <User > {
const response = await apiClient.post ('/users' , data)
return UserSchema .parse (response.data )
}
export function useCreateUser ( ) {
const queryClient = useQueryClient ()
return useMutation ({
mutationFn : createUser,
onSuccess : (newUser ) => {
queryClient.setQueryData (usersKeys.detail (newUser.id ), newUser)
queryClient.invalidateQueries ({ queryKey : usersKeys.lists () })
},
})
}
Validation Strategy Always validate API responses:
import { z } from 'zod'
async function fetchUser (id : string ): Promise <User > {
const response = await apiClient.get (`/users/${id} ` )
try {
return UserSchema .parse (response.data )
} catch (error) {
console .error ('API response validation failed:' , error)
throw new Error ('Invalid API response format' )
}
}
const result = UserSchema .safeParse (response.data )
if (!result.success ) {
console .error ('Validation errors:' , result.error .errors )
throw new Error ('Invalid user data' )
}
return result.data
Error Handling import { QueryCache } from '@tanstack/react-query'
const queryCache = new QueryCache ({
onError : (error, query ) => {
if (axios.isAxiosError (error)) {
if (error.response ?.status === 404 ) {
toast.error ('Resource not found' )
} else if (error.response ?.status === 500 ) {
toast.error ('Server error. Please try again.' )
}
}
},
})
Best Practices
Single Source of Truth - OpenAPI spec via MCP is authoritative
Validate Responses - Use Zod schemas for runtime validation
Encapsulation - Keep all API details in /src/api
Type Safety - Export types from generated/hand-written schemas
Error Handling - Handle auth errors, network errors, validation errors
Query Key Factories - Hierarchical keys for flexible invalidation
Feature-Based Organization - Group queries/mutations by feature
Workflow with AI Agent
Agent reads latest OpenAPI spec via Apidog MCP
Agent generates or updates types in /src/api/types.ts
Agent implements queries following established patterns
Agent creates mutations with proper invalidation
Agent updates components to use new API hooks
Example: Full Feature Implementation
export const TodoSchema = z.object ({
id : z.string (),
text : z.string (),
completed : z.boolean (),
})
export type Todo = z.infer <typeof TodoSchema >
export const todosKeys = {
all : ['todos' ] as const ,
lists : () => [...todosKeys.all , 'list' ] as const ,
}
export function todosQueryOptions ( ) {
return queryOptions ({
queryKey : todosKeys.lists (),
queryFn : async () => {
const response = await apiClient.get ('/todos' )
return z.array (TodoSchema ).parse (response.data )
},
})
}
export function useCreateTodo ( ) {
const queryClient = useQueryClient ()
return useMutation ({
mutationFn : async (text : string ) => {
const response = await apiClient.post ('/todos' , { text })
return TodoSchema .parse (response.data )
},
onSuccess : () => {
queryClient.invalidateQueries ({ queryKey : todosKeys.lists () })
},
})
}
export function TaskList ( ) {
const { data : todos } = useQuery (todosQueryOptions ())
const createTodo = useCreateTodo ()
return (
<div >
{todos?.map(todo => <TodoItem key ={todo.id} {...todo } /> )}
<AddTodoForm onSubmit ={(text) => createTodo.mutate(text)} />
</div >
)
}
Related Skills
tanstack-query - Query and mutation patterns
tooling-setup - TypeScript configuration for generated types
core-principles - Project structure with /src/api directory