OpenAPI 3.1 From the Ground Up: The Key Building Blocks Explained
AI generated
{ }
GET
OpenAPI 3.1 · REST API · API Design · YAML · Documentation
OpenAPI 3.1 From the Ground Up
The Key Building Blocks Explained

OpenAPI 3.1 is the industry standard for REST API documentation, yet many teams only use a fraction of its capabilities. This guide explains all the essential building blocks: from Info and Paths through Components and Schemas to Security and Callbacks, with complete practical YAML examples.

22 min read Paths · Components · Schemas · Security · Parameters · Responses OpenAPI 3.1 · JSON Schema 2020-12 · YAML

1. OpenAPI 3.1 vs. 3.0: what changed

OpenAPI 3.1 was released in February 2021 and brought the most significant change since version 2.0: full compatibility with JSON Schema 2020-12. In OpenAPI 3.0, the schema object was a customized subset of JSON Schema Draft 7 with proprietary extensions such as nullable. These deviations caused tooling issues and semantic contradictions. OpenAPI 3.1 replaces that subset with full JSON Schema 2020-12 compatibility: type can now be an array (type: [string, null] instead of nullable: true), $ref may be combined with other keywords, and every JSON Schema keyword is valid.

The second important change concerns webhooks: OpenAPI 3.1 has a new top-level field webhooks that documents callbacks an API provider sends to its consumers. In 3.0, webhooks had to be buried inside callbacks within operations. The new field makes asynchronous notifications a first-class citizen of the API specification. The third notable change: info.license now supports an identifier field following the SPDX standard instead of only a URL.

In practice, migrating from 3.0 to 3.1 mainly means: replacing every nullable: true with type: [string, null] or oneOf: [{type: string}, {type: 'null'}], adjusting exclusiveMinimum/exclusiveMaximum (in 3.1 these are now numbers instead of booleans, as defined in JSON Schema 2020-12), and setting the openapi version to 3.1.0. Tooling support for 3.1 has been widely available since 2023, Swagger UI, Redoc, Stoplight and most code generators fully support 3.1.

2. Info, Servers and the Root Object

Every OpenAPI 3.1 specification starts with the root object, which defines the version, metadata and the available servers. The info object is required and must contain at least title and version. The version is the API version, not the OpenAPI specification version, a common mistake for newcomers. The servers array defines the base URLs of the API for different environments. Without servers, clients assume the server is relative to the location of the specification.

The externalDocs field lets you link to further documentation. Root-level tags define groups with descriptions that are then referenced in operations, which matters for a clean documentation structure. The info.contact object with name, email and URL is indispensable for public APIs. The info.termsOfService field links to the terms of use. All of these fields are relevant metadata for automatically generated documentation pages and developer portals.


# openapi.yaml - Root Object, complete OpenAPI 3.1 example
openapi: 3.1.0

info:
  title: Mironsoft Commerce API
  version: "2.0.0"
  description: |
    REST API for the Mironsoft Commerce stack.
    Supported authentication: Bearer JWT, OAuth 2.0 (Authorization Code).
  contact:
    name: Mironsoft API Support
    email: api@mironsoft.de
    url: https://mironsoft.de/support
  license:
    name: Proprietary
    identifier: LicenseRef-Proprietary

servers:
  - url: https://api.mironsoft.de/v2
    description: Production
  - url: https://api-staging.mironsoft.de/v2
    description: Staging
  - url: http://localhost:8080/v2
    description: Local Development

tags:
  - name: orders
    description: Create, read and manage orders
    externalDocs:
      description: Order lifecycle documentation
      url: https://docs.mironsoft.de/orders
  - name: products
    description: Product catalog and inventory
  - name: auth
    description: Authentication and token management

externalDocs:
  description: Full Mironsoft Developer Docs
  url: https://docs.mironsoft.de

3. Paths and Operations: the core of the API

The paths object is the centerpiece of any OpenAPI specification. Each key is a URL path template, each value a Path Item Object that defines the HTTP methods and their operations. An operation requires at least a responses object, but should also include operationId, summary, description and tags. The operationId is especially important: code generators use it as the function name, so it must be unique, descriptive and follow a consistent convention, e.g. getOrderById, createOrder, updateOrderStatus.

Path templates use curly braces for path parameters: /orders/{orderId}. Every parameter defined in curly braces must be declared in the parameters array of the operation (or the path item) with in: path. Path parameters are always required: true, the specification mandates this. Parameters defined at the path item level apply to all operations of that path and can be overridden in individual operations. This pattern is useful for auth parameters that apply to all methods of an endpoint.


# Paths and Operations - complete example for /orders/{orderId}
paths:
  /orders:
    post:
      operationId: createOrder
      summary: Create a new order
      tags: [orders]
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOrderRequest'
            examples:
              standard_order:
                $ref: '#/components/examples/StandardOrder'
      responses:
        '201':
          description: Order created successfully
          headers:
            Location:
              description: URL of the new resource
              schema:
                type: string
                format: uri
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Order'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /orders/{orderId}:
    parameters:
      - name: orderId
        in: path
        required: true
        schema:
          type: string
          format: uuid
        description: Unique order ID (UUID v4)

    get:
      operationId: getOrderById
      summary: Retrieve an order
      tags: [orders]
      security:
        - BearerAuth: []
      responses:
        '200':
          description: Order found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Order'
        '404':
          $ref: '#/components/responses/NotFound'

4. Parameters: path, query, header and cookie

OpenAPI distinguishes four kinds of parameters via the in field: path, query, header and cookie. Path parameters are part of the URL path and always required. Query parameters appear after the ? and are typically optional, used for filtering, sorting and pagination. Header parameters document custom HTTP headers (standard headers such as Authorization are defined via security). Cookie parameters document values transmitted via cookies.

Every parameter has a schema object that defines the data type and validation rules. For query parameters with complex data structures, the style field matters: form (default), spaceDelimited, pipeDelimited or deepObject control how arrays and objects are serialized. The explode: true field (default for form) produces a separate parameter per array element (?tag=a&tag=b), while explode: false combines them (?tag=a,b). These details are decisive for compatibility with automatically generated client code.

5. Schemas with JSON Schema 2020-12

In OpenAPI 3.1, schemas are complete JSON Schema 2020-12 objects. That means full access to every JSON Schema keyword: type as an array, if/then/else for conditional validation, $defs for local definitions, unevaluatedProperties for strict schemas and prefixItems for typed tuple arrays. The most important practical change compared to OpenAPI 3.0: nullable: true no longer exists. Instead, use type: [string, "null"] or a oneOf with a null type.

Well-designed schemas make use of description, example (or examples), default, minimum/maximum, minLength/maxLength, pattern and enum where it makes sense. These fields are not just for documentation, they drive validation in mock servers, code generation in client SDKs and validation logic in server-side middleware layers. A schema without descriptions and examples is technically correct but largely useless for consumers.


# Schemas with JSON Schema 2020-12 - OpenAPI 3.1 example
components:
  schemas:
    Order:
      type: object
      required: [id, status, items, total, createdAt]
      description: Complete order representation
      properties:
        id:
          type: string
          format: uuid
          description: Unique order ID
          examples: ["550e8400-e29b-41d4-a716-446655440000"]
          readOnly: true
        status:
          type: string
          enum: [pending, confirmed, shipped, delivered, cancelled]
          description: Current status of the order
        items:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/OrderItem'
        total:
          type: object
          required: [amount, currency]
          properties:
            amount:
              type: number
              format: decimal
              minimum: 0
              exclusiveMinimum: true
              description: Total amount in the given currency
            currency:
              type: string
              pattern: '^[A-Z]{3}$'
              description: ISO 4217 currency code
              examples: ["EUR", "USD"]
        note:
          type: ["string", "null"]
          maxLength: 500
          description: Optional order note (null when not set)
        createdAt:
          type: string
          format: date-time
          readOnly: true
      additionalProperties: false

6. Components: reuse without duplicates

The components object is the library system of OpenAPI. It holds reusable definitions that can be referenced with $ref. Available sections: schemas, responses, parameters, examples, requestBodies, headers, securitySchemes, links, callbacks and, new in 3.1, pathItems. Consistent use of components is the single most important factor for a maintainable OpenAPI specification. Anyone defining schemas inline inside operations creates duplicates that need adjusting in many places whenever something changes.

For shared error responses, a standardized error schema pays off. An RFC 9457 Problem Details compatible error structure with type, title, status, detail and instance is defined in components/schemas and referenced by every error response in components/responses. That way, all 4xx and 5xx responses automatically share a consistent format that can be updated in a single change.

7. Security schemes: Bearer, OAuth2 and API key

OpenAPI 3.1 supports four security scheme types: http (for Basic and Bearer), apiKey, oauth2 and openIdConnect. Security schemes are defined in components/securitySchemes and can then be applied at the root level as a default or targeted at the operation level. An empty array security: [] on an operation overrides the global security and marks the endpoint as publicly accessible.

OAuth 2.0 in OpenAPI is particularly involved: the oauth2 scheme contains separate configurations for each flow (authorizationCode, clientCredentials, implicit, password) with authorization URL, token URL and scope definitions. Scopes are listed in the operation's security array as a string array. Scope definitions must be complete and descriptive, Swagger UI generates the OAuth consent UI in the interactive documentation from them.


