Skip to content

feat(openapi): complete specification with PUT, PATCH, DELETE and error responses #13

Description

@dev-queiroz

Summary

Complete OpenAPI 3.0.3 specification generation by adding missing HTTP methods (PUT, PATCH, DELETE) and proper error response documentation. Currently only GET/POST and GET-by-id are generated.

Motivation

The existing OpenAPI generator produces incomplete specs missing critical operations:

Current (incomplete):

GET /produto - list
POST /produto - create  
GET /produto/{id} - get one

Expected (complete):

GET /produto - list
POST /produto - create
GET /produto/{id} - get one
PUT /produto/{id} - replace entire resource
PATCH /produto/{id} - partial update
DELETE /produto/{id} - delete resource

Additionally, error responses are not documented, leading to incomplete API documentation and missing validation error details for API consumers.

Scope

In Scope

  • PUT /{id} path with complete body replacement
  • PATCH /{id} path with partial updates
  • DELETE /{id} path returning success status
  • 400 (Bad Request) response for validation errors
  • 404 (Not Found) response for missing resources
  • 500 (Internal Server Error) response
  • Proper HTTP status codes (200, 201, 204, 400, 404, 500)
  • Request body schemas for each operation
  • Response schemas with examples
  • Error response body structure

Out of Scope

  • Authentication/security schemes (Feature 1.3.3)
  • Custom error codes beyond HTTP standards
  • Server-specific error formats
  • Rate limiting headers
  • CORS documentation

Technical Design

PUT Endpoint Design

Operation:

PUT /produto/{id}

Request:

{
  "nome": "Produto Updated",
  "valor": 149.99,
  "ativo": true
}

Response (200 OK):

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "nome": "Produto Updated",
  "valor": 149.99,
  "ativo": true,
  "criadoEm": "2024-01-15T10:30:00Z"
}

OpenAPI Schema:

put:
  operationId: updateProduto
  summary: Replace entire Produto
  parameters:
    - name: id
      in: path
      required: true
      schema:
        type: string
        format: uuid
  requestBody:
    required: true
    content:
      application/json:
        schema:
          $ref: '#/components/schemas/UpdateProdutoDto'
  responses:
    '200':
      description: Produto updated successfully
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Produto'
    '400':
      $ref: '#/components/responses/ValidationError'
    '404':
      $ref: '#/components/responses/NotFound'

PATCH Endpoint Design

Operation:

PATCH /produto/{id}

Request (partial - only changed fields):

{
  "valor": 129.99
}

Response (200 OK):

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "nome": "Produto Original",
  "valor": 129.99,  // Updated
  "ativo": true,
  "criadoEm": "2024-01-15T10:30:00Z"
}

OpenAPI Schema:

patch:
  operationId: partialUpdateProduto
  summary: Partially update Produto
  parameters:
    - name: id
      in: path
      required: true
      schema:
        type: string
        format: uuid
  requestBody:
    required: true
    content:
      application/json:
        schema:
          $ref: '#/components/schemas/PartialUpdateProdutoDto'
  responses:
    '200':
      description: Produto updated successfully
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Produto'

DELETE Endpoint Design

Operation:

DELETE /produto/{id}

Response (204 No Content):

HTTP 204 No Content
(no body)

OpenAPI Schema:

delete:
  operationId: deleteProduto
  summary: Delete Produto
  parameters:
    - name: id
      in: path
      required: true
      schema:
        type: string
        format: uuid
  responses:
    '204':
      description: Produto deleted successfully
    '404':
      $ref: '#/components/responses/NotFound'

Error Response Definitions

