DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Enterprise AI Trend Report: Gain insights on ethical AI, MLOps, generative AI, large language models, and much more.

2024 Cloud survey: Share your insights on microservices, containers, K8s, CI/CD, and DevOps (+ enter a $750 raffle!) for our Trend Reports.

PostgreSQL: Learn about the open-source RDBMS' advanced capabilities, core components, common commands and functions, and general DBA tasks.

AI Automation Essentials. Check out the latest Refcard on all things AI automation, including model training, data security, and more.

Related

  • 8 Strategies To Accelerate Web Portal Development
  • Implement OpenAPI for AWS Lambda in Quarkus Using API Gateway Integrator
  • What Is API-First?
  • The Easiest and Quickest Way to Generate an OpenAPI Spec for an Existing Website

Trending

  • Getting Started With NCache Java Edition (Using Docker)
  • Data Processing in GCP With Apache Airflow and BigQuery
  • Modern Digital Authentication Protocols
  • Implement RAG Using Weaviate, LangChain4j, and LocalAI
  1. DZone
  2. Software Design and Architecture
  3. Integration
  4. Why Your OpenAPI Spec Sucks

Why Your OpenAPI Spec Sucks

Discover why your OpenAPI spec might be falling short! Learn how to enhance your API documentation by addressing common issues.

By 
Sharon Pikovski user avatar
Sharon Pikovski
·
Jan. 22, 24 · Opinion
Like (2)
Save
Tweet
Share
2.2K Views

Join the DZone community and get the full member experience.

Join For Free

A mix of anticipation and dread washes over me as I open a new inbound email with an attached specification file. With a heavy sigh, I begin scrolling through its contents, only to be greeted by disappointment yet again.

