skills/doyajin174/api-contract-design/SKILL.md
Design APIs using schema-first approach with OpenAPI/Swagger. Use when creating new APIs, documenting existing ones, or when frontend/backend teams need to work in parallel. Covers OpenAPI spec, validation, and code generation.
npx skillsauth add aiskillstore/marketplace api-contract-designInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
OpenAPI(Swagger) 기반 스키마 우선 API 설계 스킬입니다.
"코드보다 계약(Contract)이 먼저다." "프론트엔드와 백엔드가 동시에 개발할 수 있게 API를 먼저 정의한다."
| 접근법 | 장점 | 단점 | |--------|------|------| | Schema-First (권장) | 병렬 개발 가능, 명확한 계약 | 초기 설계 시간 필요 | | Code-First | 빠른 시작 | 문서와 코드 불일치 위험 |
openapi.yamlopenapi: 3.1.0
info:
title: My API
version: 1.0.0
description: API for My Application
servers:
- url: https://api.example.com/v1
description: Production
- url: http://localhost:3000/api
description: Development
paths:
/users:
get:
summary: Get all users
operationId: getUsers
tags:
- Users
parameters:
- name: page
in: query
schema:
type: integer
default: 1
- name: limit
in: query
schema:
type: integer
default: 20
maximum: 100
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/UserListResponse'
'401':
$ref: '#/components/responses/Unauthorized'
post:
summary: Create a new user
operationId: createUser
tags:
- Users
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUserRequest'
responses:
'201':
description: User created
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'400':
$ref: '#/components/responses/BadRequest'
'409':
description: Email already exists
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/users/{userId}:
get:
summary: Get user by ID
operationId: getUserById
tags:
- Users
parameters:
- name: userId
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'404':
$ref: '#/components/responses/NotFound'
components:
schemas:
User:
type: object
required:
- id
- email
- name
- createdAt
properties:
id:
type: string
format: uuid
email:
type: string
format: email
name:
type: string
minLength: 1
maxLength: 100
avatarUrl:
type: string
format: uri
nullable: true
createdAt:
type: string
format: date-time
updatedAt:
type: string
format: date-time
CreateUserRequest:
type: object
required:
- email
- name
- password
properties:
email:
type: string
format: email
name:
type: string
minLength: 1
maxLength: 100
password:
type: string
minLength: 8
UserListResponse:
type: object
required:
- data
- pagination
properties:
data:
type: array
items:
$ref: '#/components/schemas/User'
pagination:
$ref: '#/components/schemas/Pagination'
Pagination:
type: object
required:
- page
- limit
- total
- totalPages
properties:
page:
type: integer
limit:
type: integer
total:
type: integer
totalPages:
type: integer
Error:
type: object
required:
- code
- message
properties:
code:
type: string
message:
type: string
details:
type: object
responses:
BadRequest:
description: Bad request
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Unauthorized:
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
NotFound:
description: Resource not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
security:
- BearerAuth: []
api/
├── openapi.yaml # 메인 스펙
├── paths/ # 엔드포인트별 분리
│ ├── users.yaml
│ ├── posts.yaml
│ └── auth.yaml
├── schemas/ # 스키마 분리
│ ├── user.yaml
│ ├── post.yaml
│ └── common.yaml
└── generated/ # 자동 생성 코드
├── types.ts
└── client.ts
# api/paths/users.yaml
/users:
get:
$ref: '../operations/users/getUsers.yaml'
post:
$ref: '../operations/users/createUser.yaml'
# api/openapi.yaml
paths:
/users:
$ref: './paths/users.yaml#/~1users'
npm install -D openapi-typescript
# 타입 생성
npx openapi-typescript ./api/openapi.yaml -o ./src/types/api.ts
import type { paths, components } from './types/api';
type User = components['schemas']['User'];
type CreateUserRequest = components['schemas']['CreateUserRequest'];
// API 응답 타입
type GetUsersResponse = paths['/users']['get']['responses']['200']['content']['application/json'];
npm install openapi-fetch
// lib/api-client.ts
import createClient from 'openapi-fetch';
import type { paths } from './types/api';
export const api = createClient<paths>({
baseUrl: process.env.NEXT_PUBLIC_API_URL,
});
// 사용
const { data, error } = await api.GET('/users', {
params: {
query: { page: 1, limit: 20 },
},
});
const { data: user } = await api.POST('/users', {
body: {
email: '[email protected]',
name: 'John',
password: 'password123',
},
});
npm install -D orval
// orval.config.ts
export default {
api: {
input: './api/openapi.yaml',
output: {
mode: 'tags-split',
target: './src/api',
schemas: './src/api/schemas',
client: 'react-query',
},
},
};
// 스키마에서 Zod 스키마 생성
import { z } from 'zod';
// OpenAPI 스펙 기반 Zod 스키마
export const CreateUserRequestSchema = z.object({
email: z.string().email(),
name: z.string().min(1).max(100),
password: z.string().min(8),
});
// API 라우트에서 검증
export async function POST(request: Request) {
const body = await request.json();
const result = CreateUserRequestSchema.safeParse(body);
if (!result.success) {
return Response.json(
{ code: 'VALIDATION_ERROR', message: result.error.message },
{ status: 400 }
);
}
// result.data는 타입 안전
const user = await createUser(result.data);
return Response.json(user, { status: 201 });
}
npm install swagger-ui-react
// app/api-docs/page.tsx
'use client';
import SwaggerUI from 'swagger-ui-react';
import 'swagger-ui-react/swagger-ui.css';
export default function ApiDocs() {
return <SwaggerUI url="/api/openapi.yaml" />;
}
npm install @scalar/nextjs-api-reference
// app/api-docs/page.tsx
import { ApiReference } from '@scalar/nextjs-api-reference';
export default function ApiDocs() {
return (
<ApiReference
configuration={{
spec: {
url: '/api/openapi.yaml',
},
}}
/>
);
}
servers:
- url: https://api.example.com/v1
- url: https://api.example.com/v2
parameters:
- name: API-Version
in: header
schema:
type: string
enum: ['2024-01-01', '2024-06-01']
1. API 스펙 작성 (openapi.yaml)
↓
2. 팀 리뷰 (PR)
↓
3. 타입 생성 (openapi-typescript)
↓
4. 병렬 개발
- Frontend: Mock 서버로 개발
- Backend: 스펙 기반 구현
↓
5. 통합 테스트
# Prism (Stoplight)
npm install -D @stoplight/prism-cli
# Mock 서버 실행
npx prism mock ./api/openapi.yaml
development
Apple Human Interface Guidelines for content display components. Use this skill when the user asks about charts component, collection view, image view, web view, color well, image well, activity view, lockup, data visualization, content display, displaying images, rendering web content, color pickers, or presenting collections of items in Apple apps. Also use when the user says how should I display charts, what's the best way to show images, should I use a web view, how do I build a grid of items, what component shows media, or how do I present a share sheet. Cross-references: hig-foundations for color/typography/accessibility, hig-patterns for data visualization patterns, hig-components-layout for structural containers, hig-platforms for platform-specific component behavior.
tools
Automate HelpDesk tasks via Rube MCP (Composio): list tickets, manage views, use canned responses, and configure custom fields. Always search tools first for current schemas.
testing
Expert Haskell engineer specializing in advanced type systems, pure functional design, and high-reliability software. Use PROACTIVELY for type-level programming, concurrency, and architecture guidance.
tools
GraphQL gives clients exactly the data they need - no more, no less. One endpoint, typed schema, introspection. But the flexibility that makes it powerful also makes it dangerous. Without proper controls, clients can craft queries that bring down your server. This skill covers schema design, resolvers, DataLoader for N+1 prevention, federation for microservices, and client integration with Apollo/urql. Key insight: GraphQL is a contract. The schema is the API documentation. Design it carefully.