Files
2025-11-29 17:52:44 +08:00

14 KiB

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

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:

# 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)

# 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

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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# Before
type User {
  email: String
}

# After
type User {
  email: String!  # Now non-null
}

Impact: Clients expecting nullable email may fail

4. Argument Type Changed

# 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

# 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

# ❌ 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

# ❌ 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

// 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

# OpenAPI
PaymentMethod:
  oneOf:
    - $ref: '#/components/schemas/CreditCard'
    - $ref: '#/components/schemas/BankAccount'
// TypeScript
type PaymentMethod = CreditCard | BankAccount;

Enum Mapping

# OpenAPI
UserRole:
  type: string
  enum:
    - admin
    - member
    - guest
// TypeScript
enum UserRole {
  Admin = 'admin',
  Member = 'member',
  Guest = 'guest'
}
// or
type UserRole = 'admin' | 'member' | 'guest';

GraphQL to TypeScript

# GraphQL
type User {
  id: ID!
  email: String!
  name: String
  posts: [Post!]!
}
// 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)

# Install
npm install -g @stoplight/spectral-cli

# Validate
spectral lint openapi.yaml

# Custom ruleset
spectral lint openapi.yaml --ruleset .spectral.yaml

GraphQL Inspector

# 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

# 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