# Security Schemes - Bearer JWT + OAuth 2.0 in OpenAPI 3.1
components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: |
        JWT bearer token. Obtain via POST /auth/token.
        Header: Authorization: Bearer <token>

    OAuth2:
      type: oauth2
      flows:
        authorizationCode:
          authorizationUrl: https://auth.mironsoft.de/oauth/authorize
          tokenUrl: https://auth.mironsoft.de/oauth/token
          refreshUrl: https://auth.mironsoft.de/oauth/token/refresh
          scopes:
            orders:read: Read orders
            orders:write: Create and update orders
            products:read: Read product catalog
            admin: Administrative access to all resources

    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: API key for server-to-server communication

# Global security - applies to all operations
security:
  - BearerAuth: []

# Public endpoint - overrides global security
paths:
  /products:
    get:
      operationId: listProducts
      security: []  # Public endpoint, no auth required
      summary: List products publicly
      tags: [products]
      responses:
        '200':
          description: Product list

8. OpenAPI 3.0 vs. 3.1 side by side

Moving from OpenAPI 3.0 to 3.1 is, for most APIs, not a breaking change at the API level, but it does require adjustments to the specification file. The table below shows the most important differences and migration steps for teams updating an existing 3.0 specification.

Feature OpenAPI 3.0 OpenAPI 3.1 Migration
Nullable fields nullable: true type: [string, "null"] Search & replace, adjust the type
JSON Schema version Subset of Draft 7 Full 2020-12 All new keywords available
Webhooks Nested in callbacks Top-level webhooks Move to its own section
$ref with siblings Not allowed Fully supported No allOf wrapper needed anymore
exclusiveMin/Max Boolean Numeric value Provide the value directly

9. Summary

OpenAPI 3.1 is a thoroughly designed standard that can describe every aspect of a REST API, from simple CRUD endpoints to complex OAuth flows and asynchronous webhooks. The key building blocks: the root object with Info and Servers, the Paths object with operations and operationId, the parameters system with its four types, JSON Schema 2020-12 for schemas, Components for reuse and security schemes for authentication. Teams that apply all of these building blocks consistently get a specification that supports code generation, mock servers, validation and interactive documentation without rework.

The biggest lever for new teams: name operationId consistently and descriptively, centralize all error responses in components/responses, and always define schemas in components/schemas, never inline. These three decisions prevent the most common maintenance problems and make the specification maximally usable for code generators.

OpenAPI 3.1 Building Blocks: The Essentials at a Glance

Root & Paths

info, servers, tags at the root level. Paths with path items and operations. Name operationId uniquely and descriptively, it becomes the function name in SDKs.

Schemas (JSON Schema 2020-12)

type as an array for nullable fields. if/then/else for conditional validation. additionalProperties: false for strict schemas. Always add description and examples.

Components

Define schemas, responses, parameters, examples and requestBodies centrally. $ref for reuse. No inline schemas in operations for maintainable specs.

Security

BearerAuth for JWT, OAuth2 for the authorization code flow, ApiKey for server-to-server. Global security at the root level, security: [] for public endpoints.

Mironsoft

OpenAPI design, API documentation and code generation

Need a professional OpenAPI specification?

We build complete OpenAPI 3.1 specifications for REST APIs, from schema architecture through security configuration to generated client SDKs and mock servers.

Schema design

JSON Schema 2020-12 schemas for every API object

API review

Check existing specs against OpenAPI 3.1 quality standards

Code generation

Generate client SDKs and server stubs from the spec

10. FAQ: OpenAPI 3.1 building blocks

1Biggest difference between 3.0 and 3.1?
Full JSON Schema 2020-12 compatibility. nullable: true is replaced by type: [string, null]. $ref may be combined with other keywords. New top-level webhooks field.
2What is operationId used for?
A unique identifier used by code generators as the function name. Must be unique and descriptive: getOrderById, createOrder, updateOrderStatus.
3$ref vs. inline schemas?
$ref points to components/schemas, inline schemas are defined directly in the operation. Always prefer $ref, it prevents duplicates and eases maintenance.
4Public endpoint without auth?
security: [] at the operation level. The empty array overrides the global security and marks the endpoint as publicly accessible.
5Nullable in OpenAPI 3.1?
type: [string, "null"], type as an array instead of nullable: true. Matches the JSON Schema 2020-12 standard.
6What belongs in components/responses?
All reusable error responses: ValidationError, Unauthorized, Forbidden, NotFound, InternalServerError, with a unified RFC 9457 error schema.
7style: form vs. deepObject?
form: ?filter=value (flat). deepObject: ?filter[status]=active (nested). deepObject for complex filter parameters in REST APIs.
8readOnly vs. writeOnly?
readOnly: only in responses (id, createdAt). writeOnly: only in requests (password). Both control code generation and schema validation.
9Referencing external YAML files?
$ref: './schemas/order.yaml#/Order', relative paths to YAML or JSON. Redocly CLI can merge files before deployment (bundle).
10Tooling support for OpenAPI 3.1?
Widely available since 2023: Swagger UI 5+, Redoc 2.1+, openapi-generator 7+, Redocly CLI, Spectral. All common API gateways support 3.1.