The API request bodies in this specification file suffer from a lack of essential details, specifically the absence of the actual properties of the HTTP call. This makes it difficult to determine the expectations and behavior of the API. Not only will API consumers have a hard time understanding the API, but the lack of properties also hinders the use of external libraries for validation, analysis, or auto-generation of output (e.g., API mocking, testing, or liblab's auto SDK generation).

After encountering hundreds of specification files (referred to as specs) in my role at liblab, I’ve come to the conclusion that most spec files are in varying degrees of incompletion. Some completely disregard the community standard and omit crucial information while others could use some tweaking and refinement. This has inspired me to write this article with the goal of enhancing the quality of your spec files. It just so happens that this goal also aligns with making my job easier.

In the upcoming sections, we'll go over three common issues that make your OpenAPI spec fall short and examine possible solutions for them. By the end of this article, you’ll be able to elevate your OpenAPI spec, making it more user-friendly for API consumers, including developers, QA engineers, and other stakeholders.

Three Reasons Why Your OpenAPI Spec Sucks

You’re Still Using Swagger

Look, I get it. A lot of us still get confused about the differences between Swagger and OpenAPI. To make things simple you can think of Swagger as the former name of OpenAPI. Many tools are still using the word "Swagger" in their names but this is primarily due to the strong association and recognition that the term Swagger had gained within the developer community.

If your “Swagger” spec is actually an OpenAPI spec (indicated by the presence of "openapi: 3.x.x" at the beginning), all you need to do is update your terminology.

If you’re actually using a Swagger spec (a file that begins with "swagger: 2.0”), it's time to consider an upgrade. Swagger has certain limitations compared to OpenAPI 3, and as newer versions of OpenAPI are released, transitioning will become increasingly challenging.

Notable differences:

  • OpenAPI 3 has support for oneOf and anyOf that Swagger does not provide. Let us look at this example:
openapi: 3.0.0
info:
  title: Payment API
  version: 1.0.0
paths:
  /payments:
    post:
      summary: Create a payment
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - $ref: "#/components/schemas/CreditCardPayment"
                - $ref: "#/components/schemas/OnlinePayment"
                - $ref: "#/components/schemas/CryptoPayment"
      responses:
        "201":
          description: Created
        "400":
          description: Bad Request

In OpenAPI 3, you can explicitly define that the requestBody for a /payments POST call can be one of three options: CreditCardPayment, OnlinePayment, or CryptoPayment. However, in Swagger you would need to create a workaround by adding an object with optional fields for each payment type:

swagger: "2.0"
info:
  title: Payment API
  version: 1.0.0
paths:
  /payments:
    post:
      summary: Create a payment
      consumes:
        - application/json
      produces:
        - application/json
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/Payment"
      responses:
        "201":
          description: Created
        "400":
          description: Bad Request

definitions:
  Payment:
    type: object
    properties:
      creditCardPayment:
        $ref: "#/definitions/CreditCardPayment"
      onlinePayment:
        $ref: "#/definitions/OnlinePayment"
      cryptoPayment:
        $ref: "#/definitions/CryptoPayment"
    # Make the properties optional
    required: []

  CreditCardPayment:
    type: object
    # Properties specific to CreditCardPayment

  OnlinePayment:
    type: object
    # Properties specific to OnlinePayment

  CryptoPayment:
    type: object
    # Properties specific to CryptoPayment

This example does not resemble the OpenAPI 3 implementation fully as the API consumer has to specify the type they are sending through a property field, and they also might send more than of the fields since they are all marked optional. This approach lacks the explicit validation and semantics provided by the oneOf keyword in OpenAPI 3.

  • In OpenAPI, you can describe multiple server URLs, while in Swagger you’re bound to only one:
{
  "swagger": "2.0",
  "info": {
    "title": "Sample API",
    "version": "1.0.0"
  },
  "host": "api.example.com",
  "basePath": "/v1",
...
}


openapi: 3.0.0
info:
  title: Sample API
  version: 1.0.0
servers:
  - url: http://api.example.com/v1
    description: Production Server
  - url: https://sandbox.api.example.com/v1
    description: Sandbox Server
...

You’re Not Using Components

One way of making an OpenAPI spec more readable is by removing any unnecessary duplication — the same way as a programmer would with their code. If you find that your OpenAPI spec is too messy and hard to read you might be under-utilizing the components section. Components provide a powerful mechanism for defining reusable schemas, parameters, responses, and other elements within your specification.

Let's take a look at the following example that does not utilize components:

openapi: 3.0.0
info:
  title: Nested Query Example
  version: 1.0.0
paths:
  /users:
    get:
      summary: Get users with nested query parameters
      parameters:
        - name: filter
          in: query
          schema:
            type: object
            properties:
              name:
                type: string
              age:
                type: number
              address:
                type: object
                properties:
                  city:
                    type: string
                  state:
                    type: string
                  country:
                    type: string
                  zipcode:
                    type: string
...
  /user/{id}/friend:
    get:
      summary: Get a user's friend
      parameters:
        - name: id
          in: path
          schema:
            type: string
        - name: filter
          in: query
          schema:
            type: object
            properties:
              name:
                type: string
              age:
                type: number
              address:
                type: object
                properties:
                  city:
                    type: string
                  state:
                    type: string
                  country:
                    type: string
                  zipcode:
                    type: string
...

The filter parameter in this example is heavily nested and can be challenging to follow. It is also used in its full length by two different endpoints. We can consolidate this behavior by leveraging component schemas:

openapi: 3.0.0
info:
  title: Nested Query Example with Schema References
  version: 1.0.0
paths:
  /users:
    get:
      summary: Get users with nested query parameters
      parameters:
        - name: filter
          in: query
          schema:
            $ref: "#/components/schemas/UserFilter"
...
  /user/{id}/friend:
    get:
      summary: Get a user's friend
      parameters:
        - name: id
          in: path
          schema:
            type: string
        - name: filter
          in: query
          schema:
            $ref: "#/components/schemas/UserFilter"
...
components:
  schemas:
    UserFilter:
      type: object
      properties:
        name:
          type: string
        age:
          type: number
        address:
          $ref: "#/components/schemas/AddressFilter"

    AddressFilter:
      type: object
      properties:
        city:
          type: string
        state:
          type: string
        country:
          type: string
        zipcode:
          type: string

The second example is clean and readable. By creating UserFilter and AddressFilter, we can reuse those schemas throughout the spec file, and if they ever change, we will only have to update them in one place.

You’re Not Using Descriptions, Examples, Formats, or Patterns

You finally finished porting all your endpoints and models into your OpenAPI spec. It took you a while, but now you can finally share it with development teams, QA teams, and even customers. Shortly after you share your spec with the world, the questions start arriving: “What does this endpoint do? What’s the purpose of this parameter? When should the parameter be used?”

Lets take a look at this example:

openapi: 3.0.0
info:
  title: Sample API
  version: 1.0.0
paths:
  /data:
    post:
      summary: Upload user data
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                age:
                  type: integer
                email:
                  type: string
      responses:
        "200":
          description: Successful response

We can deduce from it that data needs to be uploaded, but questions remain: What specific data should be uploaded? Is it the data pertaining to the current user? Whose name, age, and email do these attributes correspond to?

openapi: 3.0.0
info:
  title: Sample API
  version: 1.0.0
paths:
  /data:
    post:
      summary: Upload user data
      description: >
        Endpoint for uploading new user data to the system.
        This data will be used for personalized recommendations and analysis.
        Ensure the data is in a valid JSON format.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  description: The name of a new user.
                age:
                  type: integer
                  description: The age of a new user.
                email:
                  type: string
                  description: The email address of a new user.
      responses:
        "200":
          description: Successful response

You can’t always control how your API was structured, but you can control the descriptions you give it. Reduce the number of questions you receive by adding useful descriptions wherever possible.

Even after incorporating descriptions, you still might be asked about various aspects of your OpenAPI spec. At this point, you might be thinking, "Sharon, you deceived me! I added all those descriptions yet the questions keep on coming.”

Before you give up, have you thought about adding examples?

Lets take a look at this parameter:

parameters:
  - name: id
    in: path
    required: true
    schema:
      type: string
    description: The user id.

Based on the example, we understand that "id" is a string and serves as the user's identifier. However, despite your QA team relying on your OpenAPI spec for their tests, they are encountering issues. They inform you that they are passing a string, yet the API call fails. “That’s because you’re not passing valid ids,” you tell them. You rush to add an example to your OpenAPI spec:

parameters:
  - name: id
    in: path
    required: true
    schema:
      type: string
      example: e4bb1afb-4a4f-4dd6-8be0-e615d233185b
    description: The user id.

After your update your spec a follow up question arrives: would "d0656a1f-1lac-4n7b-89de-3e8ic292b2e1” be a good example as well? The answer is no since the characters 'l' and 'n' in the example are not valid hexadecimal characters, making them illegal in the UUID format:

parameters:
  - name: id
    in: path
    required: true
    schema:
      type: string
      format: uuid
      example: e4bb1afb-4a4f-4dd6-8be0-e615d233185b
    description: The user id.

Finally, your QA team has all the information they need to interact with the endpoints that use this parameter.

But what if a parameter is not of a common format? That’s when regex patterns come in:

parameters:
  - name: id
    in: path
    required: true
    schema:
      type: string
      pattern: "[a-f0-9]{32}"
      example: 2675b703b9d4451f8d4861a3eee54449
    description: A 32-character unique user ID.

By using the pattern field, you can define custom validation rules for string properties, enabling more precise constraints on the data accepted by your API.

You can read more about formats, examples, and patterns here.

Conclusion

This list of shortcomings is certainly not exhaustive, but the most common and easily fixable ones presented in this post include upgrading from Swagger, utilizing components effectively, and providing comprehensive documentation. By making these improvements, you are laying the foundation for successful API documentation. When working on your spec, put yourself in the shoes of a new API consumer, since this is their initial interaction with the API. Ensure that it is well-documented and easy to comprehend, and set the stage for a positive developer experience.

API OpenAPI Specification Documentation

Published at DZone with permission of Sharon Pikovski. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • 8 Strategies To Accelerate Web Portal Development
  • Implement OpenAPI for AWS Lambda in Quarkus Using API Gateway Integrator
  • What Is API-First?
  • The Easiest and Quickest Way to Generate an OpenAPI Spec for an Existing Website

Partner Resources


Comments

ABOUT US

  • About DZone
  • Send feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: