API Documentation
OpenAPI/Swagger: The Industry Standard for API Description
Read aloud is unavailable in this browser.
The Language APIs Speak
In the world of distributed systems, APIs are the universal language of service communication. But a language without grammar, vocabulary, or dictionary is useless—and for decades, APIs suffered from exactly this problem. Each API was a unique snowflake, documented in ad-hoc formats, from Word documents to wiki pages to nothing at all.
OpenAPI—originally known as Swagger—changed everything. It introduced a machine-readable, standardized specification that serves as both contract and documentation for RESTful APIs. Today, OpenAPI is not merely a nice-to-have; it is the foundational layer upon which the entire API economy operates.
This page provides an exhaustive exploration of OpenAPI: its history, philosophy, structure, and practical application. By the end, you'll understand not just how to write OpenAPI specifications, but why this standard matters and how it enables the tooling ecosystem that powers modern API development.
By the end of this page, you will understand: (1) The historical evolution from Swagger to OpenAPI 3.x; (2) The complete structure of an OpenAPI specification document; (3) How to define paths, operations, parameters, request/response bodies, and security schemes; (4) The relationship between OpenAPI and code generation, validation, and testing tools; (5) Best practices for maintaining accurate, useful API specifications.
Historical Context: From Swagger to OpenAPI
Understanding where OpenAPI came from illuminates why it was designed the way it was—and why it has achieved near-universal adoption.
The Pre-Swagger Era: API Documentation Chaos
Before standardized API descriptions, developers faced a fragmented landscape:
- Ad-hoc documentation: APIs were documented in whatever format the team preferred—Word documents, PDFs, HTML pages, README files, or nothing at all
- Manual synchronization: Documentation was written separately from code, leading to inevitable drift as APIs evolved
- No machine readability: Tools couldn't automatically understand API structures, making code generation, testing, and validation manual processes
- Vendor lock-in: Each API provider used proprietary formats, forcing consumers to learn new documentation conventions for every integration
The Birth of Swagger (2010-2011)
Swagger was created by Tony Tam at Wordnik, a dictionary API company facing the documentation challenges described above. The core insight was revolutionary yet simple:
What if API documentation could be machine-readable, living alongside or generated from code, and serve as a single source of truth?
Swagger introduced three key innovations:
- A specification format: A JSON (later YAML) structure describing every aspect of an API
- UI tooling: An interactive, auto-generated documentation UI (Swagger UI)
- Code generation: Tools to generate server stubs and client SDKs from specifications
| Version | Year | Key Changes | Adoption Impact |
|---|---|---|---|
| Swagger 1.0-1.2 | 2011-2014 | Initial JSON-based specification; resource listings separate from API declarations | Early adopter phase; companies like Reverb, Wordnik |
| Swagger 2.0 | 2014 | Unified specification document; support for JSON and YAML; security definitions; reusable definitions | Mainstream adoption; became industry standard |
| OpenAPI 3.0 | 2017 | Renamed to OpenAPI under Linux Foundation; components for reuse; links/callbacks; multiple servers; improved request body handling | Enterprise adoption; major cloud providers integrate |
| OpenAPI 3.1 | 2021 | Full JSON Schema compatibility; webhooks support; improved security; path item object references | Current standard; complete JSON Schema alignment |
The SmartBear Acquisition and OpenAPI Initiative (2015-2017)
In 2015, SmartBear Software acquired the Swagger brand and specification from Reverb Technologies (Wordnik's parent company). Critically, SmartBear then donated the Swagger specification to the newly formed OpenAPI Initiative under the Linux Foundation.
This was a watershed moment:
- Vendor neutrality: The specification became governed by a consortium, not a single company
- Industry backing: Founding members included Google, IBM, Microsoft, Atlassian, Intuit, PayPal, and SmartBear
- Renamed to OpenAPI: The specification was renamed to reflect its open, vendor-neutral nature
The Swagger vs. OpenAPI distinction is sometimes confusing:
- OpenAPI Specification (OAS): The official name of the specification format
- Swagger: A set of open-source and commercial tools (Swagger UI, Swagger Editor, Swagger Codegen) that implement the specification
Think of OpenAPI as the language and Swagger as a popular suite of tools that speak that language.
The OpenAPI Initiative's governance model ensures that no single vendor can unilaterally change the specification to favor their products. This neutrality is why competitors like AWS, Google Cloud, and Microsoft Azure all support OpenAPI—it's safe common ground. When evaluating any specification format, consider: Who governs it? Can it be changed arbitrarily? Will it exist in 10 years?
OpenAPI Document Structure: Anatomy of a Specification
An OpenAPI document is a JSON or YAML file that completely describes a RESTful API. Understanding its structure is fundamental to writing effective specifications.
Top-Level Structure
Every OpenAPI 3.x document has the following top-level objects:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
openapi: "3.1.0" # Required: OpenAPI version info: # Required: API metadata title: "User Management API" version: "1.0.0" description: "API for managing users and their profiles" termsOfService: "https://api.example.com/terms" contact: name: "API Support Team" url: "https://support.example.com" email: "api-support@example.com" license: name: "Apache 2.0" url: "https://www.apache.org/licenses/LICENSE-2.0.html" servers: # Optional: API server URLs - url: "https://api.example.com/v1" description: "Production server" - url: "https://staging-api.example.com/v1" description: "Staging server" - url: "https://localhost:3000/v1" description: "Local development" paths: # Required: API endpoints /users: get: summary: "List all users" # ... operation details components: # Optional: Reusable schemas schemas: {} responses: {} parameters: {} examples: {} requestBodies: {} headers: {} securitySchemes: {} links: {} callbacks: {} security: # Optional: Global security - bearerAuth: [] tags: # Optional: Grouping - name: Users description: "User management operations" externalDocs: # Optional: External docs description: "Full API documentation" url: "https://docs.example.com"Field-by-Field Breakdown
Let's examine each top-level field in detail:
openapi (Required)
The version of the OpenAPI Specification this document uses. This is a string, not a number—"3.1.0" not 3.1. Tools use this to determine how to parse the document.
info (Required)
Metadata about the API itself, not about the specification. The title and version are required; everything else is optional but strongly recommended:
description: Supports CommonMark (Markdown), enabling rich formattingcontact: Who to reach for API issueslicense: Legal terms for API usagetermsOfService: URL linking to terms
servers (Optional but Common)
Defines the base URLs for the API. OpenAPI 3.x allows multiple servers, enabling:
- Environment-specific URLs (prod, staging, dev)
- Regional endpoints
- Server variables for dynamic construction
1234567891011121314
servers: - url: "https://{environment}.api.example.com/{version}" description: "Dynamic server URL" variables: environment: default: "prod" enum: - "prod" - "staging" - "dev" description: "Deployment environment" version: default: "v1" description: "API version"paths (Required)
The core of the specification—defines all API endpoints and their operations. We'll explore this in depth in the next section.
components (Optional but Essential for Reuse)
A container for reusable objects. Instead of duplicating schema definitions across endpoints, define them once in components and reference them with $ref. This follows the DRY (Don't Repeat Yourself) principle.
security (Optional)
Global security requirements. Can be overridden at the operation level.
tags (Optional)
Logical groupings for operations, used by documentation tools to organize endpoints.
externalDocs (Optional)
Link to external documentation for additional context.
While OpenAPI supports both JSON and YAML, YAML is strongly preferred for hand-written specifications due to its readability. JSON is typically used for machine-generated specs or when strict interoperability is required. Tools can convert between them losslessly.
Paths and Operations: Defining API Endpoints
The paths object is where the API's functionality is defined. Each path corresponds to an endpoint, and each operation (GET, POST, PUT, etc.) describes what that endpoint does.
Path Object Structure
Paths are relative URLs (the server URL is prepended). Path parameters are enclosed in curly braces:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
paths: /users: get: summary: "List Users" description: "Returns a paginated list of all users" operationId: "listUsers" tags: - Users parameters: - name: limit in: query description: "Maximum number of users to return" required: false schema: type: integer minimum: 1 maximum: 100 default: 20 - name: offset in: query description: "Number of users to skip" required: false schema: type: integer minimum: 0 default: 0 responses: '200': description: "Successful response" content: application/json: schema: type: object properties: users: type: array items: $ref: '#/components/schemas/User' total: type: integer description: "Total number of users" limit: type: integer offset: type: integer '401': $ref: '#/components/responses/UnauthorizedError' '500': $ref: '#/components/responses/InternalError' post: summary: "Create User" description: "Creates a new user in the system" operationId: "createUser" tags: - Users requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateUserRequest' examples: basicUser: summary: "Basic user creation" value: email: "user@example.com" name: "John Doe" adminUser: summary: "Admin user creation" value: email: "admin@example.com" name: "Admin User" role: "admin" responses: '201': description: "User created successfully" content: application/json: schema: $ref: '#/components/schemas/User' headers: Location: description: "URL of the created user" schema: type: string format: uri '400': $ref: '#/components/responses/ValidationError' '409': description: "User already exists" content: application/json: schema: $ref: '#/components/schemas/Error' /users/{userId}: parameters: - name: userId in: path description: "Unique identifier of the user" required: true schema: type: string format: uuid get: summary: "Get User by ID" operationId: "getUserById" tags: - Users responses: '200': description: "User found" content: application/json: schema: $ref: '#/components/schemas/User' '404': $ref: '#/components/responses/NotFoundError' put: summary: "Update User" operationId: "updateUser" tags: - Users requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateUserRequest' responses: '200': description: "User updated successfully" content: application/json: schema: $ref: '#/components/schemas/User' '404': $ref: '#/components/responses/NotFoundError' delete: summary: "Delete User" operationId: "deleteUser" tags: - Users responses: '204': description: "User deleted successfully" '404': $ref: '#/components/responses/NotFoundError'Operation Object Deep Dive
Each HTTP method (GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD, TRACE) under a path is an Operation Object. Let's examine the key fields:
operationId (Strongly Recommended)
A unique string identifying the operation. This is used by code generators to create method names. Best practices:
- Use camelCase or snake_case consistently
- Make it descriptive:
listUsers,getUserById,createUser - Ensure uniqueness across the entire specification
summary vs description
summary: Short one-line description (appears in documentation navigation)description: Full detailed description, supports Markdown
parameters
Parameters can be defined at the path level (shared by all operations) or operation level. Types include:
- path: Part of the URL path (e.g.,
/users/{userId}) - query: Query string parameters (e.g.,
?limit=10) - header: Custom request headers
- cookie: Cookie values
requestBody (POST, PUT, PATCH)
Describes the request body content, supporting multiple media types (JSON, XML, form data).
responses (Required)
Maps HTTP status codes to response definitions. At minimum, one response must be defined.
- •Always include operationId — Enables code generation and API analytics
- •Use appropriate HTTP methods — GET for reads, POST for creates, PUT for full updates, PATCH for partial updates, DELETE for removals
- •Document all response codes — Include success cases (2xx), client errors (4xx), and server errors (5xx)
- •Provide examples — Show realistic request/response examples to help consumers understand usage
- •Use tags consistently — Group related operations for logical documentation organization
- •Define security requirements — Explicitly state authentication/authorization requirements per operation
Components and Reusability: DRY API Design
The components object is the heart of specification maintainability. It contains reusable definitions that can be referenced throughout the document using JSON Reference ($ref) syntax.
Why Components Matter
Without components, you'd repeat schema definitions everywhere:
# BAD: Repeated User schema
paths:
/users:
get:
responses:
'200':
content:
application/json:
schema:
type: object
properties:
id: { type: string }
email: { type: string }
name: { type: string }
/users/{id}:
get:
responses:
'200':
content:
application/json:
schema:
type: object
properties:
id: { type: string }
email: { type: string }
name: { type: string }
With components, you define once and reference everywhere:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
components: # ========================================== # SCHEMAS: Data model definitions # ========================================== schemas: User: type: object description: "A user in the system" required: - id - email - createdAt properties: id: type: string format: uuid description: "Unique user identifier" example: "550e8400-e29b-41d4-a716-446655440000" email: type: string format: email description: "User's email address" example: "john@example.com" name: type: string description: "User's full name" example: "John Doe" role: type: string enum: - user - admin - moderator default: user description: "User's role in the system" createdAt: type: string format: date-time description: "Timestamp when user was created" updatedAt: type: string format: date-time description: "Timestamp when user was last updated" CreateUserRequest: type: object required: - email - name properties: email: type: string format: email name: type: string minLength: 1 maxLength: 100 role: type: string enum: [user, admin, moderator] UpdateUserRequest: type: object properties: name: type: string minLength: 1 maxLength: 100 role: type: string enum: [user, admin, moderator] minProperties: 1 # At least one property must be provided Error: type: object required: - code - message properties: code: type: string description: "Machine-readable error code" message: type: string description: "Human-readable error message" details: type: array items: type: object properties: field: type: string message: type: string PaginationMeta: type: object properties: total: type: integer description: "Total number of items" limit: type: integer description: "Items per page" offset: type: integer description: "Current offset" hasMore: type: boolean description: "Whether more items exist" # ========================================== # RESPONSES: Reusable response definitions # ========================================== responses: UnauthorizedError: description: "Authentication required" content: application/json: schema: $ref: '#/components/schemas/Error' example: code: "UNAUTHORIZED" message: "Authentication credentials are missing or invalid" NotFoundError: description: "Resource not found" content: application/json: schema: $ref: '#/components/schemas/Error' example: code: "NOT_FOUND" message: "The requested resource was not found" ValidationError: description: "Request validation failed" content: application/json: schema: $ref: '#/components/schemas/Error' example: code: "VALIDATION_ERROR" message: "Request validation failed" details: - field: "email" message: "Invalid email format" InternalError: description: "Internal server error" content: application/json: schema: $ref: '#/components/schemas/Error' example: code: "INTERNAL_ERROR" message: "An unexpected error occurred" # ========================================== # PARAMETERS: Reusable parameter definitions # ========================================== parameters: LimitParam: name: limit in: query description: "Maximum number of items to return" required: false schema: type: integer minimum: 1 maximum: 100 default: 20 OffsetParam: name: offset in: query description: "Number of items to skip" required: false schema: type: integer minimum: 0 default: 0 UserIdParam: name: userId in: path description: "Unique user identifier" required: true schema: type: string format: uuid # ========================================== # SECURITY SCHEMES: Authentication methods # ========================================== securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT description: "JWT Bearer token authentication" apiKeyAuth: type: apiKey in: header name: X-API-Key description: "API key authentication" oauth2: type: oauth2 description: "OAuth2 authentication" flows: authorizationCode: authorizationUrl: "https://auth.example.com/authorize" tokenUrl: "https://auth.example.com/token" scopes: read:users: "Read user information" write:users: "Create and update users" delete:users: "Delete users"Using References ($ref)
The $ref keyword creates a JSON Pointer reference to a reusable component:
# Reference to a component
$ref: '#/components/schemas/User'
# Reference to external file
$ref: './schemas/user.yaml'
# Reference to URL
$ref: 'https://api.example.com/specs/common.yaml#/components/schemas/Error'
Reference Restrictions:
- References must point to valid locations
- Circular references are allowed but should be used carefully
- Some fields cannot contain
$ref(e.g.,operationId) - References can only point to compatible object types
For large APIs, consider splitting components into separate files: schemas/user.yaml, responses/errors.yaml, parameters/pagination.yaml. The main spec references these via relative paths. This improves maintainability and enables team members to work on different parts simultaneously.
Schema Definition: Describing Data with JSON Schema
OpenAPI 3.1 is fully compatible with JSON Schema Draft 2020-12, making it the most powerful version for data modeling. Earlier versions (3.0.x) used a subset of JSON Schema Draft 7.
Primitive Types
JSON Schema defines the following primitive types:
| Type | Description | Format Examples |
|---|---|---|
string | Text values | date, date-time, email, uri, uuid, password, byte, binary |
number | Floating-point | double, float |
integer | Whole numbers | int32, int64 |
boolean | True/false | — |
array | Ordered list | — |
object | Key-value structure | — |
null | Null value | — |
String Constraints
12345678910111213141516171819202122232425262728293031
# String with various constraintsschemas: Username: type: string minLength: 3 maxLength: 30 pattern: "^[a-zA-Z0-9_]+$" description: "Alphanumeric username with underscores" Email: type: string format: email description: "Valid email address" Password: type: string format: password # Hints to UI to mask input minLength: 8 maxLength: 128 pattern: "^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).*$" description: "Requires uppercase, lowercase, and number" UUID: type: string format: uuid example: "550e8400-e29b-41d4-a716-446655440000" ISO8601DateTime: type: string format: date-time example: "2024-03-15T10:30:00Z"Numeric Constraints
1234567891011121314151617181920212223242526
schemas: Age: type: integer minimum: 0 maximum: 150 description: "User's age in years" Price: type: number format: double minimum: 0 exclusiveMinimum: true # Must be > 0, not >= 0 multipleOf: 0.01 # Two decimal places description: "Price in dollars" Percentage: type: number minimum: 0 maximum: 100 description: "Percentage value 0-100" Rating: type: integer minimum: 1 maximum: 5 description: "Star rating from 1 to 5"Object Schemas and Composition
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
schemas: # Base entity with common fields BaseEntity: type: object properties: id: type: string format: uuid readOnly: true createdAt: type: string format: date-time readOnly: true updatedAt: type: string format: date-time readOnly: true # Composition using allOf (intersection) User: allOf: - $ref: '#/components/schemas/BaseEntity' - type: object required: - email - name properties: email: type: string format: email name: type: string # Polymorphism using oneOf (union with discriminator) Pet: oneOf: - $ref: '#/components/schemas/Cat' - $ref: '#/components/schemas/Dog' - $ref: '#/components/schemas/Bird' discriminator: propertyName: petType mapping: cat: '#/components/schemas/Cat' dog: '#/components/schemas/Dog' bird: '#/components/schemas/Bird' Cat: type: object required: - petType - name properties: petType: type: string enum: [cat] name: type: string meowVolume: type: integer Dog: type: object required: - petType - name properties: petType: type: string enum: [dog] name: type: string barkVolume: type: integer Bird: type: object required: - petType - name properties: petType: type: string enum: [bird] name: type: string canFly: type: boolean # anyOf: at least one schema must match SearchResult: anyOf: - $ref: '#/components/schemas/User' - $ref: '#/components/schemas/Product' - $ref: '#/components/schemas/Order' # not: must not match the schema NotEmptyString: type: string not: maxLength: 0- •allOf — Object must match ALL schemas (intersection). Used for inheritance/mixins
- •oneOf — Object must match EXACTLY ONE schema (exclusive union). Used for polymorphism
- •anyOf — Object must match AT LEAST ONE schema (union). Used for flexible typing
- •not — Object must NOT match the schema. Used for exclusions
Security Schemes: Documenting API Authentication
Security is a first-class concept in OpenAPI. The specification provides comprehensive support for documenting API authentication and authorization requirements.
Security Scheme Types
OpenAPI supports five security scheme types:
| Type | Description | Use Cases |
|---|---|---|
http | HTTP authentication | Basic auth, Bearer tokens (JWT) |
apiKey | API in header, query, or cookie | Simple API key authentication |
oauth2 | OAuth 2.0 flows | User authentication with scopes |
openIdConnect | OpenID Connect | SSO, enterprise identity |
mutualTLS | Client certificate | Service-to-service auth |
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
components: securitySchemes: # HTTP Bearer (JWT) bearerAuth: type: http scheme: bearer bearerFormat: JWT description: | JWT access token obtained from the /auth/login endpoint. Include in Authorization header: `Bearer <token>` # HTTP Basic basicAuth: type: http scheme: basic description: "Base64-encoded username:password" # API Key in header apiKeyHeader: type: apiKey in: header name: X-API-Key description: "API key for service-to-service communication" # API Key in query parameter apiKeyQuery: type: apiKey in: query name: api_key description: "API key (use header method for production)" # OAuth2 with multiple flows oauth2: type: oauth2 description: "OAuth 2.0 authentication" flows: # Authorization Code flow (web applications) authorizationCode: authorizationUrl: "https://auth.example.com/oauth/authorize" tokenUrl: "https://auth.example.com/oauth/token" refreshUrl: "https://auth.example.com/oauth/refresh" scopes: read: "Read access to protected resources" write: "Write access to protected resources" admin: "Admin access" # Client Credentials flow (machine-to-machine) clientCredentials: tokenUrl: "https://auth.example.com/oauth/token" scopes: read: "Read access" write: "Write access" # Implicit flow (legacy, not recommended) implicit: authorizationUrl: "https://auth.example.com/oauth/authorize" scopes: read: "Read access" # OpenID Connect openIdConnect: type: openIdConnect openIdConnectUrl: "https://auth.example.com/.well-known/openid-configuration" description: "OpenID Connect for SSO"Applying Security Requirements
Security can be applied globally or per-operation:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748
# Global security (applies to all operations)security: - bearerAuth: [] # Or require multiple schemes (AND relationship)security: - bearerAuth: [] apiKeyHeader: [] # Or allow alternative schemes (OR relationship) security: - bearerAuth: [] - apiKeyHeader: [] paths: /public/health: get: summary: "Health check endpoint" # Override global security to make this public security: [] responses: '200': description: "Service is healthy" /admin/users: get: summary: "List all users (admin only)" # Require specific OAuth2 scopes security: - oauth2: - admin - read responses: '200': description: "List of users" /users/me: get: summary: "Get current user" # Any of these auth methods works security: - bearerAuth: [] - oauth2: - read - cookieAuth: [] responses: '200': description: "Current user details"The security array has specific semantics: Multiple items in the array represent OR (alternatives). Multiple schemes within a single item represent AND (all required). An empty array security: [] means no authentication required. Understanding this is critical for accurate documentation.
The OpenAPI Tooling Ecosystem
The true power of OpenAPI lies not in the specification itself, but in the vast ecosystem of tools that consume it. An OpenAPI document enables:
Documentation Generation
Transform specifications into beautiful, interactive documentation:
- Swagger UI: The original interactive documentation tool
- ReDoc: Clean, three-panel documentation layout
- Stoplight Elements: Customizable React components
- RapiDoc: Lightweight, customizable alternative
Code Generation
Generate server stubs, client SDKs, and types:
- OpenAPI Generator: Community-driven, 50+ language targets
- Swagger Codegen: Original code generator from SmartBear
- orval: TypeScript/React-focused with excellent React Query support
- openapi-typescript: Type-safe TypeScript types
Validation and Testing
Validate requests/responses against specifications:
- Spectral: Linting and style enforcement
- AJV: JSON Schema validation for runtime
- Prism: Mock server from Stoplight
- Dredd: API testing against specifications
- Schemathesis: Property-based API testing
| Category | Tool | Purpose | Best For |
|---|---|---|---|
| Documentation | Swagger UI | Interactive API explorer | Developer portals |
| Documentation | ReDoc | Clean reference docs | External documentation |
| Code Generation | OpenAPI Generator | Server/client generation | Multi-language teams |
| Code Generation | orval | TypeScript + React Query | Modern web apps |
| Linting | Spectral | Style enforcement | CI/CD pipelines |
| Mocking | Prism | Mock server | Frontend development |
| Testing | Schemathesis | Property-based testing | Security testing |
| Editing | Swagger Editor | Browser-based editor | Quick edits |
| Editing | Stoplight Studio | Visual editor | Non-developers |
1234567891011121314151617181920212223242526272829
# .spectral.yaml - OpenAPI linting configurationextends: - spectral:oas rules: # Enforce operation IDs operation-operationId: severity: error # Require descriptions operation-description: severity: warn # Enforce naming conventions operation-operationId-valid-in-url: severity: error # Custom rule: require examples oas3-valid-media-example: severity: warn # Custom rule: no deprecated operations without replacement custom-deprecated-with-replacement: given: "$..deprecated" then: field: x-deprecated-description function: truthy severity: warn message: "Deprecated operations should have x-deprecated-description"Design-First: Write OpenAPI spec first, generate code from it. Ensures documentation is always accurate. Best for public APIs and API-first organizations. Code-First: Write code first, generate OpenAPI from annotations. Faster initial development but risks spec drift. Best for internal APIs and rapid prototyping. Most mature organizations use design-first for external APIs and code-first for internal ones.
KEEP LEARNING
There’s more to understand.
You’re reading the introduction. Create a free account to read the complete lesson, save notes and keep track of your progress.