Reusable Error Response (#/components/responses/ValidationError):

responses:
  ValidationError:
    description: Request validation failed
    content:
      application/json:
        schema:
          type: object
          properties:
            statusCode:
              type: integer
              example: 400
            message:
              type: string
              example: "Validation failed"
            errors:
              type: array
              items:
                type: object
                properties:
                  field:
                    type: string
                  message:
                    type: string

  NotFound:
    description: Resource not found
    content:
      application/json:
        schema:
          type: object
          properties:
            statusCode:
              type: integer
              example: 404
            message:
              type: string
              example: "Produto 123 not found"

  InternalServerError:
    description: Internal server error
    content:
      application/json:
        schema:
          type: object
          properties:
            statusCode:
              type: integer
              example: 500
            message:
              type: string
              example: "Internal server error"

DTO Type Mapping

For each contract, generate:

  1. Read DTO (response): All fields as defined
  2. Create DTO (POST request): Exclude id, timestamps
  3. Update DTO (PUT request): Exclude id, timestamps
  4. Partial Update DTO (PATCH request): All fields optional

Example:

schemas:
  ProdutoDto:  # Read/response
    type: object
    properties:
      id:
        type: string
        format: uuid
      nome:
        type: string
      valor:
        type: number
  
  CreateProdutoDto:  # POST
    type: object
    properties:
      nome:
        type: string
      valor:
        type: number
    required:
      - nome
      - valor
  
  UpdateProdutoDto:  # PUT
    type: object
    properties:
      nome:
        type: string
      valor:
        type: number
    required:
      - nome
      - valor
  
  PartialUpdateProdutoDto:  # PATCH
    type: object
    properties:
      nome:
        type: string
      valor:
        type: number

Acceptance Criteria

  • PUT /{id} path generated with correct schema
  • PATCH /{id} path generated with partial schema
  • DELETE /{id} path generated
  • All paths include proper operationIds
  • 400 error response defined and referenced
  • 404 error response defined and referenced
  • 500 error response defined
  • Request bodies use correct DTOs (Create, Update, Partial)
  • Response bodies reference full resource schema
  • HTTP status codes: 200, 201, 204, 400, 404, 500
  • Generated spec validates against OpenAPI 3.0.3 schema
  • Examples provided for all operations
  • Backward compatible: existing GET/POST unchanged
  • No duplicate paths or operations

Testing Strategy

Unit Tests

// tests/openapi-full-paths.test.mjs

test('PUT path generated with correct schema', () => {
  const spec = generateOpenApi(testModel);
  const putPath = spec.paths['/produto/{id}'].put;
  
  expect(putPath).toBeDefined();
  expect(putPath.operationId).toBe('updateProduto');
  expect(putPath.requestBody).toBeDefined();
})

test('PATCH path generated with partial schema', () => {
  const spec = generateOpenApi(testModel);
  const patchPath = spec.paths['/produto/{id}'].patch;
  
  expect(patchPath).toBeDefined();
  expect(patchPath.operationId).toBe('partialUpdateProduto');
})

test('DELETE path returns 204', () => {
  const spec = generateOpenApi(testModel);
  const deletePath = spec.paths['/produto/{id}'].delete;
  
  expect(deletePath).toBeDefined();
  expect(deletePath.responses['204']).toBeDefined();
})

test('error responses defined and referenced', () => {
  const spec = generateOpenApi(testModel);
  const allPaths = Object.values(spec.paths);
  
  allPaths.forEach(path => {
    const operations = Object.values(path);
    operations.forEach(op => {
      if (op.responses) {
        expect(op.responses['400'] || op.responses['404']).toBeDefined();
      }
    });
  });
})

Integration Tests

  • Generate complete spec for contract
  • Validate entire spec against OpenAPI 3.0.3
  • Verify all 5 paths present (GET list, POST, GET one, PUT, PATCH, DELETE)
  • Check status codes match expectations

Validation

  • Use openapi-types or swagger-parser to validate spec
  • Check: no duplicate paths, valid schema references, valid status codes

Files to Create/Modify

New Files:

  • packages/generators/src/openapi/paths/put.ts
  • packages/generators/src/openapi/paths/patch.ts
  • packages/generators/src/openapi/paths/delete.ts
  • packages/generators/src/openapi/responses.ts
  • tests/openapi-full-paths.test.mjs (150 lines)

Modified Files:

  • packages/generators/src/openapi/generator.ts (+80 lines)
  • packages/generators/src/openapi/mapper.ts (to support DTO mapping)

Non-Goals

  • Authentication bearer token documentation (Feature 1.3.3)
  • Custom status codes beyond HTTP standards
  • Response headers documentation
  • Server URL/baseUrl configuration
  • API versioning schemes

Future Extensions

  • Request/response headers (e.g., X-Request-ID)
  • Server list with environments
  • Webhooks
  • Streaming responses
  • Deprecated endpoint markers
  • x-extension custom fields

Dependencies

Blocks:

  • Feature 1.3.3: Authentication & Security (depends on complete paths)
  • Feature 1.3.4: Example Data (depends on proper response schemas)

Depends On:

  • Phase 0: Base OpenAPI generation (✅ complete)

Implementation Notes

Path Parameter Validation

All {id} parameters should be:

- name: id
  in: path
  required: true
  schema:
    type: string
    format: uuid  # if id is uuid

Status Code Semantics

  • 200: Read operation succeeded, resource returned
  • 201: Create operation succeeded, new resource returned (POST only)
  • 204: Delete operation succeeded, no content returned
  • 400: Client error - validation failure
  • 404: Resource not found
  • 500: Server error

DTO Naming

  • {ContractName}Dto - Read/response format
  • Create{ContractName}Dto - POST request
  • Update{ContractName}Dto - PUT request
  • PartialUpdate{ContractName}Dto - PATCH request

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions