8.8 KiB
8.8 KiB
OpenAPI 3.1 Patterns and Best Practices
Comprehensive guide to OpenAPI 3.1 specification patterns for Grey Haven stack (FastAPI + TanStack Start).
OpenAPI 3.1 Overview
OpenAPI 3.1 is fully compatible with JSON Schema Draft 2020-12.
Key Differences from 3.0: Full JSON Schema compatibility, examples replaces example, webhooks support, better discriminator
Basic Structure
openapi: 3.1.0
info:
title: Grey Haven API
version: 1.0.0
servers:
- url: https://api.greyhaven.com
paths:
/users:
get:
operationId: listUsers
tags: [users]
responses:
'200':
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'
Authentication
JWT Bearer
components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
security:
- BearerAuth: []
OAuth2
components:
securitySchemes:
OAuth2:
type: oauth2
flows:
authorizationCode:
authorizationUrl: https://auth.greyhaven.com/oauth/authorize
tokenUrl: https://auth.greyhaven.com/oauth/token
scopes:
read:users: Read user data
API Key
components:
securitySchemes:
ApiKey:
type: apiKey
in: header
name: X-API-Key
Schema Patterns
Pydantic v2 to OpenAPI
from pydantic import BaseModel, Field
from typing import Literal
class User(BaseModel):
id: str = Field(..., pattern="^usr_[a-z0-9]{16}$")
email: str = Field(..., examples=["user@example.com"])
role: Literal["admin", "member", "guest"] = "member"
Generates:
User:
type: object
required: [id, email]
properties:
id:
type: string
pattern: ^usr_[a-z0-9]{16}$
email:
type: string
examples: ["user@example.com"]
role:
type: string
enum: [admin, member, guest]
default: member
Nullable and Optional
# Optional (can be omitted)
username:
type: string
# Nullable (can be null)
middle_name:
type: [string, 'null']
# Both
nickname:
type: [string, 'null']
Discriminated Unions
PaymentMethod:
type: object
required: [type]
discriminator:
propertyName: type
mapping:
card: '#/components/schemas/CardPayment'
bank: '#/components/schemas/BankPayment'
CardPayment:
allOf:
- $ref: '#/components/schemas/PaymentMethod'
- type: object
properties:
card_number:
type: string
pattern: ^\d{16}$
Response Patterns
Error Response
ErrorResponse:
type: object
required: [error, message]
properties:
error:
type: string
examples: ["VALIDATION_ERROR"]
message:
type: string
details:
type: object
additionalProperties: true
# Use in responses
responses:
'400':
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
Paginated Response
PaginatedUsers:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/User'
pagination:
type: object
properties:
page:
type: integer
minimum: 1
per_page:
type: integer
minimum: 1
maximum: 100
total:
type: integer
total_pages:
type: integer
Multiple Status Codes
responses:
'201':
description: Created
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'202':
description: Accepted (async)
content:
application/json:
schema:
type: object
properties:
job_id:
type: string
Request Body
Required vs Optional
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [email, password] # Required
properties:
email:
type: string
format: email
password:
type: string
minLength: 8
name:
type: string # Optional
File Upload
requestBody:
content:
multipart/form-data:
schema:
properties:
file:
type: string
format: binary
Parameters
Path
parameters:
- name: user_id
in: path
required: true
schema:
type: string
pattern: ^usr_[a-z0-9]{16}$
Query
parameters:
- name: status
in: query
schema:
type: string
enum: [pending, processing, shipped]
- name: created_after
in: query
schema:
type: string
format: date-time
- name: sort
in: query
schema:
type: string
enum: [created_at:asc, created_at:desc]
default: created_at:desc
Headers
components:
parameters:
TenantId:
name: X-Tenant-ID
in: header
required: true
schema:
type: string
paths:
/orders:
post:
parameters:
- $ref: '#/components/parameters/TenantId'
Response Headers
responses:
'200':
headers:
X-RateLimit-Limit:
schema:
type: integer
X-RateLimit-Remaining:
schema:
type: integer
X-Request-ID:
schema:
type: string
format: uuid
Multi-Language Examples
x-codeSamples:
- lang: TypeScript
source: |
const response = await fetch("https://api.greyhaven.com/users", {
method: "POST",
headers: { "Authorization": `Bearer ${token}` },
body: JSON.stringify({ email, password })
});
- lang: Python
source: |
async with httpx.AsyncClient() as client:
response = await client.post(
"https://api.greyhaven.com/users",
headers={"Authorization": f"Bearer {token}"},
json={"email": email, "password": password}
)
- lang: Shell
source: |
curl -X POST https://api.greyhaven.com/users \
-H "Authorization: Bearer $TOKEN" \
-d '{"email": "user@example.com"}'
Webhooks (OpenAPI 3.1)
webhooks:
orderCreated:
post:
requestBody:
content:
application/json:
schema:
type: object
required: [event, data]
properties:
event:
type: string
const: order.created
data:
$ref: '#/components/schemas/Order'
responses:
'200':
description: Received
Best Practices
- Use $ref: Define schemas once, reference everywhere
- Examples: Realistic examples for all schemas
- Error Schemas: Consistent error format
- Validation: Use pattern, minLength, minimum
- Descriptions: Document every field
- operationId: Unique for SDK generation
- Tags: Group related endpoints
- Deprecation: Mark with
deprecated: true - Security: Define at global or operation level
- Versioning: Include in URL (/api/v1/)
Common Patterns
Multi-Tenant
components:
parameters:
TenantId:
name: X-Tenant-ID
in: header
required: true
schema:
type: string
Idempotency
components:
parameters:
IdempotencyKey:
name: Idempotency-Key
in: header
schema:
type: string
format: uuid
Rate Limiting
responses:
'429':
description: Rate limit exceeded
headers:
X-RateLimit-Reset:
schema:
type: integer
FastAPI Integration
from fastapi import FastAPI
app = FastAPI(
title="Grey Haven API",
version="1.0.0",
openapi_version="3.1.0"
)
# Customize OpenAPI
def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
openapi_schema = get_openapi(
title=app.title,
version=app.version,
routes=app.routes
)
# Add security schemes
openapi_schema["components"]["securitySchemes"] = {
"BearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT"
}
}
app.openapi_schema = openapi_schema
return app.openapi_schema
app.openapi = custom_openapi
Validation
Use @redocly/cli for validation:
npx @redocly/cli lint openapi.yaml
Common issues:
- Missing operationId
- Missing response descriptions
- Inconsistent naming
- Missing examples
- Invalid $ref paths
Related: mermaid-diagrams.md | documentation-standards.md | Return to INDEX