1006 lines
21 KiB
Markdown
1006 lines
21 KiB
Markdown
# 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<CreateUserResponse> {
|
|
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<User> {
|
|
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<User[]> {
|
|
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<User>;
|
|
},
|
|
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<User>;
|
|
},
|
|
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<User[]>;
|
|
},
|
|
});
|
|
}
|
|
```
|
|
|
|
### Usage in React Components
|
|
```typescript
|
|
// List users
|
|
function UserList() {
|
|
const { data: users, isLoading, error } = useUsers();
|
|
|
|
if (isLoading) return <div>Loading...</div>;
|
|
if (error) return <div>Error: {error.message}</div>;
|
|
|
|
return (
|
|
<ul>
|
|
{users?.map(user => (
|
|
<li key={user.id}>{user.name}</li>
|
|
))}
|
|
</ul>
|
|
);
|
|
}
|
|
|
|
// Get single user
|
|
function UserProfile({ userId }: { userId: string }) {
|
|
const { data: user, isLoading } = useUser(userId);
|
|
|
|
if (isLoading) return <div>Loading...</div>;
|
|
if (!user) return <div>User not found</div>;
|
|
|
|
return (
|
|
<div>
|
|
<h1>{user.name}</h1>
|
|
<p>{user.email}</p>
|
|
<p>Role: {user.role}</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// 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 onSubmit={handleSubmit}>
|
|
{/* form fields */}
|
|
<button type="submit" disabled={createUser.isPending}>
|
|
{createUser.isPending ? 'Creating...' : 'Create User'}
|
|
</button>
|
|
</form>
|
|
);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 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.
|
|
|