OpenAPI Specification Generation Guidelines¶
Version: 1.0.0
Target: OpenAPI 3.0.3
Purpose: Generate REST API specifications from entity specifications
Type Mappings¶
Map data types from specs/_meta/data-types.md to OpenAPI schema types:
type_mappings:
# Primitive Types
string:
type: string
text:
type: string
integer:
type: integer
format: int32
long:
type: integer
format: int64
positive_integer:
type: integer
minimum: 1
decimal:
type: number
format: double
boolean:
type: boolean
datetime:
type: string
format: date-time
date:
type: string
format: date
# Formatted Types
uuid:
type: string
format: uuid
uri:
type: string
format: uri
email:
type: string
format: email
language_code:
type: string
pattern: "^[a-z]{2}$"
version_string:
type: string
pattern: "^\\d+\\.\\d+\\.\\d+$"
# Restricted String Types
short_string:
type: string
minLength: 1
maxLength: 255
long_string:
type: string
minLength: 1
maxLength: 8000
identifier_token:
type: string
minLength: 1
# Composite Types
multilingual_text:
type: object
additionalProperties:
type: string
reference:
type: object
required: [id]
properties:
id: {type: string, format: uuid}
href: {type: string, format: uri}
type: {type: string}
name: {type: string}
date_range:
type: object
required: [start, end]
properties:
start: {type: string, format: date}
end: {type: string, format: date}
date_range_open_ended:
type: object
anyOf:
- required: [start]
- required: [end]
properties:
start: {type: string, format: date}
end: {type: string, format: date}
coordinates:
type: object
required: [x, y]
properties:
x: {type: integer, minimum: 1}
y: {type: integer, minimum: 1}
OpenAPI Document Template¶
openapi: 3.0.3
info:
title: RUT Domain API
description: |
REST API for the RUT domain model entities.
This API is automatically generated from entity specifications
in the specs/ directory.
version: {version}
contact:
name: API Support
email: support@example.org
license:
name: MIT
url: https://opensource.org/licenses/MIT
servers:
- url: https://api.example.org/v1
description: Production server
- url: https://api-staging.example.org/v1
description: Staging server
- url: http://localhost:8080/v1
description: Local development server
tags:
- name: entities
description: Domain model entities
paths:
# Generated from entity api_operations
components:
schemas:
# Generated from entity specifications
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
OAuth2:
type: oauth2
flows:
authorizationCode:
authorizationUrl: https://auth.example.org/oauth/authorize
tokenUrl: https://auth.example.org/oauth/token
scopes:
read: Read access
write: Write access
admin: Administrative access
security:
- BearerAuth: []
- OAuth2: [read, write]
Schema Generation Rules¶
1. Entity Schema¶
For each entity specification, generate a schema component:
components:
schemas:
{EntityName}:
type: object
description: |
{description from specification}
{purpose from specification}
required:
# List all required properties
- {required_property}
properties:
{propertyName}:
type: {openapi_type}
format: {format}
description: {property_description}
example: {example}
minLength: {min_length}
maxLength: {max_length}
pattern: {pattern}
minimum: {minimum}
maximum: {maximum}
readOnly: {true if immutable or auto_generated}
example:
# Include JSON example from specification
2. Enumeration Schema¶
For enumeration types:
components:
schemas:
{EnumName}:
type: string
description: {description}
enum:
- {Value1}
- {Value2}
- {Value3}
example: {example}
x-enum-descriptions:
{Value1}: {description}
{Value2}: {description}
{Value3}: {description}
3. Path Generation¶
For each api_operation in the entity specification:
paths:
{api_endpoint}:
{http_method}:
operationId: {operation}_{entity_id}
tags:
- entities
summary: {operation} {entity_name}
description: {operation description}
parameters:
# From api_operation parameters or query_parameters
requestBody:
# If operation has request_body
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/{EntityName}'
responses:
'{status_code}':
description: {status description}
content:
application/json:
schema:
$ref: '#/components/schemas/{EntityName}'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/Conflict'
'500':
$ref: '#/components/responses/InternalServerError'
Complete Example¶
For the Population entity:
paths:
/api/v1/populations:
get:
operationId: list_populations
tags:
- entities
summary: List populations
description: Retrieve a paginated list of population entities
parameters:
- name: page
in: query
description: Page number
schema:
type: integer
default: 1
minimum: 1
- name: page_size
in: query
description: Number of items per page
schema:
type: integer
default: 50
minimum: 1
maximum: 100
- name: search
in: query
description: Search in name and description
schema:
type: string
responses:
'200':
description: Successful response
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/Population'
pagination:
$ref: '#/components/schemas/PaginationInfo'
post:
operationId: create_population
tags:
- entities
summary: Create a new population
description: Create a new population entity
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/PopulationCreate'
responses:
'201':
description: Population created successfully
headers:
Location:
schema:
type: string
format: uri
description: URI of the created resource
content:
application/json:
schema:
$ref: '#/components/schemas/Population'
'400':
$ref: '#/components/responses/BadRequest'
/api/v1/populations/{id}:
parameters:
- name: id
in: path
required: true
description: Population identifier
schema:
type: string
format: uuid
get:
operationId: get_population
tags:
- entities
summary: Get a population
description: Retrieve a population by its identifier
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/Population'
'404':
$ref: '#/components/responses/NotFound'
put:
operationId: update_population
tags:
- entities
summary: Update a population
description: Update an existing population entity
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/PopulationUpdate'
responses:
'200':
description: Population updated successfully
content:
application/json:
schema:
$ref: '#/components/schemas/Population'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/Conflict'
delete:
operationId: delete_population
tags:
- entities
summary: Delete a population
description: Delete a population entity
responses:
'204':
description: Population deleted successfully
'404':
$ref: '#/components/responses/NotFound'
'409':
description: Cannot delete (referenced by other entities)
content:
application/json:
schema:
$ref: '#/components/responses/Conflict'
components:
schemas:
Population:
type: object
description: |
A specific subset of a universe that is defined with precise
temporal and geographic boundaries.
required:
- id
- createdDate
- modifiedDate
properties:
id:
type: string
format: uuid
description: Unique identifier for the population
readOnly: true
example: "123e4567-e89b-12d3-a456-426614174000"
name:
type: string
minLength: 1
maxLength: 255
description: The name of the population
example: "Swedish labour force Q1 2024"
description:
type: string
minLength: 1
maxLength: 8000
description: Detailed description of the population
eventPeriod:
type: object
description: The time period during which events occurred
required:
- start
properties:
start:
type: string
format: date
end:
type: string
format: date
referencePeriod:
type: object
description: The time period to which the data refers
required:
- start
properties:
start:
type: string
format: date
end:
type: string
format: date
geography:
type: string
minLength: 1
maxLength: 8000
description: Geographic scope or boundaries
example: "Sweden, all municipalities"
createdDate:
type: string
format: date-time
description: When the population was created
readOnly: true
modifiedDate:
type: string
format: date-time
description: When the population was last modified
readOnly: true
version:
type: string
pattern: "^\\d+\\.\\d+\\.\\d+$"
description: Version number (semantic versioning)
example: "1.0.0"
example:
id: "pop-12345678-1234-1234-1234-123456789001"
name: "Swedish labour force Q1 2024"
description: "Persons aged 16-64 registered as residents in Sweden..."
eventPeriod:
start: "2024-01-01"
referencePeriod:
start: "2024-01-01"
end: "2024-03-31"
geography: "Sweden, all municipalities"
createdDate: "2024-01-15T10:30:00Z"
modifiedDate: "2024-01-15T10:30:00Z"
version: "1.0.0"
PopulationCreate:
type: object
description: Schema for creating a new population
properties:
# Exclude auto-generated fields (id, createdDate, modifiedDate)
name:
type: string
minLength: 1
maxLength: 255
description:
type: string
minLength: 1
maxLength: 8000
eventPeriod:
$ref: '#/components/schemas/DateRangeOpenEnded'
referencePeriod:
$ref: '#/components/schemas/DateRangeOpenEnded'
geography:
type: string
minLength: 1
maxLength: 8000
version:
type: string
pattern: "^\\d+\\.\\d+\\.\\d+$"
PopulationUpdate:
type: object
description: Schema for updating a population
properties:
# Allow updating all writable fields
name:
type: string
minLength: 1
maxLength: 255
description:
type: string
minLength: 1
maxLength: 8000
eventPeriod:
$ref: '#/components/schemas/DateRangeOpenEnded'
referencePeriod:
$ref: '#/components/schemas/DateRangeOpenEnded'
geography:
type: string
minLength: 1
maxLength: 8000
version:
type: string
pattern: "^\\d+\\.\\d+\\.\\d+$"
DateRangeOpenEnded:
type: object
anyOf:
- required: [start]
- required: [end]
properties:
start:
type: string
format: date
end:
type: string
format: date
PaginationInfo:
type: object
properties:
page:
type: integer
minimum: 1
pageSize:
type: integer
minimum: 1
totalItems:
type: integer
minimum: 0
totalPages:
type: integer
minimum: 0
Error:
type: object
required:
- code
- message
properties:
code:
type: string
message:
type: string
details:
type: array
items:
type: object
properties:
field:
type: string
message:
type: string
responses:
BadRequest:
description: Bad request
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Unauthorized:
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Forbidden:
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
NotFound:
description: Resource not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Conflict:
description: Conflict
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
InternalServerError:
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Best Practices¶
- Versioning: Use semantic versioning in the API path (/v1, /v2)
- DTOs: Create separate schemas for Create/Update operations
- Pagination: Always include pagination for list operations
- Error Handling: Use standard error response schemas
- HATEOAS: Include links in responses for discoverability
- Filtering: Support filtering via query parameters
- Sorting: Support sorting via query parameters
- Field Selection: Support sparse fieldsets
- Documentation: Include comprehensive descriptions and examples
- Security: Define security schemes and apply to operations
Output Location¶
api/
├── openapi.yaml # Complete OpenAPI specification
├── openapi-components/ # Reusable components
│ ├── schemas/
│ │ └── entities.yaml
│ ├── parameters.yaml
│ ├── responses.yaml
│ └── security.yaml
└── paths/
└── entities/
├── populations.yaml
├── universes.yaml
└── data-sets.yaml
Validation¶
After generation: 1. Validate against OpenAPI 3.0.3 schema 2. Use tools like Spectral for linting 3. Generate documentation with Redoc or Swagger UI 4. Test with Postman or similar tools