commit 06d3ba93172981e0e5cd2d44f0d87072f8f24d27 Author: Zhongwei Li Date: Sat Nov 29 17:52:44 2025 +0800 Initial commit diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..f15d9f1 --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,11 @@ +{ + "name": "api-contract-sync-manager", + "description": "Validate and synchronize API contracts (OpenAPI, GraphQL) with implementation, detect breaking changes, and generate client code", + "version": "1.0.0", + "author": { + "name": "Anand Tyagi" + }, + "skills": [ + "./skills/api-contract-sync" + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..7b92e57 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# api-contract-sync-manager + +Validate and synchronize API contracts (OpenAPI, GraphQL) with implementation, detect breaking changes, and generate client code diff --git a/plugin.lock.json b/plugin.lock.json new file mode 100644 index 0000000..cb19bd1 --- /dev/null +++ b/plugin.lock.json @@ -0,0 +1,53 @@ +{ + "$schema": "internal://schemas/plugin.lock.v1.json", + "pluginId": "gh:ananddtyagi/claude-code-marketplace:plugins/api-contract-sync-manager", + "normalized": { + "repo": null, + "ref": "refs/tags/v20251128.0", + "commit": "9ab46a54cbfd5a79f506f8bc92211f85a1b0b665", + "treeHash": "df1c0b69948435393f2db3cbcb5900fd9d4bb61087a8fbb8ce965e6b83e587c5", + "generatedAt": "2025-11-28T10:13:40.756845Z", + "toolVersion": "publish_plugins.py@0.2.0" + }, + "origin": { + "remote": "git@github.com:zhongweili/42plugin-data.git", + "branch": "master", + "commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390", + "repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data" + }, + "manifest": { + "name": "api-contract-sync-manager", + "description": "Validate and synchronize API contracts (OpenAPI, GraphQL) with implementation, detect breaking changes, and generate client code", + "version": "1.0.0" + }, + "content": { + "files": [ + { + "path": "README.md", + "sha256": "b4bd7aafeccd80b51a755e97801960f31a64fcd8dd6de8509ef015006bd81047" + }, + { + "path": ".claude-plugin/plugin.json", + "sha256": "8e01a362992351bebeaea921e5eefc2636b692c96148a819ef160a726afb9563" + }, + { + "path": "skills/api-contract-sync/EXAMPLES.md", + "sha256": "ef471be859624653ec009620cf25153e59340646b5ff779d4bec09b01b53f077" + }, + { + "path": "skills/api-contract-sync/REFERENCE.md", + "sha256": "58e8465f2a0509a8fd2924ccb58dc51bde09de653a728468a2b2a25a4ce214bd" + }, + { + "path": "skills/api-contract-sync/SKILL.md", + "sha256": "8b1786923d36c1877cbe4e1f2a52e8bbee680b4ba78972d38f85607fd7734498" + } + ], + "dirSha256": "df1c0b69948435393f2db3cbcb5900fd9d4bb61087a8fbb8ce965e6b83e587c5" + }, + "security": { + "scannedAt": null, + "scannerVersion": null, + "flags": [] + } +} \ No newline at end of file diff --git a/skills/api-contract-sync/EXAMPLES.md b/skills/api-contract-sync/EXAMPLES.md new file mode 100644 index 0000000..31c741d --- /dev/null +++ b/skills/api-contract-sync/EXAMPLES.md @@ -0,0 +1,1005 @@ +# API Contract Sync Manager - Examples + +Real-world usage scenarios and code samples for common API contract synchronization tasks. + +## Example 1: Validating an OpenAPI Spec Against Express.js Routes + +### Scenario +You have an Express.js backend with an OpenAPI spec, and you want to verify that all documented endpoints are actually implemented. + +### OpenAPI Spec (`openapi.yaml`) +```yaml +openapi: 3.0.0 +info: + title: User API + version: 1.0.0 +paths: + /api/users: + get: + summary: List users + operationId: listUsers + responses: + '200': + description: Success + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' + post: + summary: Create user + operationId: createUser + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateUserRequest' + responses: + '201': + description: Created + /api/users/{id}: + get: + summary: Get user by ID + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: Success + delete: + summary: Delete user + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '204': + description: Deleted +components: + schemas: + User: + type: object + properties: + id: + type: string + email: + type: string + name: + type: string + CreateUserRequest: + type: object + required: + - email + properties: + email: + type: string + name: + type: string +``` + +### Express.js Implementation (`routes/users.js`) +```javascript +const express = require('express'); +const router = express.Router(); + +// GET /api/users - List users +router.get('/users', async (req, res) => { + const users = await db.users.findAll(); + res.json(users); +}); + +// POST /api/users - Create user +router.post('/users', async (req, res) => { + const { email, name } = req.body; + const user = await db.users.create({ email, name }); + res.status(201).json(user); +}); + +// GET /api/users/:id - Get user by ID +router.get('/users/:id', async (req, res) => { + const user = await db.users.findById(req.params.id); + if (!user) { + return res.status(404).json({ error: 'User not found' }); + } + res.json(user); +}); + +// Note: DELETE /api/users/:id is NOT implemented + +module.exports = router; +``` + +### Usage +**User asks**: "Does our Express API implementation match the OpenAPI spec?" + +**Analysis Process**: +1. Read and parse `openapi.yaml` +2. Extract endpoints: + - GET /api/users + - POST /api/users + - GET /api/users/{id} + - DELETE /api/users/{id} +3. Search for Express route handlers using Grep +4. Match implementations to spec + +**Result Report**: +``` +API Implementation Coverage Report +=================================== + +✓ GET /api/users - IMPLEMENTED + File: routes/users.js:5 + +✓ POST /api/users - IMPLEMENTED + File: routes/users.js:11 + +✓ GET /api/users/:id - IMPLEMENTED + File: routes/users.js:18 + +✗ DELETE /api/users/:id - MISSING + Documented in spec but not found in implementation + Expected location: routes/users.js + +Coverage: 75% (3/4 endpoints) + +Recommendation: +Implement the missing DELETE endpoint or remove it from the spec if it's not needed. +``` + +--- + +## Example 2: Detecting Breaking Changes Between API Versions + +### Scenario +You're preparing to release API v2 and need to identify all breaking changes from v1 to inform client teams. + +### API v1 Spec +```yaml +paths: + /api/users: + post: + requestBody: + content: + application/json: + schema: + type: object + required: + - email + properties: + email: + type: string + name: + type: string + responses: + '201': + content: + application/json: + schema: + $ref: '#/components/schemas/User' + +components: + schemas: + User: + type: object + properties: + id: + type: string + email: + type: string + name: + type: string + phone: + type: string +``` + +### API v2 Spec +```yaml +paths: + /api/users: + post: + requestBody: + content: + application/json: + schema: + type: object + required: + - email + - role # NEW: Required field added + properties: + email: + type: string + name: + type: string + role: + type: string + enum: + - admin + - member + responses: + '201': + content: + application/json: + schema: + $ref: '#/components/schemas/User' + +components: + schemas: + User: + type: object + properties: + id: + type: string + email: + type: string + name: + type: string + role: + type: string + # phone removed +``` + +### Usage +**User asks**: "Compare API v1 and v2 specs and tell me about breaking changes" + +**Breaking Changes Detected**: +``` +API v2.0 Breaking Changes Report +================================= + +🔴 BREAKING CHANGES (2) + +1. POST /api/users - Required field added + Field: role + Type: string (enum: admin, member) + Impact: All existing POST /api/users requests will fail validation + Migration: Add "role" field to request body + +2. User schema - Property removed + Property: phone + Impact: Clients reading user.phone will get undefined + Migration: Remove phone field usage or use alternative data source + +⚠️ NON-BREAKING CHANGES (1) + +1. User schema - Property added + Property: role + Type: string + Impact: Safe - clients will simply ignore this new field + +RECOMMENDATION: This requires a MAJOR version bump (v1 → v2) + +Client Migration Required: +- Update all POST /api/users calls to include role field +- Remove dependencies on user.phone property +- Update TypeScript interfaces if using typed clients +``` + +**Generated Migration Guide**: +```markdown +# Migration Guide: API v1 → v2 + +## Breaking Changes + +### 1. User Creation Requires Role +POST /api/users now requires a `role` field. + +**Before (v1)**: +```json +{ + "email": "user@example.com", + "name": "John Doe" +} +``` + +**After (v2)**: +```json +{ + "email": "user@example.com", + "name": "John Doe", + "role": "member" +} +``` + +**Action Required**: Update all user creation calls to include role. + +### 2. User Phone Field Removed +The `phone` field has been removed from the User object. + +**Before (v1)**: +```javascript +const phone = user.phone; +``` + +**After (v2)**: +```javascript +// phone field no longer available +// Use contact service API if phone is needed +``` + +**Action Required**: Remove code that depends on user.phone. + +## Timeline +- **Now**: v1 and v2 both available +- **March 1, 2026**: v1 deprecated +- **June 1, 2026**: v1 removed + +## Testing +Test your integration against v2 at: https://api-v2.example.com +``` + +--- + +## Example 3: Generating TypeScript Types from OpenAPI + +### Scenario +Your frontend team needs TypeScript interfaces for a newly documented API. + +### OpenAPI Spec +```yaml +components: + schemas: + User: + type: object + required: + - id + - email + - role + properties: + id: + type: string + format: uuid + email: + type: string + format: email + name: + type: string + role: + type: string + enum: + - admin + - member + - guest + createdAt: + type: string + format: date-time + settings: + $ref: '#/components/schemas/UserSettings' + + UserSettings: + type: object + properties: + theme: + type: string + enum: + - light + - dark + notifications: + type: boolean + + CreateUserRequest: + type: object + required: + - email + - role + properties: + email: + type: string + name: + type: string + role: + type: string + enum: + - admin + - member + - guest + + CreateUserResponse: + type: object + properties: + user: + $ref: '#/components/schemas/User' + token: + type: string +``` + +### Usage +**User asks**: "Generate TypeScript types from the OpenAPI spec" + +### Generated TypeScript +```typescript +// Auto-generated from OpenAPI spec +// Do not edit manually + +/** + * User role enum + */ +export enum UserRole { + Admin = 'admin', + Member = 'member', + Guest = 'guest' +} + +/** + * UI theme options + */ +export enum Theme { + Light = 'light', + Dark = 'dark' +} + +/** + * User settings configuration + */ +export interface UserSettings { + theme?: Theme; + notifications?: boolean; +} + +/** + * User entity + */ +export interface User { + /** UUID identifier */ + id: string; + + /** User email address */ + email: string; + + /** Optional display name */ + name?: string; + + /** User role */ + role: UserRole; + + /** Account creation timestamp */ + createdAt?: Date; + + /** User preferences */ + settings?: UserSettings; +} + +/** + * Request body for creating a new user + */ +export interface CreateUserRequest { + email: string; + name?: string; + role: UserRole; +} + +/** + * Response from user creation + */ +export interface CreateUserResponse { + user?: User; + token?: string; +} +``` + +**Additional Generation**: API Client Functions + +```typescript +// API client with type safety +export class UserApiClient { + constructor(private baseUrl: string) {} + + /** + * Create a new user + */ + async createUser( + data: CreateUserRequest + ): Promise { + const response = await fetch(`${this.baseUrl}/api/users`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data), + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + return response.json(); + } + + /** + * Get user by ID + */ + async getUser(id: string): Promise { + const response = await fetch(`${this.baseUrl}/api/users/${id}`); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + return response.json(); + } + + /** + * List all users + */ + async listUsers(): Promise { + const response = await fetch(`${this.baseUrl}/api/users`); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + return response.json(); + } +} +``` + +--- + +## Example 4: Creating React Query Hooks from REST Endpoints + +### Scenario +Generate React Query hooks for type-safe data fetching. + +### OpenAPI Endpoints +```yaml +paths: + /api/users/{id}: + get: + operationId: getUser + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/User' + + /api/users: + post: + operationId: createUser + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateUserRequest' + responses: + '201': + content: + application/json: + schema: + $ref: '#/components/schemas/User' +``` + +### Usage +**User asks**: "Generate React Query hooks from the API spec" + +### Generated Hooks +```typescript +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; + +/** + * Hook to fetch a user by ID + * @param userId - User UUID + */ +export function useUser(userId: string) { + return useQuery({ + queryKey: ['users', userId], + queryFn: async () => { + const response = await fetch(`/api/users/${userId}`); + if (!response.ok) { + throw new Error('Failed to fetch user'); + } + return response.json() as Promise; + }, + enabled: !!userId, // Only fetch if userId is provided + }); +} + +/** + * Hook to create a new user + */ +export function useCreateUser() { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (data: CreateUserRequest) => { + const response = await fetch('/api/users', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data), + }); + + if (!response.ok) { + throw new Error('Failed to create user'); + } + + return response.json() as Promise; + }, + onSuccess: (newUser) => { + // Invalidate users list to refetch + queryClient.invalidateQueries({ queryKey: ['users'] }); + + // Optimistically update cache with new user + queryClient.setQueryData(['users', newUser.id], newUser); + }, + }); +} + +/** + * Hook to list all users + */ +export function useUsers() { + return useQuery({ + queryKey: ['users'], + queryFn: async () => { + const response = await fetch('/api/users'); + if (!response.ok) { + throw new Error('Failed to fetch users'); + } + return response.json() as Promise; + }, + }); +} +``` + +### Usage in React Components +```typescript +// List users +function UserList() { + const { data: users, isLoading, error } = useUsers(); + + if (isLoading) return
Loading...
; + if (error) return
Error: {error.message}
; + + return ( +
    + {users?.map(user => ( +
  • {user.name}
  • + ))} +
+ ); +} + +// Get single user +function UserProfile({ userId }: { userId: string }) { + const { data: user, isLoading } = useUser(userId); + + if (isLoading) return
Loading...
; + if (!user) return
User not found
; + + return ( +
+

{user.name}

+

{user.email}

+

Role: {user.role}

+
+ ); +} + +// Create user +function CreateUserForm() { + const createUser = useCreateUser(); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + try { + await createUser.mutateAsync({ + email: 'user@example.com', + name: 'John Doe', + role: UserRole.Member, + }); + alert('User created!'); + } catch (error) { + alert('Failed to create user'); + } + }; + + return ( +
+ {/* form fields */} + +
+ ); +} +``` + +--- + +## Example 5: GraphQL Schema Evolution Best Practices + +### Scenario +Your GraphQL schema needs to evolve, and you want to ensure backward compatibility. + +### Current Schema (v1) +```graphql +type User { + id: ID! + email: String! + name: String! + avatar: String + createdAt: String! +} + +type Query { + user(id: ID!): User + users: [User!]! +} + +type Mutation { + createUser(email: String!, name: String!): User! +} +``` + +### Proposed Schema (v2) +```graphql +scalar DateTime +scalar URL + +type User { + id: ID! + email: String! + # Deprecated: use 'displayName' instead + name: String! @deprecated(reason: "Use 'displayName' for consistency") + displayName: String! + avatar: URL + createdAt: DateTime! + # New field (non-breaking) + updatedAt: DateTime +} + +input CreateUserInput { + email: String! + name: String! # Keep for backward compatibility + displayName: String # New optional field +} + +type Query { + user(id: ID!): User + # Enhanced with pagination (non-breaking) + users(limit: Int = 10, offset: Int = 0): UserConnection! +} + +type UserConnection { + nodes: [User!]! + totalCount: Int! + pageInfo: PageInfo! +} + +type PageInfo { + hasNextPage: Boolean! + hasPreviousPage: Boolean! +} + +type Mutation { + # Updated to use input type (non-breaking if backward compatible) + createUser(input: CreateUserInput!): User! +} +``` + +### Usage +**User asks**: "Review the GraphQL schema changes for breaking changes" + +### Analysis Report +``` +GraphQL Schema Evolution Analysis +================================== + +✅ NON-BREAKING CHANGES (5) + +1. Added new field: User.displayName + - Type: String! + - Impact: None - clients can ignore new fields + +2. Added new field: User.updatedAt + - Type: DateTime + - Impact: None - optional field + +3. Deprecated field: User.name + - Reason: "Use 'displayName' for consistency" + - Impact: None - field still available + - Action: Clients should migrate to displayName over time + +4. Enhanced Query.users with pagination + - Added arguments: limit, offset + - Changed return type: [User!]! → UserConnection! + - Impact: POTENTIALLY BREAKING if not handled correctly + - Mitigation: Keep old resolver temporarily + +5. Added custom scalars + - DateTime, URL + - Impact: None - improved type safety + +⚠️ POTENTIALLY BREAKING CHANGES (1) + +1. Query.users return type changed + - Old: [User!]! + - New: UserConnection! + - Risk: Existing queries expecting array will break + +RECOMMENDATION: This change needs careful migration + +Suggested Migration Strategy: +```graphql +type Query { + # Keep old field for backward compatibility + users: [User!]! @deprecated(reason: "Use usersConnection for pagination") + + # Add new field with different name + usersConnection(limit: Int = 10, offset: Int = 0): UserConnection! +} +``` + +This allows gradual migration without breaking existing clients. +``` + +### Recommended Final Schema (v2 - Backward Compatible) +```graphql +scalar DateTime +scalar URL + +type User { + id: ID! + email: String! + name: String! @deprecated(reason: "Use 'displayName' for consistency") + displayName: String! + avatar: URL + createdAt: DateTime! + updatedAt: DateTime +} + +input CreateUserInput { + email: String! + displayName: String! +} + +type Query { + user(id: ID!): User + + # Keep old version for backward compatibility + users: [User!]! @deprecated(reason: "Use 'usersConnection' for pagination support") + + # Add new version with pagination + usersConnection(limit: Int = 10, offset: Int = 0): UserConnection! +} + +type UserConnection { + nodes: [User!]! + totalCount: Int! + pageInfo: PageInfo! +} + +type PageInfo { + hasNextPage: Boolean! + hasPreviousPage: Boolean! +} + +type Mutation { + createUser(input: CreateUserInput!): User! +} +``` + +**Migration Timeline**: +``` +Phase 1 (Now): Deploy v2 with both old and new fields +- users (deprecated) → returns [User!]! +- usersConnection (new) → returns UserConnection! + +Phase 2 (3 months): Monitor usage +- Track which clients still use deprecated fields +- Contact teams to migrate + +Phase 3 (6 months): Remove deprecated fields +- Remove users query +- Remove User.name field +- Only usersConnection and displayName remain +``` + +--- + +## Example 6: Finding Undocumented Endpoints + +### Scenario +You suspect some endpoints are implemented but not documented in the OpenAPI spec. + +### OpenAPI Spec +```yaml +paths: + /api/users: + get: + summary: List users + /api/users/{id}: + get: + summary: Get user +``` + +### Actual Implementation +```javascript +// routes/users.js +router.get('/users', listUsers); +router.get('/users/:id', getUser); +router.post('/users', createUser); // Not in spec! +router.delete('/users/:id', deleteUser); // Not in spec! + +// routes/admin.js +router.get('/admin/stats', getStats); // Not in spec! +router.post('/admin/reset', resetDatabase); // Not in spec! +``` + +### Usage +**User asks**: "Find any API endpoints that exist in code but aren't documented" + +### Analysis Process +1. Parse OpenAPI spec → extract documented endpoints +2. Search codebase for route definitions (Grep for Express patterns) +3. Compare and identify undocumented routes + +### Result Report +``` +Undocumented Endpoints Report +============================== + +Found 4 implemented endpoints not in OpenAPI spec: + +1. POST /api/users + File: routes/users.js:15 + Handler: createUser + Status: PRODUCTION (likely intentional) + Recommendation: Add to OpenAPI spec with proper schema + +2. DELETE /api/users/:id + File: routes/users.js:23 + Handler: deleteUser + Status: PRODUCTION (likely intentional) + Recommendation: Add to OpenAPI spec + +3. GET /admin/stats + File: routes/admin.js:8 + Handler: getStats + Status: INTERNAL (admin only) + Recommendation: Consider separate internal API spec or document with security note + +4. POST /admin/reset + File: routes/admin.js:18 + Handler: resetDatabase + Status: DANGEROUS (database reset) + Recommendation: Remove from production or add strict authorization + +SUMMARY: +- 2 documented endpoints (50% coverage) +- 4 undocumented endpoints (50% missing) + +Next Steps: +1. Add POST /users and DELETE /users/:id to public spec +2. Create separate admin-api.yaml for internal endpoints +3. Secure or remove dangerous /admin/reset endpoint +``` + +--- + +## Summary + +These examples demonstrate how the API Contract Sync Manager skill helps with: + +1. **Validation** - Ensuring specs match implementations +2. **Change Detection** - Identifying breaking vs. non-breaking changes +3. **Code Generation** - Creating type-safe clients and hooks +4. **Evolution** - Managing schema changes over time +5. **Coverage** - Finding undocumented or unimplemented endpoints + +The skill adapts to your specific tech stack (Express, FastAPI, GraphQL, etc.) and provides actionable insights for maintaining healthy API contracts. + diff --git a/skills/api-contract-sync/REFERENCE.md b/skills/api-contract-sync/REFERENCE.md new file mode 100644 index 0000000..8836f78 --- /dev/null +++ b/skills/api-contract-sync/REFERENCE.md @@ -0,0 +1,688 @@ +# API Contract Sync Manager - Technical Reference + +This document provides technical details for working with OpenAPI and GraphQL specifications, including structure, breaking change patterns, and validation strategies. + +## OpenAPI Specification Structure + +### OpenAPI 3.0/3.1 Schema + +```yaml +openapi: 3.0.0 +info: + title: API Name + version: 1.0.0 + description: API description +servers: + - url: https://api.example.com/v1 +paths: + /users: + get: + summary: List users + operationId: listUsers + parameters: + - name: limit + in: query + schema: + type: integer + responses: + '200': + description: Success + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' +components: + schemas: + User: + type: object + required: + - id + - email + properties: + id: + type: string + format: uuid + email: + type: string + format: email + name: + type: string + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT +``` + +### Key OpenAPI Fields + +**info**: Metadata about the API +- `title`: API name +- `version`: Semantic version +- `description`: Overview of API purpose + +**servers**: Base URLs for API +- `url`: Full base URL +- `description`: Environment name (optional) + +**paths**: API endpoints +- Key is the path (e.g., `/users`, `/users/{id}`) +- Operations: `get`, `post`, `put`, `patch`, `delete` + +**components**: Reusable definitions +- `schemas`: Data models +- `parameters`: Reusable parameters +- `responses`: Reusable responses +- `securitySchemes`: Auth methods + +### OpenAPI Data Types + +| Type | Format | Description | Example | +|------|--------|-------------|---------| +| string | - | Text | "hello" | +| string | date | ISO 8601 date | "2025-10-16" | +| string | date-time | ISO 8601 timestamp | "2025-10-16T10:30:00Z" | +| string | email | Email address | "user@example.com" | +| string | uuid | UUID v4 | "123e4567-e89b..." | +| integer | int32 | 32-bit integer | 42 | +| integer | int64 | 64-bit integer | 9007199254740991 | +| number | float | Floating point | 3.14 | +| number | double | Double precision | 3.141592653589793 | +| boolean | - | True/false | true | +| array | - | List of items | [1, 2, 3] | +| object | - | Key-value pairs | {"key": "value"} | + +### OpenAPI References + +Use `$ref` to avoid duplication: + +```yaml +# Define once +components: + schemas: + User: + type: object + properties: + id: + type: string + +# Reference multiple times +paths: + /users/{id}: + get: + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/User' + /users: + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/User' +``` + +## GraphQL Schema Structure + +### GraphQL SDL (Schema Definition Language) + +```graphql +# Scalar types +scalar DateTime +scalar Email + +# Enum types +enum UserRole { + ADMIN + MEMBER + GUEST +} + +# Object types +type User { + id: ID! + email: Email! + name: String + role: UserRole! + createdAt: DateTime! + posts: [Post!]! +} + +type Post { + id: ID! + title: String! + content: String! + author: User! + published: Boolean! +} + +# Input types (for mutations) +input CreateUserInput { + email: Email! + name: String + role: UserRole = MEMBER +} + +# Query operations +type Query { + user(id: ID!): User + users(limit: Int = 10, offset: Int = 0): [User!]! + posts(authorId: ID): [Post!]! +} + +# Mutation operations +type Mutation { + createUser(input: CreateUserInput!): User! + updateUser(id: ID!, input: UpdateUserInput!): User! + deleteUser(id: ID!): Boolean! +} + +# Subscription operations +type Subscription { + userCreated: User! + postPublished: Post! +} +``` + +### GraphQL Type Modifiers + +| Modifier | Meaning | Example | +|----------|---------|---------| +| `Type` | Nullable | Can be `User` or `null` | +| `Type!` | Non-null | Must be `User`, never `null` | +| `[Type]` | Nullable list of nullable items | `[User]`, `null`, or `[User, null]` | +| `[Type]!` | Non-null list of nullable items | `[User]` or `[User, null]` but never `null` | +| `[Type!]` | Nullable list of non-null items | `[User]` or `null`, never `[User, null]` | +| `[Type!]!` | Non-null list of non-null items | `[User]`, never `null` or `[User, null]` | + +### GraphQL Directives + +```graphql +type User { + id: ID! + email: String! + oldEmail: String @deprecated(reason: "Use 'email' field instead") + internalId: String @internal +} + +type Query { + users: [User!]! @auth(requires: ADMIN) +} +``` + +Common directives: +- `@deprecated`: Mark field as deprecated +- `@auth`, `@requires`: Custom authorization +- `@skip`, `@include`: Conditional inclusion (query-side) + +## Breaking Change Patterns + +### OpenAPI Breaking Changes + +#### 1. Removed Endpoints +```yaml +# Before +paths: + /users/{id}: + delete: + # ... + +# After +paths: + # /users/{id} delete operation removed +``` +**Impact**: Clients calling DELETE /users/{id} will get 404 + +#### 2. Required Parameters Added +```yaml +# Before +parameters: + - name: email + in: query + required: false + +# After +parameters: + - name: email + in: query + required: true # Changed to required +``` +**Impact**: Existing calls without `email` parameter will fail validation + +#### 3. Parameter Type Changed +```yaml +# Before +parameters: + - name: limit + in: query + schema: + type: string + +# After +parameters: + - name: limit + in: query + schema: + type: integer # Changed from string +``` +**Impact**: Clients sending "10" as string may fail validation + +#### 4. Required Property Added to Request +```yaml +# Before +CreateUserRequest: + type: object + required: + - email + properties: + email: + type: string + +# After +CreateUserRequest: + type: object + required: + - email + - role # New required field + properties: + email: + type: string + role: + type: string +``` +**Impact**: Requests without `role` field will be rejected + +#### 5. Response Property Removed +```yaml +# Before +User: + properties: + id: + type: string + email: + type: string + phone: + type: string + +# After +User: + properties: + id: + type: string + email: + type: string + # phone removed +``` +**Impact**: Clients accessing `user.phone` will get undefined/null + +#### 6. Property Type Changed +```yaml +# Before +User: + properties: + id: + type: string + +# After +User: + properties: + id: + type: integer # Changed type +``` +**Impact**: Type mismatches in strongly-typed clients + +#### 7. Enum Values Removed +```yaml +# Before +UserRole: + type: string + enum: + - admin + - member + - guest + +# After +UserRole: + type: string + enum: + - admin + - member + # guest removed +``` +**Impact**: Requests with `role: "guest"` will fail validation + +### GraphQL Breaking Changes + +#### 1. Field Removed from Type +```graphql +# Before +type User { + id: ID! + email: String! + phone: String +} + +# After +type User { + id: ID! + email: String! + # phone removed +} +``` +**Impact**: Queries requesting `phone` field will fail + +#### 2. Argument Added to Field +```graphql +# Before +type Query { + users: [User!]! +} + +# After +type Query { + users(role: UserRole!): [User!]! # New required argument +} +``` +**Impact**: Existing queries without `role` argument will fail + +#### 3. Non-Null Modifier Added +```graphql +# Before +type User { + email: String +} + +# After +type User { + email: String! # Now non-null +} +``` +**Impact**: Clients expecting nullable email may fail + +#### 4. Argument Type Changed +```graphql +# Before +type Query { + user(id: String!): User +} + +# After +type Query { + user(id: ID!): User # Changed from String to ID +} +``` +**Impact**: May cause validation issues depending on implementation + +#### 5. Field Type Changed +```graphql +# Before +type User { + createdAt: String +} + +# After +type User { + createdAt: DateTime! # Changed scalar type +} +``` +**Impact**: Clients expecting string format may break + +### Non-Breaking Changes (Safe) + +#### OpenAPI Non-Breaking +- ✓ Add new endpoint +- ✓ Add optional parameter +- ✓ Add optional request body property +- ✓ Add response property +- ✓ Make required field optional +- ✓ Add new enum value +- ✓ Expand validation (e.g., increase maxLength) +- ✓ Add default values + +#### GraphQL Non-Breaking +- ✓ Add new field to type +- ✓ Add new type +- ✓ Add new query/mutation +- ✓ Add optional argument to field +- ✓ Remove non-null modifier (make nullable) +- ✓ Add value to enum +- ✓ Deprecate field (with @deprecated) + +## Validation Strategies + +### OpenAPI Validation + +#### Structural Validation +1. **Schema adherence**: Validate against OpenAPI 3.0/3.1 spec +2. **Reference integrity**: Ensure all `$ref` point to valid schemas +3. **Required fields**: Check all required fields exist +4. **Type consistency**: Verify types match throughout + +#### Content Validation +1. **Descriptions**: All operations and schemas should have descriptions +2. **Examples**: Include request/response examples +3. **Error responses**: Document 4xx and 5xx responses +4. **Security**: All protected endpoints have security requirements +5. **Tags**: Operations grouped with consistent tags + +#### Common Issues to Check +```yaml +# ❌ Bad: Missing description +/users: + get: + responses: + '200': + description: Success + +# ✓ Good: Descriptive +/users: + get: + summary: List all users + description: Returns a paginated list of users with optional filtering + responses: + '200': + description: Successfully retrieved user list +``` + +### GraphQL Validation + +#### Schema Validation +1. **Type definitions**: All types properly defined +2. **Field types**: All fields reference valid types +3. **Arguments**: Argument types are valid scalars, enums, or input types +4. **Resolver coverage**: All fields have resolvers (implementation check) +5. **Circular references**: Detect and handle circular dependencies + +#### Naming Conventions +- **Types**: PascalCase (e.g., `User`, `BlogPost`) +- **Fields**: camelCase (e.g., `firstName`, `createdAt`) +- **Enums**: UPPER_SNAKE_CASE (e.g., `USER_ROLE`, `POST_STATUS`) +- **Arguments**: camelCase (e.g., `userId`, `includeDeleted`) + +#### Common Issues +```graphql +# ❌ Bad: Query returns nullable when it shouldn't +type Query { + user(id: ID!): User # Could return null +} + +# ✓ Good: Clear nullability +type Query { + user(id: ID!): User # Can return null if not found + requireUser(id: ID!): User! # Always returns user or throws error +} +``` + +## Type Generation Strategies + +### OpenAPI to TypeScript + +#### Basic Type Mapping +```typescript +// OpenAPI schema +{ + "type": "object", + "required": ["id", "email"], + "properties": { + "id": { "type": "string", "format": "uuid" }, + "email": { "type": "string", "format": "email" }, + "name": { "type": "string" }, + "age": { "type": "integer" } + } +} + +// Generated TypeScript +interface User { + id: string; // uuid format → string + email: string; // email format → string + name?: string; // optional → ? + age?: number; // integer → number +} +``` + +#### Union Types from oneOf +```yaml +# OpenAPI +PaymentMethod: + oneOf: + - $ref: '#/components/schemas/CreditCard' + - $ref: '#/components/schemas/BankAccount' +``` + +```typescript +// TypeScript +type PaymentMethod = CreditCard | BankAccount; +``` + +#### Enum Mapping +```yaml +# OpenAPI +UserRole: + type: string + enum: + - admin + - member + - guest +``` + +```typescript +// TypeScript +enum UserRole { + Admin = 'admin', + Member = 'member', + Guest = 'guest' +} +// or +type UserRole = 'admin' | 'member' | 'guest'; +``` + +### GraphQL to TypeScript + +```graphql +# GraphQL +type User { + id: ID! + email: String! + name: String + posts: [Post!]! +} +``` + +```typescript +// TypeScript +interface User { + id: string; // ID! → string + email: string; // String! → string + name: string | null; // String → string | null + posts: Post[]; // [Post!]! → Post[] +} +``` + +## Validation Tools Integration + +### Spectral (OpenAPI) +```bash +# Install +npm install -g @stoplight/spectral-cli + +# Validate +spectral lint openapi.yaml + +# Custom ruleset +spectral lint openapi.yaml --ruleset .spectral.yaml +``` + +### GraphQL Inspector +```bash +# Install +npm install -g @graphql-inspector/cli + +# Validate schema +graphql-inspector validate schema.graphql + +# Compare schemas +graphql-inspector diff old.graphql new.graphql + +# Coverage check +graphql-inspector coverage schema.graphql documents/*.graphql +``` + +### OpenAPI Diff +```bash +# Install +npm install -g openapi-diff + +# Compare versions +openapi-diff old.yaml new.yaml + +# Output markdown report +openapi-diff old.yaml new.yaml --format markdown > changes.md +``` + +## Security Considerations + +### OpenAPI Security +1. **Authentication**: Always define security schemes +2. **Authorization**: Document which roles can access endpoints +3. **Sensitive data**: Mark PII fields in schemas +4. **Rate limiting**: Document rate limit headers +5. **HTTPS only**: Use https:// in server URLs + +### GraphQL Security +1. **Query depth limiting**: Prevent deeply nested queries +2. **Query cost analysis**: Limit expensive operations +3. **Field-level auth**: Use directives like @auth +4. **Input validation**: Validate all arguments +5. **Disable introspection**: In production environments + +## Best Practices Summary + +### OpenAPI +- Use semantic versioning in `info.version` +- Group related operations with tags +- Always include request/response examples +- Document all error responses (4xx, 5xx) +- Use `$ref` to keep specs DRY +- Validate specs in CI/CD pipeline + +### GraphQL +- Use meaningful type and field names +- Add descriptions to all types and fields +- Use custom scalars for validated types (Email, URL, etc.) +- Implement pagination for list fields +- Use @deprecated instead of removing fields +- Version schema with additive changes only + +### Both +- Automate validation in CI/CD +- Generate client code from specs +- Keep specs in version control +- Review API changes in pull requests +- Maintain changelog of API versions +- Test generated client code + diff --git a/skills/api-contract-sync/SKILL.md b/skills/api-contract-sync/SKILL.md new file mode 100644 index 0000000..a29fe45 --- /dev/null +++ b/skills/api-contract-sync/SKILL.md @@ -0,0 +1,389 @@ +--- +name: API Contract Sync Manager +description: Validate OpenAPI, Swagger, and GraphQL schemas match backend implementation. Detect breaking changes, generate TypeScript clients, and ensure API documentation stays synchronized. Use when working with API spec files (.yaml, .json, .graphql), reviewing API changes, generating frontend types, or validating endpoint implementations. +allowed-tools: Read, Grep, Glob, RunTerminalCmd +--- + +# API Contract Sync Manager + +Maintain synchronization between API specifications and their implementations, detect breaking changes, and generate client code to ensure contracts stay reliable across frontend and backend teams. + +## When to Use This Skill + +Use this skill when: +- Working with OpenAPI/Swagger specification files (`.yaml`, `.json`) +- Managing GraphQL schemas (`.graphql`, `.gql`) +- Reviewing API changes in pull requests +- Generating TypeScript types or client code from specs +- Validating that implementations match documented APIs +- Detecting breaking vs. non-breaking API changes +- Creating API versioning strategies +- Onboarding new developers to an API-driven codebase + +## Core Capabilities + +### 1. Spec Validation + +Validate API specification files for correctness and completeness: + +**OpenAPI/Swagger Validation**: +- Check schema syntax and structure +- Validate against OpenAPI 3.0/3.1 standards +- Ensure all endpoints have proper descriptions +- Verify request/response schemas are complete +- Check for required security definitions +- Validate parameter types and constraints + +**GraphQL Validation**: +- Parse and validate SDL (Schema Definition Language) +- Check for schema stitching issues +- Validate resolver coverage +- Detect circular dependencies +- Verify input/output type consistency + +**Validation Approach**: +1. Read the spec file using the Read tool +2. Parse the structure (YAML/JSON for OpenAPI, SDL for GraphQL) +3. Check for common issues: + - Missing required fields + - Invalid references (`$ref`) + - Inconsistent naming conventions + - Missing examples or descriptions + - Security scheme gaps +4. Report findings with line numbers and suggestions + +### 2. Implementation Matching + +Cross-reference API specifications with actual code implementations: + +**For REST APIs**: +1. Extract all endpoints from OpenAPI spec (paths, methods) +2. Search codebase for route definitions: + - Express.js: `app.get()`, `router.post()`, etc. + - FastAPI: `@app.get()`, `@router.post()` + - Django: `path()`, `urlpatterns` + - Spring Boot: `@GetMapping`, `@PostMapping` +3. Compare spec endpoints against implemented routes +4. Flag discrepancies: + - Documented but not implemented + - Implemented but not documented + - Parameter mismatches + - Response type differences + +**For GraphQL**: +1. Extract types, queries, mutations from schema +2. Search for resolver implementations +3. Verify all schema fields have resolvers +4. Check resolver signatures match schema types + +**Implementation Matching Steps**: +``` +1. Parse spec → extract endpoints/operations +2. Use Grep to find route handlers in codebase +3. Compare and categorize: + - ✓ Matched: spec and implementation align + - ⚠ Drift: partial match with differences + - ✗ Missing: documented but not implemented + - ⚠ Undocumented: implemented but not in spec +4. Generate coverage report +``` + +### 3. Breaking Change Detection + +Compare two versions of an API spec to detect breaking vs. non-breaking changes: + +**Breaking Changes** (require version bump): +- Removed endpoints or operations +- Removed required request parameters +- Changed parameter types (e.g., string → number) +- Made optional parameters required +- Removed response properties that clients depend on +- Changed response status codes +- Renamed endpoints, parameters, or fields +- Stricter validation rules (e.g., regex patterns) + +**Non-Breaking Changes** (safe to deploy): +- Added new endpoints +- Added optional parameters +- Made required parameters optional +- Added new response properties +- Expanded enum values +- Improved descriptions/examples +- Added deprecation warnings + +**Change Detection Process**: +1. Read both spec versions (old and new) +2. Compare schemas field by field +3. Categorize each change as breaking or non-breaking +4. Generate migration guide with: + - Summary of breaking changes + - Impact on existing clients + - Required client updates + - Recommended versioning strategy + +### 4. Client Code Generation + +Generate type-safe client code from API specifications: + +**TypeScript Interfaces**: +```typescript +// From OpenAPI schema +interface User { + id: string; + email: string; + name?: string; + createdAt: Date; +} + +interface CreateUserRequest { + email: string; + name?: string; +} + +interface CreateUserResponse { + user: User; + token: string; +} +``` + +**API Client Functions**: +```typescript +// HTTP client with proper typing +async function createUser( + data: CreateUserRequest +): Promise { + const response = await fetch('/api/users', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(data) + }); + return response.json(); +} +``` + +**React Query Hooks**: +```typescript +// Auto-generated hooks for data fetching +function useUser(userId: string) { + return useQuery(['user', userId], () => + fetch(`/api/users/${userId}`).then(r => r.json()) + ); +} + +function useCreateUser() { + return useMutation((data: CreateUserRequest) => + fetch('/api/users', { + method: 'POST', + body: JSON.stringify(data) + }).then(r => r.json()) + ); +} +``` + +**Generation Steps**: +1. Parse OpenAPI/GraphQL schema +2. Extract all data models (schemas, types) +3. Generate TypeScript interfaces with proper types +4. Create client functions for each endpoint +5. Optionally generate hooks for React Query/SWR +6. Add JSDoc comments from spec descriptions + +### 5. Coverage Analysis + +Identify gaps between documentation and implementation: + +**Analysis Report Structure**: +``` +API Coverage Report +================== + +Documented Endpoints: 45 +Implemented Endpoints: 42 +Coverage: 93% + +Missing Implementations: +- DELETE /api/users/{id} (documented but not found) +- POST /api/users/{id}/suspend (documented but not found) + +Undocumented Endpoints: +- GET /api/internal/health (found in code, not in spec) +- POST /api/debug/reset (found in code, not in spec) + +Mismatched Signatures: +- POST /api/users + Spec expects: { email, name, role } + Code accepts: { email, name } (missing 'role') +``` + +**Coverage Analysis Process**: +1. Run implementation matching (see section 2) +2. Calculate coverage percentage +3. List all discrepancies with file locations +4. Prioritize issues by severity +5. Suggest next steps to achieve 100% coverage + +### 6. Migration Guides + +Create upgrade guides when API versions change: + +**Migration Guide Template**: +```markdown +# API v2.0 Migration Guide + +## Breaking Changes + +### 1. User Creation Endpoint +**Change**: Required `role` field added to POST /api/users +**Impact**: All user creation calls will fail without this field +**Action Required**: +- Update all POST /api/users calls to include `role` +- Default to 'member' if no specific role needed + +Before: +```json +{ "email": "user@example.com", "name": "John" } +``` + +After: +```json +{ "email": "user@example.com", "name": "John", "role": "member" } +``` + +### 2. Authentication Token Format +**Change**: JWT tokens now use RS256 instead of HS256 +**Impact**: Token validation must be updated +**Action Required**: +- Update JWT verification libraries +- Fetch new public key from /.well-known/jwks.json +``` + +**Guide Generation Steps**: +1. Detect all breaking changes (see section 3) +2. Group changes by endpoint or feature +3. For each change, document: + - What changed and why + - Impact on existing clients + - Required code updates with before/after examples + - Timeline for deprecation +4. Add general upgrade instructions + +## Best Practices + +### For OpenAPI Specs +1. **Use $ref liberally**: Define schemas once, reference everywhere +2. **Version your APIs**: Use `/v1/`, `/v2/` prefixes or version headers +3. **Add examples**: Include request/response examples in spec +4. **Document errors**: Define all possible error responses +5. **Security first**: Always specify security requirements + +### For GraphQL Schemas +1. **Use descriptions**: Document all types, fields, and arguments +2. **Deprecate, don't remove**: Use `@deprecated` directive +3. **Input validation**: Use custom scalars for validated types +4. **Pagination patterns**: Use connection/edge patterns consistently +5. **Error handling**: Define custom error types + +### For Breaking Changes +1. **Version bump**: Major version for breaking changes +2. **Deprecation period**: Maintain old version for transition +3. **Clear communication**: Document changes prominently +4. **Backward compatibility**: Provide adapters when possible +5. **Client coordination**: Ensure clients can update before removal + +## Common Workflows + +### Workflow 1: Validate Existing Spec +``` +1. User: "Validate the OpenAPI spec" +2. Read the spec file (usually openapi.yaml or swagger.json) +3. Parse and validate structure +4. Report any issues with suggestions +``` + +### Workflow 2: Check Implementation Match +``` +1. User: "Does our API implementation match the spec?" +2. Read spec file +3. Extract all endpoints +4. Search codebase for route handlers +5. Compare and generate coverage report +``` + +### Workflow 3: Detect Breaking Changes +``` +1. User: "Compare API v1 and v2 specs" +2. Read both spec files +3. Diff schemas systematically +4. Categorize changes as breaking/non-breaking +5. Generate migration guide +``` + +### Workflow 4: Generate TypeScript Types +``` +1. User: "Generate TypeScript types from the API spec" +2. Read OpenAPI/GraphQL schema +3. Extract all data models +4. Generate TypeScript interfaces +5. Create client functions or hooks if requested +``` + +### Workflow 5: Find Coverage Gaps +``` +1. User: "What endpoints are missing in our spec?" +2. Run implementation matching +3. Identify undocumented endpoints +4. Suggest adding them to spec with proper schemas +``` + +## Tools and Commands + +### Validation Tools +When validation tools are available, use them: +- **OpenAPI**: `npx @stoplight/spectral-cli lint openapi.yaml` +- **GraphQL**: `npx graphql-inspector validate schema.graphql` + +### Comparison Tools +For advanced diff analysis: +- **OpenAPI**: `npx openapi-diff old.yaml new.yaml` +- **GraphQL**: `npx graphql-inspector diff old.graphql new.graphql` + +### Code Generation +Recommend these tools for automated generation: +- **openapi-typescript**: Generate TypeScript from OpenAPI +- **graphql-code-generator**: Generate TypeScript from GraphQL +- **orval**: Generate React Query hooks from OpenAPI + +## Error Handling + +When encountering issues: + +**Invalid Spec File**: +- Report specific syntax errors with line numbers +- Suggest corrections based on spec version +- Provide valid example structure + +**Missing Implementation**: +- List file locations where handlers should exist +- Suggest framework-specific code to implement +- Estimate implementation effort + +**Type Mismatches**: +- Show expected vs. actual types clearly +- Explain impact of the mismatch +- Suggest type coercion or spec updates + +## Additional Resources + +For more detailed information on specific topics, see: +- [REFERENCE.md](REFERENCE.md) - Technical details on OpenAPI and GraphQL structures +- [EXAMPLES.md](EXAMPLES.md) - Real-world usage scenarios and code samples + +## Requirements + +This skill works best with: +- API spec files in the codebase +- Structured routing in backend code +- TypeScript for type generation (optional but recommended) + +No additional packages are required for basic validation and comparison. Advanced features may suggest installing validation tools via npm. +