<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ OpenApi - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ OpenApi - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Tue, 05 May 2026 16:59:29 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/openapi/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Automate API Documentation Updates with GitHub Actions and OpenAPI Specifications ]]>
                </title>
                <description>
                    <![CDATA[ Maintaining up-to-date API documentation is often one of the biggest pain points for developers and teams. Too often, the API spec changes but the docs lag behind, leaving developers with outdated or inconsistent information. This frustrates consumer... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-automate-api-documentation-updates-with-github-actions-and-openapi-specifications/</link>
                <guid isPermaLink="false">68c0398aeff4d53c8494c5ab</guid>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ github-actions ]]>
                    </category>
                
                    <category>
                        <![CDATA[ OpenApi ]]>
                    </category>
                
                    <category>
                        <![CDATA[ documentation ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ EZINNE ANNE EMILIA ]]>
                </dc:creator>
                <pubDate>Tue, 09 Sep 2025 14:28:26 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1757428080226/175085d0-cfea-41a0-aa52-a50ad8212980.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Maintaining up-to-date API documentation is often one of the biggest pain points for developers and teams. Too often, the API spec changes but the docs lag behind, leaving developers with outdated or inconsistent information. This frustrates consumers of your API and increases support overhead.</p>
<p>This is where automation comes in. By combining OpenAPI specifications with GitHub Actions, you can ensure your documentation is always in sync with your API changes.</p>
<ul>
<li><p><strong>OpenAPI</strong> acts as the single reference point for your API design, keeping your docs consistent, accurate, and aligned with your API.</p>
</li>
<li><p><strong>GitHub Actions</strong> automates the workflow, validating your spec, building docs, and publishing to GitHub Pages in seconds.</p>
</li>
</ul>
<p>This tutorial walks you through a working example of how to use GitHub Actions to auto-update your docs.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-your-repository">How to set up your repository</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-the-openapi-specification">How to Create the OpenAPI Specification</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-test-the-api-spec-locally">How to Test the API Spec Locally</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-install-tools">Install tools</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-create-a-landing-page">Create a landing page</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-validate-your-spec">Validate Your Spec</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-preview-in-the-browser">Preview in the Browser</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-push-local-changes-to-github">How to Push Local Changes to GitHub</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-your-github-actions-workflow">How to Set Up Your GitHub Actions Workflow</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-github-pages">How to Set Up GitHub Pages</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-github-pages">What is GitHub Pages?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-setting-up-github-pages">Setting Up GitHub Pages</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-handle-multiple-versions">How to Handle Multiple Versions</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-about-the-versions">About the Versions</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-version-1-v1">Version 1 (v1)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-version-2-v2">Version 2 (v2)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-version-3-v3">Version 3 (v3)</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-the-versions-locally">How to Set Up the Versions Locally</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-validate-the-api-specs">How to Validate the API Specs</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-update-the-github-actions-workflow">How to Update the GitHub Actions Workflow</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-summary">Summary</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li><p><a target="_blank" href="https://docs.npmjs.com/downloading-and-installing-node-js-and-npm">Node.js and npm installed.</a></p>
</li>
<li><p><a target="_blank" href="https://www.freecodecamp.org/news/gitting-things-done-book/">A GitHub account with basic Git knowledge.</a></p>
</li>
<li><p><a target="_blank" href="https://code.visualstudio.com/download">Visual Studio Code Editor</a>.</p>
</li>
<li><p><a target="_blank" href="https://idratherbewriting.com/learnapidoc/">Basic knowledge of documentation and OpenAPI</a>.</p>
</li>
</ul>
<h2 id="heading-how-to-set-up-your-repository">How to Set Up Your Repository</h2>
<p>If you don’t already have one, create a GitHub repository. For this tutorial, I’ll use <code>api-docs</code> as the repo name.</p>
<p>Then open VSCode and create a folder with the same name.</p>
<h2 id="heading-how-to-create-the-openapi-specification">How to Create the OpenAPI Specification</h2>
<p>Inside the folder you just created, create a folder called <code>spec</code> and add a file named <code>greetings.yaml</code> with the following content:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">openapi:</span> <span class="hljs-number">3.0</span><span class="hljs-number">.3</span>
<span class="hljs-attr">info:</span>
  <span class="hljs-attr">title:</span> <span class="hljs-string">Greetings</span> <span class="hljs-string">API</span>
  <span class="hljs-attr">version:</span> <span class="hljs-number">1.0</span><span class="hljs-number">.0</span>
  <span class="hljs-attr">description:</span> <span class="hljs-string">This</span> <span class="hljs-string">is</span> <span class="hljs-string">a</span> <span class="hljs-string">greetings</span> <span class="hljs-string">API</span> <span class="hljs-string">demonstrating</span> <span class="hljs-string">a</span> <span class="hljs-string">simple</span> <span class="hljs-string">greeting</span> <span class="hljs-string">endpoint</span> <span class="hljs-string">with</span> <span class="hljs-string">query</span> <span class="hljs-string">parameters</span> <span class="hljs-string">and</span> <span class="hljs-string">multilingual</span> <span class="hljs-string">support.</span>
  <span class="hljs-attr">license:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">MIT</span>
    <span class="hljs-attr">url:</span> <span class="hljs-string">https://opensource.org/licenses/MIT</span>
<span class="hljs-attr">servers:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">url:</span> <span class="hljs-string">https://api.yourdomain.com/v1</span>
    <span class="hljs-attr">description:</span> <span class="hljs-string">Production</span> <span class="hljs-string">server(v1)</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">url:</span> <span class="hljs-string">https://staging.yourdomain.com/v1</span>
    <span class="hljs-attr">description:</span> <span class="hljs-string">Staging</span> <span class="hljs-string">server(v1)</span>
<span class="hljs-attr">security:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">api_key:</span> []
<span class="hljs-attr">paths:</span>
  <span class="hljs-string">/hello:</span>
    <span class="hljs-attr">get:</span>
      <span class="hljs-attr">summary:</span> <span class="hljs-string">Returns</span> <span class="hljs-string">a</span> <span class="hljs-string">greeting</span>
      <span class="hljs-attr">operationId:</span> <span class="hljs-string">getGreeting</span>
      <span class="hljs-attr">parameters:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">name</span>
          <span class="hljs-attr">in:</span> <span class="hljs-string">query</span>
          <span class="hljs-attr">required:</span> <span class="hljs-literal">false</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Name</span> <span class="hljs-string">of</span> <span class="hljs-string">the</span> <span class="hljs-string">person</span> <span class="hljs-string">to</span> <span class="hljs-string">greet</span>
          <span class="hljs-attr">schema:</span>
            <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
            <span class="hljs-attr">example:</span> <span class="hljs-string">Ezinne</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">lang</span>
          <span class="hljs-attr">in:</span> <span class="hljs-string">query</span>
          <span class="hljs-attr">required:</span> <span class="hljs-literal">false</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Language</span> <span class="hljs-string">of</span> <span class="hljs-string">the</span> <span class="hljs-string">greeting</span> <span class="hljs-string">(default</span> <span class="hljs-string">is</span> <span class="hljs-string">English)</span>
          <span class="hljs-attr">schema:</span>
            <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
            <span class="hljs-attr">enum:</span> [<span class="hljs-string">en</span>, <span class="hljs-string">fr</span>, <span class="hljs-string">es</span>, <span class="hljs-string">ig</span>]
            <span class="hljs-attr">example:</span> <span class="hljs-string">en</span>
      <span class="hljs-attr">responses:</span>
        <span class="hljs-attr">'200':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Successful</span> <span class="hljs-string">response</span>
          <span class="hljs-attr">content:</span>
            <span class="hljs-attr">application/json:</span>
              <span class="hljs-attr">schema:</span>
                <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
                <span class="hljs-attr">properties:</span>
                  <span class="hljs-attr">message:</span>
                    <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
              <span class="hljs-attr">examples:</span>
                <span class="hljs-attr">english:</span>
                  <span class="hljs-attr">value:</span> { <span class="hljs-attr">message:</span> <span class="hljs-string">"Hello, Ezinne!"</span> }
                <span class="hljs-attr">french:</span>
                  <span class="hljs-attr">value:</span> { <span class="hljs-attr">message:</span> <span class="hljs-string">"Bonjour, Ezinne!"</span> }
                <span class="hljs-attr">spanish:</span>
                  <span class="hljs-attr">value:</span> { <span class="hljs-attr">message:</span> <span class="hljs-string">"¡Hola, Ezinne!"</span> }
                <span class="hljs-attr">igbo:</span>
                  <span class="hljs-attr">value:</span> { <span class="hljs-attr">message:</span> <span class="hljs-string">"Ndeewo, Ezinne!"</span> }
<span class="hljs-attr">components:</span>
  <span class="hljs-attr">securitySchemes:</span>
    <span class="hljs-attr">api_key:</span>
      <span class="hljs-attr">type:</span> <span class="hljs-string">apiKey</span>
      <span class="hljs-attr">name:</span> <span class="hljs-string">Authorization</span>
      <span class="hljs-attr">in:</span> <span class="hljs-string">header</span>
</code></pre>
<p>This is a simple spec with multilingual greetings. As your API grows (say more languages or versions), keeping docs in sync manually might get tedious. That’s why automation helps.</p>
<h2 id="heading-how-to-test-the-api-spec-locally">How to Test the API Spec Locally</h2>
<h3 id="heading-install-tools">Install tools:</h3>
<p>Before setting GitHub Actions, you can test the API Spec locally on your machine by setting up <a target="_blank" href="https://github.com/Redocly">Redocly</a> (used to be called Redoc) and testing it in an HTML environment.</p>
<p>Redocly is a lightweight, customizable tool to render OpenAPI specs as an interactive HTML documentation. It’s ideal for static site deployment which makes it ideal for this scenario.</p>
<ul>
<li><p>Install Redoc globally with <code>npm install -g @redocly/cli</code></p>
</li>
<li><p>Install http-server globally with <code>npm install -g http-server</code></p>
</li>
</ul>
<p>The http-server is a local server you can use to test the doc on your machine before you push to GitHub and deploy to GitHub Pages.</p>
<h3 id="heading-create-a-landing-page">Create a landing page:</h3>
<p>In your project, make a <code>docs</code> folder and add <code>index.html</code>:</p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>API Documentation<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"utf-8"</span>/&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1"</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">redoc</span> <span class="hljs-attr">spec-url</span>=<span class="hljs-string">"../spec/greetings.yaml"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">redoc</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<h3 id="heading-validate-your-spec">Validate Your Spec:</h3>
<p><code>redocly lint spec/greetings.yaml</code></p>
<p>You should see this if there are no errors or warnings:</p>
<pre><code class="lang-powershell">Woohoo! Your API description is valid. 🎉
</code></pre>
<p><strong>Note:</strong> Validating your API Spec before testing is important as it’ll flag any possible errors. This is because Redocly will fail to run the preview if there are any errors in your spec. </p>
<h3 id="heading-preview-in-the-browser">Preview in the browser:</h3>
<p>Run <code>http-server</code>, and you should see this in the terminal:</p>
<pre><code class="lang-powershell">Starting up http<span class="hljs-literal">-server</span>, serving ./
Available on:
  http://<span class="hljs-number">127.0</span>.<span class="hljs-number">0.1</span>:<span class="hljs-number">8080</span>
  http://<span class="hljs-number">192.168</span>.x.x:<span class="hljs-number">8080</span>
Hit CTRL<span class="hljs-literal">-C</span> to stop the server
</code></pre>
<p>Open <a target="_blank" href="http://localhost:8080/docs/index.html"><code>http://127.0.0.1:8080/</code></a> and navigate to <code>/docs</code> to see your docs.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1756983802999/944b8603-7b2e-477a-8156-fdaa60f7e0af.png" alt="A preview of the API Specification in a Html page" class="image--center mx-auto" width="1894" height="865" loading="lazy"></p>
<h2 id="heading-how-to-push-local-changes-to-github">How to Push Local Changes to GitHub</h2>
<p>After making local changes, you need to set up the API documentation so it can update automatically whenever you make changes.</p>
<p>Run these commands if you are pushing to the repository for the first time:</p>
<pre><code class="lang-powershell">git init
git add .
git commit <span class="hljs-literal">-m</span> <span class="hljs-string">"first commit"</span>
git branch <span class="hljs-literal">-M</span> main
git remote add origin &lt;your<span class="hljs-literal">-repo</span><span class="hljs-literal">-url</span>&gt;
git push <span class="hljs-literal">-u</span> origin main
</code></pre>
<h2 id="heading-how-to-set-up-your-github-actions-workflow">How to Set Up Your GitHub Actions Workflow</h2>
<p>You can set up your GitHub workflow by creating a few folders.</p>
<p>First, create <code>.github/workflows/</code> in the <code>api-docs</code> folder. Then, inside the <code>workflows</code> folder, create a <code>docs.yml</code>. This is the workflow file that will serve as a trigger to run validation, generate the HTML with Redocly, and deploy to GitHub Pages at the same time.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Build</span> <span class="hljs-string">API</span> <span class="hljs-string">Documentation</span> <span class="hljs-string">and</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">to</span> <span class="hljs-string">GitHub</span> <span class="hljs-string">Pages</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">main</span>
    <span class="hljs-attr">paths:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'spec/greetings.yaml'</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build-spec:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">permissions:</span>
      <span class="hljs-attr">contents:</span> <span class="hljs-string">write</span> <span class="hljs-comment"># needed for gh-pages deployment</span>

    <span class="hljs-attr">steps:</span>
      <span class="hljs-comment"># 1. Checkout repository</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span> <span class="hljs-string">code</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>

      <span class="hljs-comment"># 2. Set up Node.js</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Set</span> <span class="hljs-string">up</span> <span class="hljs-string">Node.js</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-node@v4</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">node-version:</span> <span class="hljs-string">'20'</span>

      <span class="hljs-comment"># 3. Install Redocly CLI</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">Redocly</span> <span class="hljs-string">CLI</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span> <span class="hljs-string">-g</span> <span class="hljs-string">@redocly/cli</span>

      <span class="hljs-comment"># 4. Validate OpenAPI spec</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Validate</span> <span class="hljs-string">OpenAPI</span> <span class="hljs-string">Spec</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">redocly</span> <span class="hljs-string">lint</span> <span class="hljs-string">spec/greetings.yaml</span>

      <span class="hljs-comment"># 5. Build output directory</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Create</span> <span class="hljs-string">build</span> <span class="hljs-string">directory</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">mkdir</span> <span class="hljs-string">-p</span> <span class="hljs-string">public</span>

      <span class="hljs-comment"># 6. Copy spec</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Copy</span> <span class="hljs-string">spec</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">mkdir</span> <span class="hljs-string">-p</span> <span class="hljs-string">public/spec</span> <span class="hljs-string">&amp;&amp;</span> <span class="hljs-string">cp</span> <span class="hljs-string">spec/greetings.yaml</span> <span class="hljs-string">public/spec/</span>

      <span class="hljs-comment"># 7. Copy landing page</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Copy</span> <span class="hljs-string">landing</span> <span class="hljs-string">page</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">cp</span> <span class="hljs-string">docs/index.html</span> <span class="hljs-string">public/index.html</span>

      <span class="hljs-comment"># 8. Deploy to GitHub Pages</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">to</span> <span class="hljs-string">GitHub</span> <span class="hljs-string">Pages</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">peaceiris/actions-gh-pages@v4</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">github_token:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.GITHUB_TOKEN</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">publish_dir:</span> <span class="hljs-string">./public</span>
</code></pre>
<p>Here’s what’s going on in this code:</p>
<ul>
<li><p>Runs when changes are pushed to <code>main</code> that affect <code>spec/greetings.yaml</code>.</p>
</li>
<li><p>Checks out the repo code.</p>
</li>
<li><p>Sets up Node.js and installs Redocly.</p>
</li>
<li><p>Validates your OpenAPI spec (so broken specs won’t deploy).</p>
</li>
<li><p>Copies the spec and index page into a <code>public/</code> folder.</p>
</li>
<li><p>Deploys <code>public/</code> to the <code>gh-pages</code> branch with GitHub Pages.</p>
</li>
</ul>
<p>Since we’re done with local testing, update the file path in the <code>index.html</code>:</p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>API Documentation<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"utf-8"</span>/&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1"</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">redoc</span> <span class="hljs-attr">spec-url</span>=<span class="hljs-string">"./spec/greetings.yaml"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">redoc</span>&gt;</span> <span class="hljs-comment">&lt;!--update the filepath to match your gh config--&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>This is so the <code>public</code> directory in the workflow will be able to access it correctly.</p>
<p>This workflow will only run when it detects changes in the API Spec (<code>greetings.yml</code>). To see the workflow in action, make a minor edit in the <code>greetings.yaml</code>.</p>
<p>Push the changes to your GitHub repository:</p>
<pre><code class="lang-powershell">git add .
git commit <span class="hljs-literal">-m</span> <span class="hljs-string">'add changes'</span>
git push
</code></pre>
<h2 id="heading-how-to-set-up-github-pages">How to Set Up GitHub Pages</h2>
<h3 id="heading-what-is-github-pages">What is GitHub Pages?</h3>
<p><a target="_blank" href="https://docs.github.com/en/pages/getting-started-with-github-pages/what-is-github-pages">GitHub Pages</a> is a hosting platform owned by GitHub where you can host websites directly from your GitHub account. This means you can publish static sites on the internet using a GitHub domain and anyone with the website link can access it.</p>
<p>There are other hosting platforms you can use to deploy static websites such as <a target="_blank" href="https://www.netlify.com/">Netlify</a> and <a target="_blank" href="https://vercel.com/">Vercel</a>. But using GitHub Pages for this documentation is easier to set up as it’s on the same platform.</p>
<h3 id="heading-setting-up-github-pages">Setting up GitHub Pages</h3>
<p>Set up GitHub Pages by clicking on the Settings tab in your repository.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1756985360548/fa3518a7-0b44-4c7b-ae7f-d0e0b17a84c6.png" alt="A preview of the settings tab in the `api-docs` repository" class="image--center mx-auto" width="1909" height="865" loading="lazy"></p>
<p>Under Source, choose:</p>
<ul>
<li><p>Deploy from branch: <code>gh-pages</code></p>
</li>
<li><p>Folder: <code>/ (root)</code></p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1756985446692/a4774bcc-1a42-49f8-a9fd-8ca9339808ef.png" alt="A step-by-step preview of the gh-pages and root setup" class="image--center mx-auto" width="1669" height="525" loading="lazy"></p>
<p>Then save and wait for the workflow to finish.</p>
<p>Your docs will be live at: <code>https://&lt;username&gt;.github.io/api-docs</code>.</p>
<h2 id="heading-how-to-handle-multiple-versions">How to Handle Multiple Versions</h2>
<p>What if you had multiple API versions to update? Let’s assume the simple greetings API in this tutorial had more features added to it across different versions. In this case, you can manage the APIs for the different versions in a single page and also build and deploy it automatically. </p>
<h3 id="heading-about-the-versions">About the Versions</h3>
<h4 id="heading-version-1-v1">Version 1 (v1)</h4>
<p>This is the starting point which is <code>greetings.yaml</code>. The API only has a single <code>/hello</code> endpoint that returns a greeting in four languages (English, French, Spanish, or Igbo).</p>
<h4 id="heading-version-2-v2">Version 2 (v2)</h4>
<p>In version 2, the API adds create and read features. You can:</p>
<ul>
<li><p>Use <code>POST /hello</code> to create and save a greeting.</p>
</li>
<li><p>Retrieve greetings by their unique ID with <code>GET /hello/{id}</code>.</p>
</li>
</ul>
<h4 id="heading-version-3-v3">Version 3 (v3)</h4>
<p>Version 3 builds on top of v2 by adding an update functionality. Along with creating and retrieving greetings, you can now update an existing greeting using <code>PUT /hello/{id}</code>.</p>
<h3 id="heading-how-to-set-up-the-versions-locally">How to Set Up the Versions Locally</h3>
<p>First, create a <code>v1</code> folder and move the <code>greetings.yaml</code> file to it. Since we are going to be using versions, you can delete the existing <code>spec</code> folder.</p>
<p>Then, create a <code>v2</code> folder and create a <code>greetings-v2.yaml</code> file. <a target="_blank" href="https://ezinneanne.github.io/api-doc/v2/greetings-v2.yaml">Get the greetings API for version 2 here</a>.</p>
<p>Next, create a <code>v3</code> folder and add <code>greetings-v3.yaml</code> file. <a target="_blank" href="https://ezinneanne.github.io/api-doc/v3/greetings-v3.yaml">Get the greetings API for version 3 here</a>.</p>
<p>To follow the same pattern with others, rename the version 1 file to <code>greetings-v1.yaml</code>. Then update your <code>index.html</code> to accommodate the other two versions.</p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>API Documentation<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"utf-8"</span>/&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
      <span class="hljs-selector-tag">body</span> {
        <span class="hljs-attribute">font-family</span>: Arial, sans-serif;
        <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
      }
      <span class="hljs-selector-tag">header</span> {
        <span class="hljs-attribute">background</span>: <span class="hljs-number">#2c3e50</span>;
        <span class="hljs-attribute">color</span>: white;
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
        <span class="hljs-attribute">display</span>: flex;
        <span class="hljs-attribute">justify-content</span>: space-between;
        <span class="hljs-attribute">align-items</span>: center;
      }
      <span class="hljs-selector-tag">select</span> {
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.4rem</span>;
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1rem</span>;
      }
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">header</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>API Documentation<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"version"</span>&gt;</span>Version: <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">select</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"version"</span> <span class="hljs-attr">onchange</span>=<span class="hljs-string">"loadSpec()"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"./v1/greetings-v1.yaml"</span>&gt;</span>v1<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"./v2/greetings-v2.yaml"</span>&gt;</span>v2<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"./v3/greetings-v3.yaml"</span>&gt;</span>v3<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">select</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- ReDoc container --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"redoc-container"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- ReDoc script --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
      <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">loadSpec</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">const</span> version = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"version"</span>).value;
        Redoc.init(version, {}, <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"redoc-container"</span>));
      }
      <span class="hljs-comment">// Load default (v1) on first load</span>
      <span class="hljs-built_in">window</span>.onload = loadSpec;
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<h3 id="heading-how-to-validate-the-api-specs">How to Validate the API Specs</h3>
<p>Earlier in this article, I mentioned testing your specification locally. Now that you have two more versions of the greetings API, run the test to highlight and fix any existing errors.</p>
<ul>
<li><p>For the version V2: <code>redocly lint v2/greetings-v2.yaml</code></p>
</li>
<li><p>For the version V3: <code>redocly lint v3/greetings-v3.yaml</code></p>
</li>
</ul>
<h3 id="heading-how-to-update-the-github-actions-workflow">How to Update the GitHub Actions Workflow</h3>
<p>Now that you have three API Spec versions, you need to update your workflow so it will monitor the three spec files and the HTML document for changes, and then push and deploy them to GitHub Pages as well.</p>
<p>Add this to your <code>.github/workflows/docs.yml</code>:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Name of the workflow</span>
<span class="hljs-attr">name:</span> <span class="hljs-string">Build</span> <span class="hljs-string">and</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">API</span> <span class="hljs-string">Documentation</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span> [ <span class="hljs-string">main</span> ]
    <span class="hljs-attr">paths:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'docs/index.html'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'v1/greetings-v1.yaml'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'v2/greetings-v2.yaml'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'v3/greetings-v3.yaml'</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build-and-deploy:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>

    <span class="hljs-attr">permissions:</span>
      <span class="hljs-attr">contents:</span> <span class="hljs-string">write</span>

    <span class="hljs-attr">steps:</span>
      <span class="hljs-comment"># 1. Checkout the repository</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span> <span class="hljs-string">repository</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>

      <span class="hljs-comment"># 2. Create build directory</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Create</span> <span class="hljs-string">build</span> <span class="hljs-string">directory</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">mkdir</span> <span class="hljs-string">-p</span> <span class="hljs-string">public</span>

      <span class="hljs-comment"># 3. Copy YAML specs into public folder</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Copy</span> <span class="hljs-string">v1</span> <span class="hljs-string">spec</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">mkdir</span> <span class="hljs-string">-p</span> <span class="hljs-string">public/v1</span> <span class="hljs-string">&amp;&amp;</span> <span class="hljs-string">cp</span> <span class="hljs-string">v1/greetings-v1.yaml</span> <span class="hljs-string">public/v1/</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Copy</span> <span class="hljs-string">v2</span> <span class="hljs-string">spec</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">mkdir</span> <span class="hljs-string">-p</span> <span class="hljs-string">public/v2</span> <span class="hljs-string">&amp;&amp;</span> <span class="hljs-string">cp</span> <span class="hljs-string">v2/greetings-v2.yaml</span> <span class="hljs-string">public/v2/</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Copy</span> <span class="hljs-string">v3</span> <span class="hljs-string">spec</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">mkdir</span> <span class="hljs-string">-p</span> <span class="hljs-string">public/v3</span> <span class="hljs-string">&amp;&amp;</span> <span class="hljs-string">cp</span> <span class="hljs-string">v3/greetings-v3.yaml</span> <span class="hljs-string">public/v3/</span>

      <span class="hljs-comment"># 4. Copy landing page into public</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Copy</span> <span class="hljs-string">landing</span> <span class="hljs-string">page</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">cp</span> <span class="hljs-string">docs/index.html</span> <span class="hljs-string">public/index.html</span>

      <span class="hljs-comment"># 5. Deploy to GitHub Pages</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">to</span> <span class="hljs-string">GitHub</span> <span class="hljs-string">Pages</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">peaceiris/actions-gh-pages@v4</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">github_token:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.GITHUB_TOKEN</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">publish_dir:</span> <span class="hljs-string">./public</span>
</code></pre>
<p>And finally, push the changes and reload the site. This should showcase the updated documentation.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1756986868235/9de187f1-12c4-46ca-a73b-daafa353ed1f.png" alt="A preview of the API documentation in a hosted GitHub Pages environment" class="image--center mx-auto" width="1894" height="868" loading="lazy"></p>
<h2 id="heading-summary">Summary</h2>
<p>In this tutorial, you have learned how to auto-update your API docs. We started with a single OpenAPI spec and a basic HTML page rendered by Redocly, and tested it locally. We then set up GitHub Actions to automatically validate the spec, copy the files, and deploy the docs to GitHub Pages. Finally, we extended the setup to handle multiple API versions in one place.</p>
<p>With this workflow, your documentation stays accurate, up-to-date, and hassle-free so every change you make to your API spec goes live when you push the changes.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build Robust Networking Layers in Swift with OpenAPI ]]>
                </title>
                <description>
                    <![CDATA[ What is the Problem We’re Solving? For many app developers, including me, writing the networking layer of an application is a familiar and tedious process. You write and test your first call and after that, it involves a repetitive cycle of tasks. Th... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-robust-networking-layers-in-swift-with-openapi/</link>
                <guid isPermaLink="false">687fd1a603524e3b4e7b77fe</guid>
                
                    <category>
                        <![CDATA[ OpenApi ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Swift ]]>
                    </category>
                
                    <category>
                        <![CDATA[ openapi generator ]]>
                    </category>
                
                    <category>
                        <![CDATA[ OpenAPI Specification ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SwiftUI ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Sravan Karuturi ]]>
                </dc:creator>
                <pubDate>Tue, 22 Jul 2025 18:00:06 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1753206547489/dce9a849-1ccd-4cb0-bca8-f879a5aadf5f.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <h2 id="heading-what-is-the-problem-were-solving"><strong>What is the Problem We’re Solving?</strong></h2>
<p>For many app developers, including me, writing the networking layer of an application is a familiar and tedious process. You write and test your first call and after that, it involves a repetitive cycle of tasks.</p>
<p>This is how it would look in the case of Swift:</p>
<ol>
<li><p>You create a <code>URLSession</code> Instance.</p>
</li>
<li><p>You create a <code>URLRequest</code> Object.</p>
</li>
<li><p>You create the <code>@Codable</code> models to match the expected input and output from the server.</p>
</li>
</ol>
<p>You do the above steps for each API endpoint you have on your backend that your app uses. Not only is this process time-consuming and not challenging for developers, it’s also error prone. </p>
<p>In the above case, if there was a minor change in the backend API – perhaps a renamed field or a new property – this would lead to the app potentially breaking. But you wouldn’t know this until you shipped it to QA or in a worse case, your consumer. This is where the OpenAPI Specification emerges as a modern, robust solution. </p>
<p>In this tutorial, you’ll learn what OpenAPI is and how it can help make your development process better. After that, we’ll implement OpenAPI by creating a small SwiftUI app and using OpenAPI methodologies to interface with the <code>JSONPlaceholder</code> API. Let’s get started.</p>
<h2 id="heading-who-is-this-guide-for"><strong>Who is This Guide For?</strong></h2>
<p>This guide is intended both for new developers looking for best practices and for experienced developers looking to implement or learn more about the OpenAPI Specification. Let’s get into it.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-openapi-and-why-should-you-care">What is OpenAPI and Why Should You Care?</a></p>
<ul>
<li><a class="post-section-overview" href="#heading-benefits-for-swift-ios-developers">Benefits for Swift (iOS) Developers</a></li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-a-practical-guide-to-implementing-this-solution">A Practical Guide to Implementing This Solution</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-step-1-create-a-good-openapiyaml-file-the-specification">Step 1: Create a good openapi.yaml file (the specification)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-set-up-your-project">Step 2: Set up your project</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-3-write-a-wrapper">Step 3: Write a wrapper</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-4-call-the-wrapper-and-display-the-data">Step 4: Call the wrapper and display the data</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-potential-pitfalls">Potential Pitfalls</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-verbose-or-ugly-generated-code">Verbose or ugly generated code</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-large-specs-and-performance-issues">Large Specs and Performance Issues</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-unsupported-spec-features">Unsupported Spec Features</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion-embrace-spec-driven-development">Conclusion: Embrace Spec-Driven Development</a></p>
</li>
</ul>
<h2 id="heading-what-is-openapi-and-why-should-you-care"><strong>What is OpenAPI and Why Should You Care?</strong></h2>
<p>At its core, the OpenAPI Specification provides a <em>standard, language-agnostic interface</em> for describing RESTful APIs. This specification, once populated, allows both humans and computers to discover and understand the capabilities of a service without needing to access the source code or the network requests.</p>
<p>The power of OpenAPI is that it acts as a <em>formal contract between different parts of the system.</em> This contract helps both frontend and backend programmers by removing ambiguity during design process. This also has added benefit of using code generators to generate boiler-plate code both on backend and on the client ( which we will also discuss today ).</p>
<p>Traditionally when you want to create a new API in a team, either the PM, the frontend engineer, or the backend engineer takes it upon themselves to request it. Then the backend team builds it and documents it. This in turn is used by the front end team to use the API. </p>
<p><code>Some Requester → Backend Team → Documentation → Frontend Team</code></p>
<p>If you’re using OpenAPI, when someone makes a request for a new API, it is formalized into a specification after deliberations with both the frontend and the backend team. This then serves as the source of truth and is used to generate the backend and the frontend code without as much interdependence.</p>
<p><code>Some Requester → All Teams → Specification → All Teams.</code></p>
<p>This not only streamlines the process of adding new APIs, but provides a definitive source of truth for each endpoint. This also makes it so that frontend engineers and backend engineers are not misaligned about a provided parameter in the result being an <code>Int</code> or a <code>String</code> and so on. <strong>It’s all in the Spec.</strong></p>
<h3 id="heading-benefits-for-swift-ios-developers"><strong>Benefits for Swift (iOS) Developers</strong></h3>
<p>Adopting OpenAPI and <code>swift-openapi-generator</code> brings a host of tangible benefits to the Swift/App development process. It transforms how applications interact with web services in a few key ways.</p>
<h4 id="heading-reduced-development-time-and-cost">Reduced Development Time and Cost</h4>
<p>The most immediate improvement you will see is the significant reduction in boilerplate code you have to write. The generator automates the creation of what is called boilerplate code or ceremonial code. This is the repetitive logic for network requests, response handling, and data model definitions.</p>
<p>By delegating this work, developers can work on the core features of the application which leads to faster and more interesting development cycles.</p>
<h4 id="heading-compile-time-type-safety">Compile Time Type Safety</h4>
<p>This has been a major improvement for me personally. Instead of relying on the “strongly” typed keys for JSON parsing, we now work with strongly typed models. The generator creates native Swift struct and enum types directly from the schemas defined in the OpenAPI document. This brings the power of a strongly-typed system to the networking and parsing layer.</p>
<p>For example, if the return value of an API is made optional, instead of crashing at runtime, we will fail to compile at build time. This forces us to address this issue right away. </p>
<h4 id="heading-improved-collaboration-and-interoperability">Improved Collaboration and Interoperability</h4>
<p>This makes sure that all the developers are on the same page with regard to a given endpoint. And since this specification is language agnostic, it will serve as a universal language for all teams involved in the project – mobile, web and backend. </p>
<h4 id="heading-other-tooling">Other Tooling</h4>
<p>Once you have a specification, you can use that to power a wide variety of tools. You can generate interactive documentation, create mock servers for frontend development, and run automated tests. </p>
<p>Alright hopefully you’re sold – so now how do you implement this into your project?</p>
<h2 id="heading-a-practical-guide-to-implementing-this-solution">A Practical Guide to Implementing This Solution</h2>
<p>We’ll now take a look at a practical example so you can understand how you can implement this in a project. This involves:</p>
<ul>
<li><p>Creating an openapi.yaml file to describe the API specification.</p>
</li>
<li><p>Configuring and integrating <code>swift-openapi-generator</code> into a SwiftUI application. </p>
</li>
<li><p>Prototyping an app that fetches and displays a list of posts from the <a target="_blank" href="https://jsonplaceholder.typicode.com/">https://jsonplaceholder.typicode.com/</a> </p>
</li>
</ul>
<p>To follow along, you will need Xcode installed and a basic understanding of Swift programming and SwiftUI for App development.</p>
<h3 id="heading-step-1-create-a-good-openapiyaml-file-the-specification">Step 1: Create a good openapi.yaml file (the specification)</h3>
<p>The quality of a specification is really important because it directly determines the quality of the code produced by <code>swift-openapi-generator</code>. If you don’t have a good specification, you might run into several issues that developers often complain about, like confusing and long method names.</p>
<p>For example, it might generate something like <code>get_all_my_meal_recipes_hyphen_detailed</code>. This might happen because the generator is forced to create a new name based on the API path if the identifier is not provided in the spec. So, instead of dealing with these issues one after the other, we will create a <em>good clear specification</em> to start with.</p>
<p>Since we’re using the <code>jsonplaceholder</code> as our backend server, we are limited by what tweaks we can make – but it is a fantastic project that lets us mimic a backend server.</p>
<p>In general, an OpenAPI.yaml file contains:</p>
<ol>
<li><p>OpenAPI Info and servers – This will provide the metadata about the API like the OpenAPI version, which server to point to for calls, and so on. </p>
</li>
<li><p>Paths – This will provide the available endpoints. In our case, it can contain /posts as one of them. We also will have to mention the kind of endpoint (get, post, put, and so on)</p>
</li>
<li><p>OperationID – This field instructs the generator to create a clear method with this name. </p>
</li>
<li><p>Responses – This defines the possible outcomes of an API call. We will specify the structure of a successful 200 OK response or any other errors here. </p>
</li>
<li><p>Components / Schemas – This defines all the reusable components and data models. If we have a Post schema definer here, the generator will use this to create a Post struct in Swift to match this. </p>
</li>
</ol>
<p>Keeping in mind all these elements, I compiled a yaml file for us to use for this tutorial:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># openapi.yaml</span>
<span class="hljs-attr">openapi:</span> <span class="hljs-string">"3.0.3"</span>
<span class="hljs-attr">info:</span>
  <span class="hljs-attr">title:</span> <span class="hljs-string">"JSONPlaceholder API"</span>
  <span class="hljs-attr">version:</span> <span class="hljs-string">"1.0.0"</span>
<span class="hljs-attr">servers:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">url:</span> <span class="hljs-string">"https://jsonplaceholder.typicode.com"</span>
<span class="hljs-attr">paths:</span>
  <span class="hljs-string">/posts:</span>
    <span class="hljs-attr">get:</span>
      <span class="hljs-attr">summary:</span> <span class="hljs-string">"Get all posts"</span>
      <span class="hljs-attr">operationId:</span> <span class="hljs-string">"getPosts"</span>
      <span class="hljs-attr">responses:</span>
        <span class="hljs-attr">"200":</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">"A list of posts"</span>
          <span class="hljs-attr">content:</span>
            <span class="hljs-attr">application/json:</span>
              <span class="hljs-attr">schema:</span>
                <span class="hljs-attr">type:</span> <span class="hljs-string">array</span>
                <span class="hljs-attr">items:</span>
                  <span class="hljs-string">$ref:</span> <span class="hljs-string">"#/components/schemas/Post"</span>
<span class="hljs-attr">components:</span>
  <span class="hljs-attr">schemas:</span>
    <span class="hljs-attr">Post:</span>
      <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
      <span class="hljs-attr">required:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">userId</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">id</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">title</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">body</span>
      <span class="hljs-attr">properties:</span>
        <span class="hljs-attr">userId:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">integer</span>
        <span class="hljs-attr">id:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">integer</span>
        <span class="hljs-attr">title:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
        <span class="hljs-attr">body:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
</code></pre>
<p>The first line here, <code>openapi: “3.0.3”</code>, just tells the generators and parsers that we are using version <code>3.0.3</code>. </p>
<p>Next, we have some more metadata – the name and version of the API. We also have the server we are calling with our APIs.</p>
<p>After defining this metadata, we now define our endpoints. For the sake of this example, let’s assume that we only have one endpoint to call to get posts. We represent this by saying <code>/posts</code> under paths. We then specify which kind it is by specifying <code>get:</code>.</p>
<p>We give a short description of what it does in the <code>summary</code> and then specify an <code>operationId</code> which is what we this function will be called in our generated code. We also specify exactly what structure the response will have, that is, a JSON of an array of <code>Posts</code>.</p>
<p>We then list any components we have across our APIs like the <code>Post</code>. Note that we are using the <code>Post</code> schema in the return response structure before we define it further down. The schemas in components will determine the Model structs we will generate using this yaml file.</p>
<h3 id="heading-step-2-set-up-your-project">Step 2: Set up your project</h3>
<p>Create a new SwiftUI project. For the purpose of this tutorial, we’ll use an iOS app – but you can do this with any app. Select Swift as the language and SwiftUI for the interface.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753047209636/75cad7d3-f403-4285-8209-fd2bb65418e5.png" alt="App Creation Screen" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753047246793/3491aa5d-c35c-4157-adf2-789fe5e9cd96.png" alt="Basic SwiftUI App after it's created" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Add the <code>openapi.yaml</code> file we just created to this project. (You can also create this file in Xcode and copy, paste from the script above.)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753047290178/fc31b759-b1c4-4b48-b58b-d90109614cd0.png" alt="Adding the openapi.yaml file to our project" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Now, add the following swift packages to the project. (<strong>Note: Please read the entire section about adding packages before you proceed.</strong>)</p>
<ol>
<li><p>Swift OpenAPI Generator – <a target="_blank" href="https://github.com/apple/swift-openapi-generator">https://github.com/apple/swift-openapi-generator</a> – The Core Generator Plugin.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753047376776/b1d7b2d0-9b1f-45c8-8c74-4395a4c80dd9.png" alt="Adding Swift OpenAPI Generator to our Project" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753047411622/c6eca0c0-538e-40b5-a9b0-9fa044b60694.png" alt="Making sure that no targets are selected for the OpenAPIGenerator" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
</li>
<li><p>Swift OpenAPI Runtime – <a target="_blank" href="https://github.com/apple/swift-openapi-runtime">https://github.com/apple/swift-openapi-runtime</a> – This contains the common types and protocols used by the code generated by the generator plugin.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753047457532/846ec488-cfed-4ca5-811b-c38dba0aaf30.png" alt="Adding OpenAPIRuntime to our project" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
</li>
<li><p>Swift OpenAPI URLSession – <a target="_blank" href="https://github.com/apple/swift-openapi-urlsession">https://github.com/apple/swift-openapi-urlsession</a> – This is a transport layer that allows the generated code to use the Apple URLSession to make network requests. </p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753047476699/9556047e-cc33-44b4-9980-0c692d4c1d01.png" alt="Adding OpenAPIURLSession to our Project" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
</li>
</ol>
<p>One major caveat to note here when adding these packages is that <strong>The Swift OpenAPI Generator</strong> should <strong>not</strong> be added to your project target. This is because we’re only using this to generate the code, but we’re not using it in the app.</p>
<p>If you get this error: <code>swift-openapi-generator/Sources/_OpenAPIGeneratorCore/PlatformChecks.swift:21:5 _OpenAPIGeneratorCore is only to be used by swift-openapi-generator itself—your target should not link this library or the command line tool directly.</code> – then you made this mistake.</p>
<p>The easiest way to fix this is removing the package and adding it again. Or you can go to <code>Project → Target → Build Phases → Link Binary with Libraries → Remove Swift OpenAPI Generator</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753048265985/100428a4-e46a-4aa1-8fcf-4c74e26ffed6.png" alt="Where to check if you encounter that error" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Now that we added these generator and runtime plugins, we need to give the generator some instructions on what to generate. You can do this with an <code>openapi-generator-config.yaml</code> file. For our project, use the following file. It’s really simple:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">generate:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">types</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">client</span>
</code></pre>
<p>This tells our generator to generate the <strong>types</strong> – the swift structs, enums, and so on from the schema section of the file, and the <strong>client</strong> – the main class which interacts with the networking logic.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753048237863/963d6bc0-a368-427b-add3-75dcf4bd3edf.png" alt="openapi-generator-config.yaml file" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Save this into an <code>openapi-generator-config.yaml</code> file as shown.</p>
<p>And finally, we want the generator to run whenever we want to build this application/target. We can specify this in the Build Phases tab of the target. Under the “ Target → Build Phases → Run Build Tool Plug-ins” , add the OpenAPIGenerator Plugin.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753048334094/24e20adc-c6e0-4d66-965b-19d476a5ffd3.png" alt="Adding the generator in the build phase" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>The first time the project is built after setting this, Xcode will display a security dialog. This will let us “Trust and Enable” for this plugin. It’s a one time confirmation that gives this plugin the permission required to run during the build process.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753048374405/6ff51ff4-d33a-4920-b95f-9fda0ee0aef4.png" alt="Trust and Enable security dialog for the generator" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>As soon as you build the second time after giving these permissions, you will generate the files. You might not see any changes in the Xcode window itself. But if you’re curious to see the result, go to this folder. </p>
<p><code>DerivedData →  &lt;ProjectName&gt;*identifier → Build → intermediates.noindex → BuildToolPluginIntermediates → &lt;TargetName&gt;.output → &lt;TargetName&gt; → OpenAPIGenerator → GeneratedSources</code></p>
<p>More on derived data folder here: <a target="_blank" href="https://gayeugur.medium.com/derived-data-2e9468c6da9b">https://gayeugur.medium.com/derived-data-2e9468c6da9b</a> if you’re curious.</p>
<p>Keep in mind that this location might vary based on Xcode version, OpenAPI version, and your project settings. But you don’t need to worry about the file location.</p>
<p>You will see three files called Client.swift, Types.swift, and Server.swift.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753048508703/8fcec6bf-ad86-47de-b57c-7dca22256e06.png" alt="Generated Files" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>These are the files that the our generator created and populated with the types and functions we need.</p>
<p>In the next section, we discuss how to use these files to make calls to the server.</p>
<h3 id="heading-step-3-write-a-wrapper">Step 3: Write a wrapper</h3>
<p>While it’s certainly possible to make the calls to server using just the generated code (<code>Client</code>) type throughout our application, a more maintainable approach is to use a wrapper around these types. This will provide a stable, clean interface for the rest our our app to use, and it decouples feature code from the generated code.</p>
<p>I can hear you thinking: “Wait a second. Isn’t the entire purpose of generating this code to avoid this boilerplate abstraction?”</p>
<p>While it adds some abstraction on top of the generated code, it’s valuable to have this for number of reasons. Here are but a few of them:</p>
<ol>
<li><p>Better naming. The generated <code>Post</code> struct right now will be called <code>Components.Schemas.Post</code>.</p>
</li>
<li><p>If you ever want to move away from the generator, an abstraction is really helpful.</p>
</li>
<li><p>If you want to Mock this server call, you can do this via the abstraction.</p>
</li>
<li><p>UI Optimization. You might want to flatten the structure of a model to reduce the number of computed variables in there, and so on.</p>
</li>
</ol>
<p>So, we want to wrap this around a file called <code>WebService.swift</code>:</p>
<pre><code class="lang-swift"><span class="hljs-comment">// WebService.swift</span>
<span class="hljs-keyword">import</span> Foundation
<span class="hljs-keyword">import</span> OpenAPIURLSession

<span class="hljs-comment">// A clean, app-specific Post model.</span>
<span class="hljs-comment">// This decouples views from the generated types.</span>
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">AppPost</span>: <span class="hljs-title">Identifiable</span>, <span class="hljs-title">Codable</span> </span>{
    <span class="hljs-keyword">let</span> id: <span class="hljs-type">Int</span>
    <span class="hljs-keyword">let</span> title: <span class="hljs-type">String</span>
    <span class="hljs-keyword">let</span> body: <span class="hljs-type">String</span>
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">WebService</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> client: <span class="hljs-type">Client</span>

    <span class="hljs-keyword">init</span>() {
        <span class="hljs-comment">// The server URL and transport are from the generated code.</span>
        <span class="hljs-comment">// `Servers.Server1.url()` corresponds to the first URL in the `servers` array of the spec.</span>
        <span class="hljs-keyword">self</span>.client = <span class="hljs-type">Client</span>(
            serverURL: <span class="hljs-keyword">try</span>! <span class="hljs-type">Servers</span>.<span class="hljs-type">Server1</span>.url(),
            transport: <span class="hljs-type">URLSessionTransport</span>()
        )
    }

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">getPosts</span><span class="hljs-params">()</span></span> async <span class="hljs-keyword">throws</span> -&gt; [<span class="hljs-type">AppPost</span>] {
        <span class="hljs-comment">// Call the generated method, which was named using `operationId`.</span>
        <span class="hljs-keyword">let</span> response = <span class="hljs-keyword">try</span> await client.getPosts(.<span class="hljs-keyword">init</span>())

        <span class="hljs-comment">// The generated response is a type-safe enum covering all documented status codes.</span>
        <span class="hljs-keyword">switch</span> response {
        <span class="hljs-keyword">case</span>.ok(<span class="hljs-keyword">let</span> okResponse):
            <span class="hljs-comment">// The body is also a type-safe enum for different content types.</span>
            <span class="hljs-keyword">switch</span> okResponse.body {
            <span class="hljs-keyword">case</span>.json(<span class="hljs-keyword">let</span> posts):
                <span class="hljs-comment">// Map the generated `Components.Schemas.Post` to our clean `AppPost` model.</span>
                <span class="hljs-keyword">return</span> posts.<span class="hljs-built_in">map</span> { post <span class="hljs-keyword">in</span>
                    <span class="hljs-type">AppPost</span>(id: post.id, title: post.title, body: post.body)
                }
            }
        <span class="hljs-comment">// The generator forces the handling of other documented responses.</span>
        <span class="hljs-comment">// Our simple spec only has a 200, so any other response is undocumented.</span>
        <span class="hljs-keyword">case</span>.undocumented(statusCode: <span class="hljs-keyword">let</span> statusCode, <span class="hljs-number">_</span>):
            <span class="hljs-keyword">throw</span> <span class="hljs-type">URLError</span>(.badServerResponse, userInfo: [<span class="hljs-string">"statusCode"</span>: statusCode])
        }
    }
}
</code></pre>
<p>Let’s go through this file to understand what we’re doing.</p>
<p>First, we import <code>OpenAPIUrlSession</code> along with <code>Foundation</code>. This allows us to call the server, get a response and parse that response.</p>
<p>Next, we define the new <code>AppPost</code> struct. This is meant to be the representation of a <code>Post</code> in the App. In the generated <code>Types.Swift</code> file, we have the generated <code>Post</code> structure. This is defined as:</p>
<pre><code class="lang-swift"><span class="hljs-comment">/// - Remark: Generated from `#/components/schemas/Post`.</span>
        <span class="hljs-keyword">internal</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Post</span>: <span class="hljs-title">Codable</span>, <span class="hljs-title">Hashable</span>, <span class="hljs-title">Sendable</span> </span>{
            <span class="hljs-comment">/// - Remark: Generated from `#/components/schemas/Post/userId`.</span>
            <span class="hljs-keyword">internal</span> <span class="hljs-keyword">var</span> userId: <span class="hljs-type">Swift</span>.<span class="hljs-type">Int</span>
            <span class="hljs-comment">/// - Remark: Generated from `#/components/schemas/Post/id`.</span>
            <span class="hljs-keyword">internal</span> <span class="hljs-keyword">var</span> id: <span class="hljs-type">Swift</span>.<span class="hljs-type">Int</span>
            <span class="hljs-comment">/// - Remark: Generated from `#/components/schemas/Post/title`.</span>
            <span class="hljs-keyword">internal</span> <span class="hljs-keyword">var</span> title: <span class="hljs-type">Swift</span>.<span class="hljs-type">String</span>
            <span class="hljs-comment">/// - Remark: Generated from `#/components/schemas/Post/body`.</span>
            <span class="hljs-keyword">internal</span> <span class="hljs-keyword">var</span> body: <span class="hljs-type">Swift</span>.<span class="hljs-type">String</span>
            <span class="hljs-comment">/// Creates a new `Post`.</span>
            <span class="hljs-comment">///</span>
            <span class="hljs-comment">/// - Parameters:</span>
            <span class="hljs-comment">///   - userId:</span>
            <span class="hljs-comment">///   - id:</span>
            <span class="hljs-comment">///   - title:</span>
            <span class="hljs-comment">///   - body:</span>
            <span class="hljs-keyword">internal</span> <span class="hljs-keyword">init</span>(
                userId: <span class="hljs-type">Swift</span>.<span class="hljs-type">Int</span>,
                id: <span class="hljs-type">Swift</span>.<span class="hljs-type">Int</span>,
                title: <span class="hljs-type">Swift</span>.<span class="hljs-type">String</span>,
                body: <span class="hljs-type">Swift</span>.<span class="hljs-type">String</span>
            ) {
                <span class="hljs-keyword">self</span>.userId = userId
                <span class="hljs-keyword">self</span>.id = id
                <span class="hljs-keyword">self</span>.title = title
                <span class="hljs-keyword">self</span>.body = body
            }
            <span class="hljs-keyword">internal</span> <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">CodingKeys</span>: <span class="hljs-title">String</span>, <span class="hljs-title">CodingKey</span> </span>{
                <span class="hljs-keyword">case</span> userId
                <span class="hljs-keyword">case</span> id
                <span class="hljs-keyword">case</span> title
                <span class="hljs-keyword">case</span> body
            }
        }
</code></pre>
<p>As you can see, our <code>AppPost</code> struct is different from this generated type. We omit the <code>userId</code> since we do not care about it (at least for now).</p>
<p>Back to the <code>WebService</code> class, we see a <code>client</code> attribute. This is a generated type variable that will let us interact with the servers. In the initializer of the <code>WebService</code> class, we create a new <code>Client</code> using the first server URL we specified in the schema and use the <code>URLSessionTransport</code> object for making these calls.</p>
<p>We then define our methods. In this case, our <code>getPosts()</code> function which returns <code>[AppPost]</code> array.</p>
<p><code>let response = try await client.getPosts(.init())</code> will call the function <code>getPosts()</code> on the <code>Client</code> object. The <code>Client.getPosts()</code> function here takes in an input struct called <code>Operations.getPosts.Input</code> which is initialized by the <code>.init()</code> passed here.</p>
<p>This generated response is a type-safe enum covering all documented codes. (Currently only <code>200</code> in our yaml file). So, we use a simple switch to look at both these cases and further use more switch statements to get the proper response. You can see how much easier this is than to parse the response manually.</p>
<p>Once we get the <code>Components.Schemas.Post</code> response, we map and convert it into <code>[AppPost]</code> array and return it.</p>
<p>Now, let’s use this wrapper to display data in our app.</p>
<h3 id="heading-step-4-call-the-wrapper-and-display-the-data">Step 4: Call the wrapper and display the data</h3>
<p>We’re at the final step now. We’ll use the wrapper we created to display the fetched posts. We’ll also use a state variable to store our <code>AppPost</code> array in our <code>ContentView</code> view. We’ll then call <code>getPosts()</code> when the view is first displayed to the user.</p>
<pre><code class="lang-swift"><span class="hljs-comment">// ContentView.swift</span>
<span class="hljs-keyword">import</span> SwiftUI

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ContentView</span>: <span class="hljs-title">View</span> </span>{
    @<span class="hljs-type">State</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> posts: [<span class="hljs-type">AppPost</span>] = []
    @<span class="hljs-type">State</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> errorMessage: <span class="hljs-type">String?</span>

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> webService = <span class="hljs-type">WebService</span>()

    <span class="hljs-keyword">var</span> body: some <span class="hljs-type">View</span> {
        <span class="hljs-type">NavigationStack</span> {
            <span class="hljs-type">List</span>(posts) { post <span class="hljs-keyword">in</span>
                <span class="hljs-type">VStack</span>(alignment:.leading, spacing: <span class="hljs-number">8</span>) {
                    <span class="hljs-type">Text</span>(post.title)
                       .font(.headline)
                    <span class="hljs-type">Text</span>(post.body)
                       .font(.subheadline)
                       .foregroundColor(.secondary)
                }
               .padding(.vertical, <span class="hljs-number">4</span>)
            }
           .navigationTitle(<span class="hljs-string">"Posts"</span>)
           .task {
                await loadPosts()
            }
           .overlay {
                <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> errorMessage {
                    <span class="hljs-type">ContentUnavailableView</span>(<span class="hljs-string">"Error"</span>, systemImage: <span class="hljs-string">"xmark.octagon"</span>, description: <span class="hljs-type">Text</span>(errorMessage))
                } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> posts.isEmpty {
                    <span class="hljs-type">ProgressView</span>()
                }
            }
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">loadPosts</span><span class="hljs-params">()</span></span> async {
        <span class="hljs-keyword">self</span>.errorMessage = <span class="hljs-literal">nil</span>
        <span class="hljs-keyword">do</span> {
            <span class="hljs-keyword">self</span>.posts = <span class="hljs-keyword">try</span> await webService.getPosts()
        } <span class="hljs-keyword">catch</span> {
            <span class="hljs-keyword">self</span>.errorMessage = error.localizedDescription
        }
    }
}

#<span class="hljs-type">Preview</span> {
    <span class="hljs-type">ContentView</span>()
}
</code></pre>
<p>You can see the dummy posts in the Preview. As you can see, all we had to do was call the <code>webService.getPosts()</code> to populate the variable.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753053957409/b89d50e8-ab73-4484-beda-3a328a575144.png" alt="Simulator Run of the app showing the fetched posts" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>You might be thinking that this is a lot of setup for a simple struct like <code>Post</code> for which we had to create a wrapper called <code>AppPost</code> anyway. But if you had ten types like this and twenty endpoints to call? You wouldn’t have to deal with a lot of repetitive, error-prone code.</p>
<h2 id="heading-potential-pitfalls">Potential Pitfalls</h2>
<p>Unfortunately, no process is perfect. You might still face a lot of issues with generated code and this method. I’ve listed some of them here and how to deal with them.</p>
<h3 id="heading-verbose-or-ugly-generated-code">Verbose or ugly generated code</h3>
<p>If you have very verbose or ugly generated code, the problem is almost always the missing <code>operationId</code> for an API path. If you don’t specify one, the generator must create a name from the path and the HTTP method with results in long unwieldy names. Adding a clear <code>operationId</code> will mitigate this issue.</p>
<h3 id="heading-large-specs-and-performance-issues">Large Specs and Performance Issues</h3>
<p>If you have a very large Spec file, generating a client for this entire specification can significantly increase the compile time. It can also result in absolutely massive <code>Types.swift</code> and <code>Client.swift</code> files.</p>
<p>There is a filter option in the <code>openapi-generator-config.yaml</code> file that will allow the generator to include only parts of the spec that are relevant to the application to improve build times and so on. But if you want everything in an API that has hundreds of endpoints, the only way to reduce compile times is to avoid regenerating this every time and decouple this step from the regular build process.</p>
<h3 id="heading-unsupported-spec-features">Unsupported Spec Features</h3>
<p>While the swift package, <code>swift-openapi-generator</code>, is robust, it does not support all the features included in the specification. I had issues with some features of the newer spec version ( <code>3.1.1</code> and had to downgrade to <code>3.0.3</code> to make it work well ).</p>
<p>There are also known issues like lack of support for certain types of recursive schemas. Sometimes, the generator errors out and fails and some other times, it generates incomplete types – which can result in a few hours of debugging (I speak from experience).</p>
<p>In any case, knowing the limits of this generator can be helpful in avoiding issues it might cause. Also keep in mind that it is always getting better thanks to its open source nature.</p>
<h2 id="heading-conclusion-embrace-spec-driven-development">Conclusion: Embrace Spec-Driven Development</h2>
<p>In this guide, you navigated the journey of adopting <code>swift-openapi-generator</code> – from understanding the power of API contracts to building a functional SwiftUI app. You also learned about the real life challenges of this process. While there is an initial learning curve, the benefits of this approach are profound.</p>
<p>The core tenet of this approach is to foster more disciplined and more robust method for building applications. By making the OpenAPI document the single source of truth, you make sure that both the frontend and backend are perfectly in sync in perpetuity.</p>
<p>Using this approach also results in more type-safe, maintainable code. The result is less time spent on writing boilerplate and debugging random integration errors and more time spent creating the app itself.</p>
<p>For developers ready to explore further, please checkout the official <code>swift-openapi-generator</code> repository on Github here: <a target="_blank" href="https://github.com/apple/swift-openapi-generator">https://github.com/apple/swift-openapi-generator</a>.</p>
<p>You can follow me on <a target="_blank" href="https://github.com/sravankaruturi">GitHub</a> and <a target="_blank" href="https://hashnode.com/@sravankaruturi">Hashnode</a> for my other posts and projects.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Work with OpenAPI in Go ]]>
                </title>
                <description>
                    <![CDATA[ Well-structured and well-documented APIs are a pleasure to work with. And nowadays the standard is OpenAPI, which comes with a good methodology for defining an API interface first, and only then constructing everything around it. This makes it easier... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-work-with-openapi-in-go/</link>
                <guid isPermaLink="false">67b5da4da9d7e29052110938</guid>
                
                    <category>
                        <![CDATA[ APIs ]]>
                    </category>
                
                    <category>
                        <![CDATA[ REST API ]]>
                    </category>
                
                    <category>
                        <![CDATA[ OpenApi ]]>
                    </category>
                
                    <category>
                        <![CDATA[ swagger ]]>
                    </category>
                
                    <category>
                        <![CDATA[ RESTful APIs ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                    <category>
                        <![CDATA[ golang ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Alex Pliutau ]]>
                </dc:creator>
                <pubDate>Wed, 19 Feb 2025 13:19:09 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1740164911110/7954a7d5-39dc-4504-82eb-f5fe583b7b84.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Well-structured and well-documented APIs are a pleasure to work with. And nowadays the standard is <a target="_blank" href="https://www.openapis.org/">OpenAPI</a>, which comes with a good methodology for defining an API interface first, and only then constructing everything around it.</p>
<p>This makes it easier to understand, implement, and consume those APIs. And standards matter, as they allow different teams, regardless of their technology stack, to effectively communicate about and work with the same API.</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3191c4bd-690b-45e8-86bb-8b460ae434c6_1295x1490.png" alt="API Lifecycle" width="1295" height="1490" loading="lazy"></p>
<p>In this practical guide, I’ll want to walk you through all the important parts involved in architecting, implementing, and consuming an API using the OpenAPI standard.</p>
<p>Before we dive in, it's helpful to have a basic understanding of the following:</p>
<ul>
<li><p>The Go programming language</p>
</li>
<li><p>RESTful APIs</p>
</li>
<li><p>JSON/YAML</p>
</li>
<li><p>Basic command-line usage</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-what-is-the-openapi-specification-oas">What is the OpenAPI Specification (OAS)?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-architecting-the-api">Architecting the API</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-openapiyaml">openapi.yaml</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-paths-and-operations">Paths and Operations</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-schemas">Schemas</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-extensions">Extensions</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-generate-a-go-server">How to Generate a Go Server</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-visualize-api-docs">How to visualize API docs</a></p>
</li>
<li><p><a target="_blank" href="heading-client-code">Client Code</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-what-is-the-openapi-specification-oas">What is the OpenAPI Specification (OAS)?</h2>
<p>The OpenAPI Specification (OAS) provides a consistent means to carry information through each stage of the API lifecycle. It is a specification language for HTTP APIs that defines structure and syntax in a way that is not wedded to the programming language the API is created in.</p>
<p>The <a target="_blank" href="https://spec.openapis.org/">OpenAPI Specification (OAS)</a> was originally based on the Swagger 2.0 Specification from SmartBear Software. Later it was moved to the <a target="_blank" href="https://www.openapis.org/">OpenAPI Initiative (OAI)</a>, a consortium of industry experts under the Linux Foundation.</p>
<p>The main idea of OpenAPI is to be able to describe APIs in agnostic terms, decoupling them from any specific programming language. Consumers of your API specification do not need to understand the guts of your application or try to learn Lisp or Haskell if that’s what you chose to write it in. They can understand exactly what they need from your API specification, written in a simple and expressive language.</p>
<p>This simple and expressive language is called <a target="_blank" href="https://www.jetbrains.com/mps/concepts/domain-specific-languages/">DSL (domain specific language)</a>. It can be written in either JSON or YAML.</p>
<p>The latest version of OAS is <a target="_blank" href="https://spec.openapis.org/oas/latest.html">v3.1.1</a> and the specification itself is huge. There are many features and corner cases, but we will try to go through the most important ones.</p>
<h2 id="heading-architecting-the-api">Architecting the API</h2>
<p>It all starts with defining what the API should provide for its consumers and what it is for. While this stage isn't always purely technical, having a sketch of your API design in OAS when gathering requirements gives you a head start when starting the design.</p>
<p>Once the requirements are ready, it's time to open your <a target="_blank" href="https://editor.swagger.io/">OpenAPI editor</a> and collaborate with your teammates.</p>
<p>And it's important to understand that it's not only about writing the JSON/YAML spec, but actually agreeing on the API design.</p>
<p>I recommend that you follow some API design guide – <a target="_blank" href="https://cloud.google.com/apis/design">Google has</a> <a target="_blank" href="https://cloud.google.com/apis/design">one</a>, for example. This will help you avoid mixed styles (like <strong>/resourceName/{id}</strong> and <strong>/resource_name/{id}</strong>, inconsistent use of HTTP methods, or unclear resource relationships.</p>
<h3 id="heading-openapiyaml">openapi.yaml</h3>
<p>The spec of your API starts in the entrypoint document <code>openapi.yaml</code> (recommended but not required name) or <code>openapi.json</code>. I've seen very big <code>openapi.yaml</code> files (50k lines), but it's possible to split your spec into multiple parts. Just keep in mind that this may not work well for some OpenAPI tools as they expect a single file. <a target="_blank" href="https://github.com/googlemaps/openapi-specification/">Google Maps OAS</a> is a good example on how to split the schema, but also comes with a pre-processor to generate a single file.</p>
<p>There are some open source tools to bundle the OAS: <a target="_blank" href="https://github.com/APIDevTools/swagger-cli">swagger-cli</a> (archived) and <a target="_blank" href="https://github.com/Redocly/redocly-cli">redocly-cli</a> are great options.</p>
<pre><code class="lang-bash">swagger-cli bundle -o _bundle/openapi.yaml openapi.yaml
</code></pre>
<p>As I mentioned earlier, the spec is huge, but let's break it into smaller parts. For this tutorial I created a dummy "Smart Home" API. You can see the full spec and code <a target="_blank" href="https://github.com/plutov/packagemain/tree/master/oapi-example">here</a>.</p>
<p>The root object is called <a target="_blank" href="https://spec.openapis.org/oas/latest.html#openapi-object">OpenAPI Object</a> and has the following structure:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># schema version</span>
<span class="hljs-attr">openapi:</span> <span class="hljs-number">3.1</span><span class="hljs-number">.1</span>

<span class="hljs-comment"># docs</span>
<span class="hljs-attr">info:</span>
  <span class="hljs-attr">title:</span> <span class="hljs-string">Smart</span> <span class="hljs-string">Home</span> <span class="hljs-string">API</span>
  <span class="hljs-attr">description:</span> <span class="hljs-string">API</span> <span class="hljs-string">Specification</span> <span class="hljs-string">for</span> <span class="hljs-string">Smart</span> <span class="hljs-string">Home</span> <span class="hljs-string">API</span>
  <span class="hljs-attr">version:</span> <span class="hljs-number">0.0</span><span class="hljs-number">.1</span>

<span class="hljs-comment"># optional servers for public APIs</span>
<span class="hljs-attr">servers:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">url:</span> <span class="hljs-string">"https://..."</span>

<span class="hljs-comment"># tags are used to group the endpoints</span>
<span class="hljs-attr">tags:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">device</span>
    <span class="hljs-attr">description:</span> <span class="hljs-string">Manage</span> <span class="hljs-string">devices</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">room</span>
    <span class="hljs-attr">description:</span> <span class="hljs-string">Manage</span> <span class="hljs-string">rooms</span>

<span class="hljs-comment"># endpoints go here</span>
<span class="hljs-attr">paths:</span>
  <span class="hljs-comment"># ...</span>

<span class="hljs-comment"># reusable objects such as schemas, error types, request bodies</span>
<span class="hljs-attr">components:</span>
  <span class="hljs-comment"># ...</span>

<span class="hljs-comment"># security mechanisms, should correspond to components.securitySchemes</span>
<span class="hljs-attr">security:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">apiKeyAuth:</span> []
</code></pre>
<p>We defined the skeleton of our schema, but the majority of OpenAPI schema lays in the <code>paths</code> and <code>components</code> props.</p>
<h3 id="heading-paths-and-operations">Paths and Operations</h3>
<p>Let's now add a few endpoints to our schema. The operations are grouped by paths, so you can have multiple HTTP methods on a single path – for example <code>GET /devices/{deviceId}</code> and <code>DELETE /devices/{deviceId}</code>.</p>
<p>It's a good practice to define all types (request bodies, responses, errors) in the <code>components</code> section and reference them instead of manually defining them in the <code>paths</code> section. This allows for easier re-use of entities. For example, in our API we have a type <code>Device</code> which can be used in many endpoints.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">paths:</span>

  <span class="hljs-comment"># the path has a parameter in it</span>
  <span class="hljs-string">/devices/{deviceId}:</span>
    <span class="hljs-attr">get:</span>
      <span class="hljs-attr">tags:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">device</span>
      <span class="hljs-attr">summary:</span> <span class="hljs-string">Get</span> <span class="hljs-string">Device</span>
      <span class="hljs-attr">operationId:</span> <span class="hljs-string">getDevice</span>

      <span class="hljs-attr">parameters:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">deviceId</span>
          <span class="hljs-attr">in:</span> <span class="hljs-string">path</span>
          <span class="hljs-attr">required:</span> <span class="hljs-literal">true</span>
          <span class="hljs-attr">schema:</span>
            <span class="hljs-string">$ref:</span> <span class="hljs-string">"#/components/schemas/ULID"</span>

      <span class="hljs-attr">responses:</span>

        <span class="hljs-attr">"200":</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Success</span>
          <span class="hljs-attr">content:</span>
            <span class="hljs-attr">application/json:</span>
              <span class="hljs-attr">schema:</span>
                <span class="hljs-string">$ref:</span> <span class="hljs-string">"#/components/schemas/Device"</span>

        <span class="hljs-attr">"404":</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Not</span> <span class="hljs-string">Found</span>
          <span class="hljs-attr">content:</span>
            <span class="hljs-attr">application/json:</span>
              <span class="hljs-attr">schema:</span>
                <span class="hljs-comment"># use common type for 404 errors</span>
                <span class="hljs-string">$ref:</span> <span class="hljs-string">"#/components/schemas/ErrorNotFound"</span>
</code></pre>
<p>In the spec above, we defined two endpoints of our API and referenced the types which we still need to define: <code>Device</code>, <code>ErrorNotFound</code> and <code>ULID</code>. Notice that for the <code>deviceId</code> path param we also used a custom type instead of a standard string, which can be helpful in the future in case we want to change the format of our IDs (for example UUID, ULID. integer, and so on).</p>
<p>Notice that each operation has a unique <code>operationId</code>. While it's optional, it's very helpful to set one, so then it can be used on the server and client sides.</p>
<p>This is a basic configuration which you can extend further if you want to. For example, when serving this schema in Swagger, it's good to see the examples of our requests (and their variations). We can define it here in <code>responses</code> section, or directly in our <code>components.schemas</code>.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">responses:</span>
  <span class="hljs-attr">"200":</span>
    <span class="hljs-attr">content:</span>
      <span class="hljs-attr">application/json:</span>
        <span class="hljs-attr">examples:</span>
          <span class="hljs-attr">new_device:</span>
            <span class="hljs-attr">value:</span> <span class="hljs-comment"># any value</span>
</code></pre>
<h3 id="heading-schemas">Schemas</h3>
<p><code>components</code> is an integral part of OAS, and contains the following properties:</p>
<ul>
<li><p>schemas</p>
</li>
<li><p>responses</p>
</li>
<li><p>parameters</p>
</li>
<li><p>requestBodies</p>
</li>
<li><p>headers</p>
</li>
<li><p>securitySchemes</p>
</li>
</ul>
<p>You can <a target="_blank" href="https://spec.openapis.org/oas/latest.html#components-object">see all here</a>.</p>
<p>We could define our <code>Device</code> type like this:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">components:</span>
  <span class="hljs-attr">schemas:</span>
    <span class="hljs-attr">Device:</span>
      <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
      <span class="hljs-attr">properties:</span>
        <span class="hljs-attr">id:</span>
          <span class="hljs-string">$ref:</span> <span class="hljs-string">'#/components/schemas/ULID'</span>
        <span class="hljs-attr">name:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
      <span class="hljs-attr">required:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">id</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">name</span>
</code></pre>
<p>But later you may have other types that have <code>name</code> or <code>id</code> fields, so it's recommended to define them separately and combine them in the final type using <code>allOf</code>:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">components:</span>
  <span class="hljs-attr">schemas:</span>
    <span class="hljs-attr">WithId:</span>
      <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
      <span class="hljs-attr">required:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">id</span>
      <span class="hljs-attr">properties:</span>
        <span class="hljs-attr">id:</span>
          <span class="hljs-string">$ref:</span> <span class="hljs-string">"#/components/schemas/ULID"</span>

    <span class="hljs-attr">WithName:</span>
      <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
      <span class="hljs-attr">required:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">name</span>
      <span class="hljs-attr">properties:</span>
        <span class="hljs-attr">name:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>

    <span class="hljs-attr">Device:</span>
      <span class="hljs-attr">allOf:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">$ref:</span> <span class="hljs-string">"#/components/schemas/WithId"</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">$ref:</span> <span class="hljs-string">"#/components/schemas/WithName"</span>
</code></pre>
<p><code>allOf</code>, <code>oneOf</code>, and <code>anyOf</code> are very powerful techniques for modeling your OAS.</p>
<h3 id="heading-extensions">Extensions</h3>
<p>OpenAPI schemas can be extended with internal properties that do not affect the schema itself, but are useful for server or client generators. A good example is our <a target="_blank" href="https://github.com/ulid/spec">ULID</a> type for ids:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">ULID:</span>
  <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
  <span class="hljs-attr">minLength:</span> <span class="hljs-number">26</span>
  <span class="hljs-attr">maxLength:</span> <span class="hljs-number">26</span>

  <span class="hljs-comment"># example is useful for Swagger docs</span>
  <span class="hljs-attr">example:</span> <span class="hljs-string">01ARZ3NDEKTSV4RRFFQ69G5FAV</span>

  <span class="hljs-attr">x-go-type:</span> <span class="hljs-string">ulid.ULID</span>
  <span class="hljs-attr">x-go-type-import:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">github.com/oklog/ulid/v2</span>
</code></pre>
<p>The <code>x-</code> props will be used by the Go server generator to use existing Go types for this field instead of generating a new one.</p>
<h2 id="heading-how-to-generate-a-go-server">How to Generate a Go Server</h2>
<p>We didn't go through all possible schema properties here and just covered the main ones – so if you’re not familiar with OAS, you should now have a good understanding of this standard. You can read the whole specification <a target="_blank" href="https://spec.openapis.org/oas/latest.html">here</a>. But now as our schema is ready, we can generate a Go server from it.</p>
<p>You can find the full list of generators on <a target="_blank" href="https://openapi.tools/">opeanapi.tools</a> – there are a lot of them. But the most popular one for Go servers is <a target="_blank" href="https://github.com/oapi-codegen/oapi-codegen">oapi-codegen</a>.</p>
<blockquote>
<p>oapi-codegen currently doesn’t support this OAS 3.1. <a target="_blank" href="https://github.com/oapi-codegen/oapi-codegen/issues/373">issue</a>. <a target="_blank" href="https://github.com/ogen-go/ogen/">ogen</a> does, though.</p>
</blockquote>
<p>You can install it via <code>go install</code>:</p>
<pre><code class="lang-bash">go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest
</code></pre>
<p>The configuration for the <code>oapi-codegen</code> generator is straightforward. You can either provide command line arguments or specify the same arguments in a yaml configuration file. You can choose which HTTP router to use for the server, where to put the output file, and more. In our case let's use the <a target="_blank" href="https://github.com/labstack/echo">echo</a> router.</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># oapi-codegen.yaml</span>

<span class="hljs-attr">package:</span> <span class="hljs-string">api</span>
<span class="hljs-attr">output:</span> <span class="hljs-string">pkg/api/api.gen.go</span>

<span class="hljs-attr">generate:</span>
  <span class="hljs-attr">strict-server:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">models:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">echo-server:</span> <span class="hljs-literal">true</span>
</code></pre>
<p>We can now generate the server code using the following command:</p>
<pre><code class="lang-bash">oapi-codegen --config=oapi-codegen.yaml openapi.yaml
</code></pre>
<p>Let's now explore the generated <code>api.gen.go</code> file.</p>
<p>Since we enabled <code>strict-server</code>, which will generate code that parses request bodies and encodes responses automatically, the interface that we need to implement is called <code>StrictServerInterface</code>:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> StrictServerInterface <span class="hljs-keyword">interface</span> {

  <span class="hljs-comment">// List Devices</span>
  <span class="hljs-comment">// (GET /devices)</span>
  ListDevices(ctx context.Context, request ListDevicesRequestObject) (ListDevicesResponseObject, error)

  <span class="hljs-comment">// Get Device</span>
  <span class="hljs-comment">// (GET /devices/{deviceId})</span>
  GetDevice(ctx context.Context, request GetDeviceRequestObject) (GetDeviceResponseObject, error)

}
</code></pre>
<p>All our types are also generated:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> ULID = ulid.ULID

<span class="hljs-keyword">type</span> Device <span class="hljs-keyword">struct</span> {
    Id   ULID   <span class="hljs-string">`json:"id"`</span>
    Name <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"name"`</span>
}

<span class="hljs-comment">// ...</span>
</code></pre>
<p>As well as code to parse the requests automatically and the Swagger definition.</p>
<h3 id="heading-implementation">Implementation</h3>
<p>What's left for us to do is to create a server using echo, implement the generated interface, and glue everything together. We can write the following code in <code>pkg/api/impl.go</code>:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> api

<span class="hljs-keyword">import</span> <span class="hljs-string">"context"</span>

<span class="hljs-keyword">type</span> Server <span class="hljs-keyword">struct</span>{}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NewServer</span><span class="hljs-params">()</span> <span class="hljs-title">Server</span></span> {
    <span class="hljs-keyword">return</span> Server{}
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(Server)</span> <span class="hljs-title">ListDevices</span><span class="hljs-params">(ctx context.Context, request ListDevicesRequestObject)</span> <span class="hljs-params">(ListDevicesResponseObject, error)</span></span> {
    <span class="hljs-comment">// actual implementation</span>
    <span class="hljs-keyword">return</span> ListDevices200JSONResponse{}, <span class="hljs-literal">nil</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(Server)</span> <span class="hljs-title">GetDevice</span><span class="hljs-params">(ctx context.Context, request GetDeviceRequestObject)</span> <span class="hljs-params">(GetDeviceResponseObject, error)</span></span> {
    <span class="hljs-comment">// actual implementation</span>
    <span class="hljs-keyword">return</span> GetDevice200JSONResponse{}, <span class="hljs-literal">nil</span>
}
</code></pre>
<p>I skipped the implementation part and just demonstrated how to return the responses. It's quite handy that <code>oapi-codegen</code> generated all possible responses for us.</p>
<p>That leaves us to start the echo server itself. Note that we don't need to write any endpoints manually now, and all request and response parsing is handled for us. Still, we need to validate the requests inside our implementation.</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"oapiexample/pkg/api"</span>

    <span class="hljs-string">"github.com/labstack/echo/v4"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    server := api.NewServer()

    e := echo.New()

    api.RegisterHandlers(e, api.NewStrictHandler(
        server,
        <span class="hljs-comment">// add middlewares here if needed</span>
        []api.StrictMiddlewareFunc{},
    ))

    e.Start(<span class="hljs-string">"127.0.0.1:8080"</span>)
}
</code></pre>
<p>Now when we run our server using <code>go run .</code>, we can curl <code>localhost:8080/devices</code> to see the response!</p>
<h3 id="heading-supported-servers">Supported servers</h3>
<p><code>oapi-codegen</code> supports many web frameworks/servers, such as Chi, Fiber, Gin as well as standard <code>net/http</code>.</p>
<h3 id="heading-how-to-visualize-api-docs">How to visualize API docs</h3>
<p>Sometimes it's handy to have Swagger docs shipped together with your API – for testing, for example, or just as public documentation. <code>oapi-codegen</code> doesn't generate the Swagger UI out of the box, but we can have a simple HTML page that has a Swagger JS which loads our OAS.</p>
<p>You can find the HTML code for our <code>pkg/api/index.html</code> <a target="_blank" href="https://swagger.io/docs/open-source-tools/swagger-ui/usage/installation/">here</a>.</p>
<p>And then we can use <code>go:embed</code> to embed the static files and add our Swagger endpoint:</p>
<pre><code class="lang-go"><span class="hljs-comment">//go:embed pkg/api/index.html</span>
<span class="hljs-comment">//go:embed openapi.yaml</span>
<span class="hljs-keyword">var</span> swaggerUI embed.FS

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// ...</span>

    <span class="hljs-comment">// serve swagger docs</span>
    e.GET(<span class="hljs-string">"/swagger/*"</span>, echo.WrapHandler(http.StripPrefix(<span class="hljs-string">"/swagger/"</span>, http.FileServer(http.FS(swaggerUI)))))
}
</code></pre>
<p>Now we can visit <code>localhost:8080/swagger/</code> to see the Swagger UI with our OAS.</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9d35f8f-e1e7-4e51-9149-e180bb192fd8_1092x922.png" alt="Swagger UI" width="1092" height="922" loading="lazy"></p>
<p>Tools like Postman are very popular for API documentation, and it's also possible to <a target="_blank" href="http://learning.postman.com/docs/integrations/available-integrations/working-with-openAPI/">import</a> your existing OpenAPI 3.0 and 3.1 definitions into Postman. Postman supports both YAML and JSON formats.</p>
<h3 id="heading-generate-oas-from-code">Generate OAS from code</h3>
<p>There is also a practice to generate OpenAPI schemas from code, especially in typed languages. This approach has been popular, with the main selling point being that keeping your OpenAPI schema near the code will hopefully mean that developers keep it up to date as they work on the code.</p>
<p>This is not always the case, which is one of a few reasons this practice is dying out. And I am also not a big fan, as I haven’t seen a big value in this. Anyway, you can have a look at the following projects: <a target="_blank" href="https://github.com/go-swagger/go-swagger">go-swagger</a>, <a target="_blank" href="https://github.com/swaggo/swag">swag</a>, <a target="_blank" href="https://github.com/swaggest/rest/">swaggest/rest</a>.</p>
<h2 id="heading-client-code">Client Code</h2>
<p>As mentioned earlier, OpenAPI is very powerful for collaboration between teams, and all you have to do now is to properly version your schema (see <code>info.version</code> part) and distribute it across the teams.</p>
<p>This part can be automated to some extent by packaging your OpenAPI schema and making it available. I've seen devs use Git submodules for that or GitHub actions to publish the version schemas.</p>
<p>Let's assume our client is a web application written in TypeScript, which is quite common for web APIs. Again, there are may generators available at <a target="_blank" href="https://openapi.tools/">opeanapi.tools</a> online but the most popular one is <a target="_blank" href="https://openapi-ts.dev/">openapi-typescript</a>.</p>
<p>Here's how you can generate the TypeScript code for local or remote schemas:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Local schema</span>
npx openapi-typescript openapi.yaml -o ./client/schema.d.ts

<span class="hljs-comment"># Remote schema</span>
npx openapi-typescript https://.../openapi.yaml -o ./client/schema.d.ts
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>OpenAPI is a de-facto standard for designing, implementing, and consuming REST APIs, so it's crucial to understand how it works.</p>
<p>I hope this article has provided a useful introduction to the OpenAPI Specification, as well as practical tips and examples for how to use OAS to architect, implement, and consume APIs.</p>
<h3 id="heading-resources">Resources</h3>
<ul>
<li><p><a target="_blank" href="https://github.com/plutov/packagemain/tree/master/oapi-example">Source code</a></p>
</li>
<li><p><a target="_blank" href="https://www.openapis.org/">OpenAPI Initiative</a></p>
</li>
<li><p><a target="_blank" href="https://openapi.tools/">openapi.tools</a></p>
</li>
<li><p><a target="_blank" href="https://editor.swagger.io/">Swagger Editor</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/oapi-codegen/oapi-codegen">oapi-codegen</a></p>
</li>
<li><p><a target="_blank" href="https://packagemain.tech">Explore more articles on packagemain.tech</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Handle Complex Use Cases in Your OpenAPI Specifications – API Documentation Guide ]]>
                </title>
                <description>
                    <![CDATA[ When you’re documenting an API reference, there are two main approaches you can follow. You can either use the manual approach of filling in the endpoints via a user interface, or organize a structured document containing all the necessary informatio... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-handle-complex-use-cases-in-api-specs/</link>
                <guid isPermaLink="false">6728f9e02b7cd4e7135f2f39</guid>
                
                    <category>
                        <![CDATA[ Technical writing  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ OpenApi ]]>
                    </category>
                
                    <category>
                        <![CDATA[ APIs ]]>
                    </category>
                
                    <category>
                        <![CDATA[ documentation ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Onyeanuna Prince ]]>
                </dc:creator>
                <pubDate>Mon, 04 Nov 2024 16:44:16 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1730415311732/58afe01c-0ac4-4351-a4b4-a15729b5bcb1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When you’re documenting an API reference, there are two main approaches you can follow. You can either use the manual approach of filling in the endpoints via a user interface, or organize a structured document containing all the necessary information about your API.</p>
<p>This structured document is called an <a target="_blank" href="https://www.openapis.org/">OpenAPI specification</a> (OpenAPI Spec or OAS).</p>
<p>An OpenAPI spec is a format for describing APIs. It's a blueprint that outlines everything about how an API works — what endpoints are available, what data you can send or receive, and what responses to expect.</p>
<p>This means that a well-written OAS file means well-written API reference documentation.</p>
<p>When writing this file, there are some parts that can get a bit complicated. For example, you might have to document a single endpoint with different methods or sometimes duplicate endpoints.</p>
<p>I encountered and documented both of those use cases. So, in this article, I'll show you how you can do the same. We'll go through each use case with sample OpenAPI specs, and at the end, I'll leave you with some useful tips for documenting your OpenAPI spec files.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-setting-the-foundation">Setting the Foundation</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-case-1-duplicate-endpoints">Use Case 1: Duplicate Endpoints</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-case-2-how-to-document-multiple-http-methods">Use Case 2: How to Document Multiple HTTP Methods</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-api-documentation-tips">API Documentation Tips</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-use-markdown-in-openapi">Use Markdown in OpenAPI</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-the-operationid-field">Use the operationId Field</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-ref-for-reusable-components">Use $ref for Reusable Components</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>There are a few things you need to know to follow along with the use cases below. These include:</p>
<ol>
<li><p><a target="_blank" href="https://www.freecodecamp.org/news/how-apis-work/"><strong>A basic knowledge of APIs and API documentation</strong></a>: Familiarity with API terminology and structure (for example, endpoints, methods, request/response structure) is essential to understand how an OpenAPI Specification (OAS) document functions.</p>
</li>
<li><p><a target="_blank" href="https://spec.openapis.org/oas/latest.html"><strong>Familiarity with OpenAPI Specification</strong></a>: Basic understanding of OAS, including its purpose, structure, and so on.</p>
</li>
<li><p><strong>Access to Swagger or other OpenAPI documentation tools</strong>: You need tools like the <a target="_blank" href="https://editor-next.swagger.io/">Swagger Editor</a> or <a target="_blank" href="https://rapidocweb.com/">RapiDoc</a>, which will allow you to test and view the OpenAPI files visually.</p>
</li>
</ol>
<h2 id="heading-setting-the-foundation"><strong>Setting the Foundation</strong></h2>
<p>In both programming and in life, if you can determine that something is "complex," this means that there's also a simplistic-regular way it can be as well. And that's the same for an OAS file.</p>
<p>When creating your spec file, you might not always encounter the use cases we'll address shortly. That's why it’s useful to know what a conventional spec file looks like.</p>
<p>An OpenAPI spec is a human and machine-readable file written in JSON or YAML. Below is an example of an OAS file structure in YAML format:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">paths:</span>
  <span class="hljs-string">/users:</span>
    <span class="hljs-attr">get:</span>
      <span class="hljs-attr">summary:</span> <span class="hljs-string">Get</span> <span class="hljs-string">a</span> <span class="hljs-string">list</span> <span class="hljs-string">of</span> <span class="hljs-string">users</span>
      <span class="hljs-attr">responses:</span>
        <span class="hljs-attr">'200':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">A</span> <span class="hljs-string">list</span> <span class="hljs-string">of</span> <span class="hljs-string">users.</span>
  <span class="hljs-string">/users/{userId}:</span>
    <span class="hljs-attr">get:</span>
      <span class="hljs-attr">summary:</span> <span class="hljs-string">Get</span> <span class="hljs-string">details</span> <span class="hljs-string">for</span> <span class="hljs-string">a</span> <span class="hljs-string">single</span> <span class="hljs-string">user</span>
      <span class="hljs-attr">parameters:</span>
 <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">userId</span>
          <span class="hljs-attr">in:</span> <span class="hljs-string">path</span>
          <span class="hljs-attr">required:</span> <span class="hljs-literal">true</span>
          <span class="hljs-attr">schema:</span>
            <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
      <span class="hljs-attr">responses:</span>
        <span class="hljs-attr">'200':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Details</span> <span class="hljs-string">of</span> <span class="hljs-string">a</span> <span class="hljs-string">single</span> <span class="hljs-string">user.</span>
  <span class="hljs-string">/orders:</span>
    <span class="hljs-attr">get:</span>
      <span class="hljs-attr">summary:</span> <span class="hljs-string">Get</span> <span class="hljs-string">a</span> <span class="hljs-string">list</span> <span class="hljs-string">of</span> <span class="hljs-string">orders</span>
      <span class="hljs-attr">responses:</span>
        <span class="hljs-attr">'200':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">A</span> <span class="hljs-string">list</span> <span class="hljs-string">of</span> <span class="hljs-string">orders.</span>
</code></pre>
<p>Here's a brief breakdown of this OAS file structure:</p>
<ol>
<li><p><strong>Paths</strong>: The <code>paths</code> section allows you to list each endpoint or URL that you can interact with in this API. Each path, such as <code>/users</code> or <code>/orders</code>, shows what type of data you can get or send to that URL.</p>
</li>
<li><p><strong>Operations</strong>: Under each path, there's a method or operation, like <code>GET</code>, which tells us what we can do with that endpoint. In this example, each path uses <code>GET</code>, which means you request information.</p>
</li>
<li><p><strong>Summary</strong>: Each operation has a <code>summary</code> — a short description explaining what the endpoint does, like "Get a list of users" or "Get details for a single user."</p>
</li>
<li><p><strong>Parameters</strong>: For paths that include placeholders, like <code>/users/{userId}</code>, you'll see a <code>parameters</code> section explaining what details are required.</p>
</li>
<li><p><strong>Responses</strong>: Each operation includes a <code>responses</code> section, which lists the possible API responses.</p>
</li>
</ol>
<p><strong>NOTE</strong>: This is not a complete OAS file. I omitted some preceding information for the purpose of this explanation.</p>
<p>Now, let’s dive into the more complex scenarios.</p>
<h2 id="heading-use-case-1-duplicate-endpoints"><strong>Use Case 1: Duplicate Endpoints</strong></h2>
<p>During the development of an API, you may create a single endpoint with multiple variations. Depending on the use case, you might want that endpoint to accept multiple data formats or specific parameters.</p>
<p>When you encounter such scenarios and want to document the API reference using an OpenAPI spec, you won't be able to replicate it exactly how it is in the <a target="_blank" href="https://postman.com/">Postman</a> collection (or any other development environment).</p>
<p>If you did try, you'll get this error:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730414590959/017c94c1-7e33-46c7-b572-b9ea03763e1f.png" alt="Failed OpenAPI Spec" width="2940" height="1490" loading="lazy"></p>
<p>The workaround for this problem is to consolidate these multiple variations under a single path definition by grouping the different requests and responses as <strong>examples</strong>.</p>
<p>In the example below, you have an API that has an endpoint for managing session registration at a conference. This endpoint has various requests and responses based on registration type (for example, Speaker, Attendee, or VIP).</p>
<p>In a Postman collection, you can have each of these endpoints in a separate folder for easy identification and testing.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730414608239/be94b45d-fb16-438f-9e7e-07cdc38c179d.png" alt="Postman collection" width="2938" height="792" loading="lazy"></p>
<p>But when documenting your spec file, it should look like this:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">openapi:</span> <span class="hljs-number">3.0</span><span class="hljs-number">.0</span>
<span class="hljs-attr">info:</span>
  <span class="hljs-attr">title:</span> <span class="hljs-string">Conference</span> <span class="hljs-string">Events</span> <span class="hljs-string">API</span>
  <span class="hljs-attr">description:</span> <span class="hljs-string">API</span> <span class="hljs-string">to</span> <span class="hljs-string">manage</span> <span class="hljs-string">event</span> <span class="hljs-string">registrations</span> <span class="hljs-string">for</span> <span class="hljs-string">conferences</span>
  <span class="hljs-attr">version:</span> <span class="hljs-number">1.0</span><span class="hljs-number">.0</span>
<span class="hljs-attr">paths:</span>
  <span class="hljs-string">/register-session:</span>
    <span class="hljs-attr">post:</span>
      <span class="hljs-attr">tags:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">Registration</span>
      <span class="hljs-attr">summary:</span> <span class="hljs-string">Register</span> <span class="hljs-string">for</span> <span class="hljs-string">a</span> <span class="hljs-string">conference</span> <span class="hljs-string">session</span>
      <span class="hljs-attr">description:</span> <span class="hljs-string">Register</span> <span class="hljs-string">a</span> <span class="hljs-string">user</span> <span class="hljs-string">for</span> <span class="hljs-string">a</span> <span class="hljs-string">specific</span> <span class="hljs-string">session</span> <span class="hljs-string">based</span> <span class="hljs-string">on</span> <span class="hljs-string">their</span> <span class="hljs-string">role,</span> <span class="hljs-string">with</span> <span class="hljs-string">details</span> <span class="hljs-string">provided</span> <span class="hljs-string">for</span> <span class="hljs-string">each</span> <span class="hljs-string">registration</span> <span class="hljs-string">type.</span>
      <span class="hljs-attr">operationId:</span> <span class="hljs-string">registerSession</span>
      <span class="hljs-attr">requestBody:</span>
        <span class="hljs-attr">description:</span> <span class="hljs-string">Registers</span> <span class="hljs-string">a</span> <span class="hljs-string">user</span> <span class="hljs-string">for</span> <span class="hljs-string">a</span> <span class="hljs-string">session</span> <span class="hljs-string">at</span> <span class="hljs-string">the</span> <span class="hljs-string">conference.</span> <span class="hljs-string">It</span> <span class="hljs-string">accepts</span> <span class="hljs-string">different</span> <span class="hljs-string">formats</span> <span class="hljs-string">for</span> <span class="hljs-string">attendance,</span> <span class="hljs-string">speaker,</span> <span class="hljs-string">or</span> <span class="hljs-string">VIP</span> <span class="hljs-string">registrations.</span>
        <span class="hljs-attr">required:</span> <span class="hljs-literal">true</span>
        <span class="hljs-attr">content:</span>
          <span class="hljs-attr">application/json:</span>
            <span class="hljs-attr">schema:</span>
              <span class="hljs-string">$ref:</span> <span class="hljs-string">'#/components/schemas/SessionRegistration'</span>
            <span class="hljs-attr">examples:</span>
              <span class="hljs-attr">Attendee:</span>
                <span class="hljs-attr">summary:</span> <span class="hljs-string">Register</span> <span class="hljs-string">as</span> <span class="hljs-string">an</span> <span class="hljs-string">Attendee</span>
                <span class="hljs-attr">value:</span>
                  <span class="hljs-attr">userType:</span> <span class="hljs-string">Attendee</span>
                  <span class="hljs-attr">userId:</span> <span class="hljs-number">789</span>
                  <span class="hljs-attr">sessionId:</span> <span class="hljs-number">1234</span>
                  <span class="hljs-attr">preferences:</span>
                    <span class="hljs-attr">seating:</span> <span class="hljs-string">General</span>
                    <span class="hljs-attr">accessLevel:</span> <span class="hljs-string">Basic</span>
              <span class="hljs-attr">Speaker:</span>
                <span class="hljs-attr">summary:</span> <span class="hljs-string">Register</span> <span class="hljs-string">as</span> <span class="hljs-string">a</span> <span class="hljs-string">Speaker</span>
                <span class="hljs-attr">value:</span>
                  <span class="hljs-attr">userType:</span> <span class="hljs-string">Speaker</span>
                  <span class="hljs-attr">userId:</span> <span class="hljs-number">456</span>
                  <span class="hljs-attr">sessionId:</span> <span class="hljs-number">1234</span>
                  <span class="hljs-attr">preferences:</span>
                    <span class="hljs-attr">seating:</span> <span class="hljs-string">VIP</span>
                    <span class="hljs-attr">accessLevel:</span> <span class="hljs-string">Full</span>
                    <span class="hljs-attr">presentationEquipment:</span> <span class="hljs-string">Projector</span>
              <span class="hljs-attr">VIP:</span>
                <span class="hljs-attr">summary:</span> <span class="hljs-string">Register</span> <span class="hljs-string">as</span> <span class="hljs-string">VIP</span>
                <span class="hljs-attr">value:</span>
                  <span class="hljs-attr">userType:</span> <span class="hljs-string">VIP</span>
                  <span class="hljs-attr">userId:</span> <span class="hljs-number">123</span>
                  <span class="hljs-attr">sessionId:</span> <span class="hljs-number">1234</span>
                  <span class="hljs-attr">preferences:</span>
                    <span class="hljs-attr">seating:</span> <span class="hljs-string">Front</span> <span class="hljs-string">Row</span>
                    <span class="hljs-attr">accessLevel:</span> <span class="hljs-string">Full</span>
                    <span class="hljs-attr">exclusiveAccess:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">responses:</span>
        <span class="hljs-attr">'200':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Successful</span> <span class="hljs-string">registration</span> <span class="hljs-string">for</span> <span class="hljs-string">Attendee,</span> <span class="hljs-string">Speaker,</span> <span class="hljs-string">or</span> <span class="hljs-string">VIP</span>
          <span class="hljs-attr">content:</span>
            <span class="hljs-attr">application/json:</span>
              <span class="hljs-attr">schema:</span>
                <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
                <span class="hljs-attr">properties:</span>
                  <span class="hljs-attr">registrationId:</span>
                    <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
                  <span class="hljs-attr">success:</span>
                    <span class="hljs-attr">type:</span> <span class="hljs-string">boolean</span>
              <span class="hljs-attr">examples:</span>
                <span class="hljs-attr">Attendee:</span>
                  <span class="hljs-attr">summary:</span> <span class="hljs-string">Response</span> <span class="hljs-string">for</span> <span class="hljs-string">Attendee</span> <span class="hljs-string">registration</span>
                  <span class="hljs-attr">value:</span>
                    <span class="hljs-attr">registrationId:</span> <span class="hljs-string">att-456def</span>
                    <span class="hljs-attr">success:</span> <span class="hljs-literal">true</span>
                <span class="hljs-attr">Speaker:</span>
                  <span class="hljs-attr">summary:</span> <span class="hljs-string">Response</span> <span class="hljs-string">for</span> <span class="hljs-string">Speaker</span> <span class="hljs-string">registration</span>
                  <span class="hljs-attr">value:</span>
                    <span class="hljs-attr">registrationId:</span> <span class="hljs-string">spk-123abc</span>
                    <span class="hljs-attr">success:</span> <span class="hljs-literal">true</span>
                <span class="hljs-attr">VIP:</span>
                  <span class="hljs-attr">summary:</span> <span class="hljs-string">Response</span> <span class="hljs-string">for</span> <span class="hljs-string">VIP</span> <span class="hljs-string">registration</span>
                  <span class="hljs-attr">value:</span>
                    <span class="hljs-attr">registrationId:</span> <span class="hljs-string">vip-789ghi</span>
                    <span class="hljs-attr">success:</span> <span class="hljs-literal">true</span>
<span class="hljs-attr">components:</span>
  <span class="hljs-attr">schemas:</span>
    <span class="hljs-attr">SessionRegistration:</span>
      <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
      <span class="hljs-attr">properties:</span>
        <span class="hljs-attr">userType:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Type</span> <span class="hljs-string">of</span> <span class="hljs-string">user</span> <span class="hljs-string">registering</span> <span class="hljs-string">(e.g.,</span> <span class="hljs-string">Attendee,</span> <span class="hljs-string">Speaker,</span> <span class="hljs-string">VIP)</span>
        <span class="hljs-attr">userId:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">integer</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Unique</span> <span class="hljs-string">ID</span> <span class="hljs-string">of</span> <span class="hljs-string">the</span> <span class="hljs-string">user</span>
        <span class="hljs-attr">sessionId:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">integer</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Unique</span> <span class="hljs-string">ID</span> <span class="hljs-string">of</span> <span class="hljs-string">the</span> <span class="hljs-string">session</span> <span class="hljs-string">to</span> <span class="hljs-string">register</span> <span class="hljs-string">for</span>
        <span class="hljs-attr">preferences:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
          <span class="hljs-attr">properties:</span>
            <span class="hljs-attr">seating:</span>
              <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
              <span class="hljs-attr">description:</span> <span class="hljs-string">Seating</span> <span class="hljs-string">preference</span> <span class="hljs-string">(e.g.,</span> <span class="hljs-string">General,</span> <span class="hljs-string">VIP,</span> <span class="hljs-string">Front</span> <span class="hljs-string">Row)</span>
            <span class="hljs-attr">accessLevel:</span>
              <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
              <span class="hljs-attr">description:</span> <span class="hljs-string">Access</span> <span class="hljs-string">level</span> <span class="hljs-string">granted</span> <span class="hljs-string">(e.g.,</span> <span class="hljs-string">Basic,</span> <span class="hljs-string">Full)</span>
            <span class="hljs-attr">presentationEquipment:</span>
              <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
              <span class="hljs-attr">description:</span> <span class="hljs-string">Required</span> <span class="hljs-string">equipment</span> <span class="hljs-string">for</span> <span class="hljs-string">Speakers</span> <span class="hljs-string">(only</span> <span class="hljs-string">applicable</span> <span class="hljs-string">to</span> <span class="hljs-string">Speaker)</span>
            <span class="hljs-attr">exclusiveAccess:</span>
              <span class="hljs-attr">type:</span> <span class="hljs-string">boolean</span>
              <span class="hljs-attr">description:</span> <span class="hljs-string">Exclusive</span> <span class="hljs-string">access</span> <span class="hljs-string">for</span> <span class="hljs-string">VIP</span> <span class="hljs-string">users</span>
</code></pre>
<p>In this file, you have a single <code>POST /register-session</code> path that captures all registration types without duplicating endpoints.</p>
<p>Here's a visual representation of this OAS file using the <a target="_blank" href="https://editor-next.swagger.io/">Swagger editor</a>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730414627353/eed5843b-f5f4-4e61-9126-51fd10e417c6.png" alt="OpenAPI Spec Multiple Request Examples" width="2940" height="1598" loading="lazy"></p>
<h2 id="heading-use-case-2-how-to-document-multiple-http-methods"><strong>Use Case 2: How to Document Multiple HTTP Methods</strong></h2>
<p>Another use case you may encounter happens when you have the same endpoint but different HTTP methods.</p>
<p>This usually comes up because each method serves a different purpose, even though they share the same path.</p>
<p>For example, a <strong>GET</strong> method retrieves information about a resource, like viewing a user's registration details for a conference session.</p>
<p>On the other hand, a <strong>PATCH</strong> method updates specific fields for that same user registration, like updating their seat preference.</p>
<p>Since both the <code>GET</code> and <code>PATCH</code> methods relate to the same resource (<code>/register-session</code> in our example), the way around this is to group them under the same path. This way, you'll be documenting two separate methods for a single path.</p>
<p>In OpenAPI, each combination of a path and method is called an "operation". Grouping operations that share the same path helps maintain clearer and more structured documents.</p>
<p>Using the conference events API example, this is what your OAS file should look like:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">openapi:</span> <span class="hljs-number">3.0</span><span class="hljs-number">.0</span>
<span class="hljs-attr">info:</span>
  <span class="hljs-attr">title:</span> <span class="hljs-string">Conference</span> <span class="hljs-string">Events</span> <span class="hljs-string">API</span>
  <span class="hljs-attr">description:</span> <span class="hljs-string">API</span> <span class="hljs-string">to</span> <span class="hljs-string">manage</span> <span class="hljs-string">event</span> <span class="hljs-string">registrations</span> <span class="hljs-string">for</span> <span class="hljs-string">conferences</span>
  <span class="hljs-attr">version:</span> <span class="hljs-number">1.0</span><span class="hljs-number">.0</span>
<span class="hljs-attr">paths:</span>
  <span class="hljs-string">/register-session:</span>
    <span class="hljs-attr">get:</span>
      <span class="hljs-attr">tags:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">Registration</span>
      <span class="hljs-attr">summary:</span> <span class="hljs-string">Retrieve</span> <span class="hljs-string">a</span> <span class="hljs-string">registration</span> <span class="hljs-string">for</span> <span class="hljs-string">a</span> <span class="hljs-string">conference</span> <span class="hljs-string">session</span>
      <span class="hljs-attr">description:</span> <span class="hljs-string">Fetch</span> <span class="hljs-string">details</span> <span class="hljs-string">for</span> <span class="hljs-string">a</span> <span class="hljs-string">specific</span> <span class="hljs-string">user's</span> <span class="hljs-string">registration,</span> <span class="hljs-string">like</span> <span class="hljs-string">seat</span> <span class="hljs-string">assignment</span> <span class="hljs-string">and</span> <span class="hljs-string">access</span> <span class="hljs-string">level.</span>
      <span class="hljs-attr">operationId:</span> <span class="hljs-string">getSessionRegistration</span>
      <span class="hljs-attr">parameters:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">in:</span> <span class="hljs-string">query</span>
          <span class="hljs-attr">name:</span> <span class="hljs-string">userId</span>
          <span class="hljs-attr">schema:</span>
            <span class="hljs-attr">type:</span> <span class="hljs-string">integer</span>
          <span class="hljs-attr">required:</span> <span class="hljs-literal">true</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">The</span> <span class="hljs-string">ID</span> <span class="hljs-string">of</span> <span class="hljs-string">the</span> <span class="hljs-string">user</span> <span class="hljs-string">whose</span> <span class="hljs-string">registration</span> <span class="hljs-string">you</span> <span class="hljs-string">want</span> <span class="hljs-string">to</span> <span class="hljs-string">retrieve.</span>
      <span class="hljs-attr">responses:</span>
        <span class="hljs-attr">'200':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Registration</span> <span class="hljs-string">details</span> <span class="hljs-string">retrieved</span> <span class="hljs-string">successfully</span>
          <span class="hljs-attr">content:</span>
            <span class="hljs-attr">application/json:</span>
              <span class="hljs-attr">schema:</span>
                <span class="hljs-string">$ref:</span> <span class="hljs-string">'#/components/schemas/SessionRegistration'</span>
              <span class="hljs-attr">example:</span>
                <span class="hljs-attr">userId:</span> <span class="hljs-number">789</span>
                <span class="hljs-attr">sessionId:</span> <span class="hljs-number">1234</span>
                <span class="hljs-attr">preferences:</span>
                  <span class="hljs-attr">seating:</span> <span class="hljs-string">General</span>
                  <span class="hljs-attr">accessLevel:</span> <span class="hljs-string">Basic</span>
                  <span class="hljs-attr">exclusiveAccess:</span> <span class="hljs-literal">false</span>

    <span class="hljs-attr">patch:</span>
      <span class="hljs-attr">tags:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">Registration</span>
      <span class="hljs-attr">summary:</span> <span class="hljs-string">Update</span> <span class="hljs-string">a</span> <span class="hljs-string">registration</span> <span class="hljs-string">for</span> <span class="hljs-string">a</span> <span class="hljs-string">conference</span> <span class="hljs-string">session</span>
      <span class="hljs-attr">description:</span> <span class="hljs-string">Update</span> <span class="hljs-string">specific</span> <span class="hljs-string">fields</span> <span class="hljs-string">for</span> <span class="hljs-string">a</span> <span class="hljs-string">user's</span> <span class="hljs-string">session</span> <span class="hljs-string">registration,</span> <span class="hljs-string">such</span> <span class="hljs-string">as</span> <span class="hljs-string">seating</span> <span class="hljs-string">or</span> <span class="hljs-string">access</span> <span class="hljs-string">level.</span>
      <span class="hljs-attr">operationId:</span> <span class="hljs-string">updateSessionRegistration</span>
      <span class="hljs-attr">requestBody:</span>
        <span class="hljs-attr">description:</span> <span class="hljs-string">Allows</span> <span class="hljs-string">updating</span> <span class="hljs-string">fields</span> <span class="hljs-string">for</span> <span class="hljs-string">a</span> <span class="hljs-string">specific</span> <span class="hljs-string">session</span> <span class="hljs-string">registration.</span>
        <span class="hljs-attr">required:</span> <span class="hljs-literal">true</span>
        <span class="hljs-attr">content:</span>
          <span class="hljs-attr">application/json:</span>
            <span class="hljs-attr">schema:</span>
              <span class="hljs-string">$ref:</span> <span class="hljs-string">'#/components/schemas/UpdateSessionPreferences'</span>
            <span class="hljs-attr">example:</span>
              <span class="hljs-attr">userId:</span> <span class="hljs-number">789</span>
              <span class="hljs-attr">preferences:</span>
                <span class="hljs-attr">seating:</span> <span class="hljs-string">VIP</span>
                <span class="hljs-attr">accessLevel:</span> <span class="hljs-string">Full</span>
                <span class="hljs-attr">exclusiveAccess:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">responses:</span>
        <span class="hljs-attr">'200':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Registration</span> <span class="hljs-string">updated</span> <span class="hljs-string">successfully</span>
          <span class="hljs-attr">content:</span>
            <span class="hljs-attr">application/json:</span>
              <span class="hljs-attr">schema:</span>
                <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
                <span class="hljs-attr">properties:</span>
                  <span class="hljs-attr">success:</span>
                    <span class="hljs-attr">type:</span> <span class="hljs-string">boolean</span>
                  <span class="hljs-attr">message:</span>
                    <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
              <span class="hljs-attr">example:</span>
                <span class="hljs-attr">success:</span> <span class="hljs-literal">true</span>
                <span class="hljs-attr">message:</span> <span class="hljs-string">"Registration updated successfully."</span>
<span class="hljs-attr">components:</span>
  <span class="hljs-attr">schemas:</span>
    <span class="hljs-attr">SessionRegistration:</span>
      <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
      <span class="hljs-attr">properties:</span>
        <span class="hljs-attr">userId:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">integer</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Unique</span> <span class="hljs-string">ID</span> <span class="hljs-string">of</span> <span class="hljs-string">the</span> <span class="hljs-string">user</span>
        <span class="hljs-attr">sessionId:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">integer</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Unique</span> <span class="hljs-string">ID</span> <span class="hljs-string">of</span> <span class="hljs-string">the</span> <span class="hljs-string">session</span>
        <span class="hljs-attr">preferences:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
          <span class="hljs-attr">properties:</span>
            <span class="hljs-attr">seating:</span>
              <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
              <span class="hljs-attr">description:</span> <span class="hljs-string">Seating</span> <span class="hljs-string">preference</span> <span class="hljs-string">(e.g.,</span> <span class="hljs-string">General,</span> <span class="hljs-string">VIP,</span> <span class="hljs-string">Front</span> <span class="hljs-string">Row)</span>
            <span class="hljs-attr">accessLevel:</span>
              <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
              <span class="hljs-attr">description:</span> <span class="hljs-string">Access</span> <span class="hljs-string">level</span> <span class="hljs-string">granted</span> <span class="hljs-string">(e.g.,</span> <span class="hljs-string">Basic,</span> <span class="hljs-string">Full)</span>
            <span class="hljs-attr">exclusiveAccess:</span>
              <span class="hljs-attr">type:</span> <span class="hljs-string">boolean</span>
              <span class="hljs-attr">description:</span> <span class="hljs-string">Exclusive</span> <span class="hljs-string">access</span> <span class="hljs-string">granted</span> <span class="hljs-string">to</span> <span class="hljs-string">certain</span> <span class="hljs-string">users</span>

    <span class="hljs-attr">UpdateSessionPreferences:</span>
      <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
      <span class="hljs-attr">properties:</span>
        <span class="hljs-attr">userId:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">integer</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Unique</span> <span class="hljs-string">ID</span> <span class="hljs-string">of</span> <span class="hljs-string">the</span> <span class="hljs-string">user</span>
        <span class="hljs-attr">preferences:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
          <span class="hljs-attr">properties:</span>
            <span class="hljs-attr">seating:</span>
              <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
              <span class="hljs-attr">description:</span> <span class="hljs-string">New</span> <span class="hljs-string">seating</span> <span class="hljs-string">preference</span> <span class="hljs-string">(e.g.,</span> <span class="hljs-string">VIP)</span>
            <span class="hljs-attr">accessLevel:</span>
              <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
              <span class="hljs-attr">description:</span> <span class="hljs-string">Updated</span> <span class="hljs-string">access</span> <span class="hljs-string">level</span> <span class="hljs-string">(e.g.,</span> <span class="hljs-string">Full)</span>
            <span class="hljs-attr">exclusiveAccess:</span>
              <span class="hljs-attr">type:</span> <span class="hljs-string">boolean</span>
              <span class="hljs-attr">description:</span> <span class="hljs-string">Updated</span> <span class="hljs-string">access</span> <span class="hljs-string">preference</span> <span class="hljs-string">(e.g.,</span> <span class="hljs-literal">true</span> <span class="hljs-string">or</span> <span class="hljs-literal">false</span><span class="hljs-string">)</span>
</code></pre>
<p>In this file, you have a single <code>/register-session</code> path with multiple methods. We define just one path, <code>/register-session</code>, and list the <code>GET</code> and <code>PATCH</code> methods under it. This keeps the spec clean, reduces duplication, and shows that these methods relate to the same resource.</p>
<p>Here's a visual representation of this OAS file using the Swagger editor:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730414663261/680dd222-8d36-41be-a73a-10a55771d906.png" alt="Multiple methods in one operation in an OpenAPI spec" width="2940" height="1596" loading="lazy"></p>
<h2 id="heading-api-documentation-tips"><strong>API Documentation Tips</strong></h2>
<p>While working with OpenAPI specifications, there are some useful tricks and tips that can make your OAS file more readable and maintainable.</p>
<h3 id="heading-use-markdown-in-openapi"><strong>Use Markdown in OpenAPI</strong></h3>
<p>OpenAPI specifications allow the use of Markdown in the <code>description</code> field. The version of Markdown used in OAS is called CommonMark, the same version used in GitHub.</p>
<p>Markdown formatting allows you to make text more visually engaging and organized. You can add formatting such as headers, lists, code blocks, bold, italics, and so on, which can make your documentation easier to scan and more accessible for readers.</p>
<p>For example, if you need to emphasize certain parts of an endpoint's purpose or highlight important details, Markdown lets you do it naturally.</p>
<p>You can add Markdown directly into any <code>description</code> field in the OpenAPI file, like this:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">paths:</span>
  <span class="hljs-string">/register-session:</span>
    <span class="hljs-attr">get:</span>
      <span class="hljs-attr">description:</span> <span class="hljs-string">|
        ## Retrieve Session Registration
</span>       <span class="hljs-string">Retrieves</span> <span class="hljs-string">the</span> <span class="hljs-string">registration</span> <span class="hljs-string">details</span> <span class="hljs-string">for</span> <span class="hljs-string">a</span> <span class="hljs-string">specific</span> <span class="hljs-string">user.</span>  
       <span class="hljs-bullet">-</span> <span class="hljs-string">**Note:**</span> <span class="hljs-string">This</span> <span class="hljs-string">data</span> <span class="hljs-string">includes</span> <span class="hljs-string">seat</span> <span class="hljs-string">assignment</span> <span class="hljs-string">and</span> <span class="hljs-string">access</span> <span class="hljs-string">level.</span>  
       <span class="hljs-bullet">-</span> <span class="hljs-attr">Example of JSON response:</span> <span class="hljs-string">`{"userId":</span> <span class="hljs-number">789</span><span class="hljs-string">,</span> <span class="hljs-attr">"sessionId":</span> <span class="hljs-number">1234</span><span class="hljs-string">,</span> <span class="hljs-attr">"seating":</span> <span class="hljs-string">"General"</span><span class="hljs-string">}`</span>
      <span class="hljs-attr">responses:</span>
        <span class="hljs-attr">'200':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Registration</span> <span class="hljs-string">details</span> <span class="hljs-string">retrieved</span> <span class="hljs-string">successfully</span>
</code></pre>
<p>When deployed to supported documentation platforms like <a target="_blank" href="https://rapidocweb.com/">RapiDoc</a> or <a target="_blank" href="https://readme.com/">ReadMe</a>, this will render beautifully with all your Markdown styling intact.</p>
<p>Here's a deployed version of this example in Readme:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730414669217/100bcde8-a25e-4eab-9102-5113476a334b.png" alt="Markdown in description field on Readme" width="1358" height="906" loading="lazy"></p>
<h3 id="heading-use-the-operationid-field"><strong>Use the</strong> <code>operationId</code> Field</h3>
<p>The <code>operationId</code> field is an optional field in OpenAPI specs that assigns a unique name to each API operation.</p>
<p>It is an identifier that you can use to call specific methods when integrating with SDKs or when linking between parts of your documentation.</p>
<p>By effectively using the <code>operationId</code>, you make it much easier for developers to reference specific actions in the API, which is especially helpful when the API is complex or has multiple endpoints.</p>
<p>Place the <code>operationId</code> inside each HTTP method block to give it a unique identifier. For instance:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">paths:</span>
  <span class="hljs-string">/register-session:</span>
    <span class="hljs-attr">get:</span>
      <span class="hljs-attr">operationId:</span> <span class="hljs-string">getSessionRegistration</span>
      <span class="hljs-attr">description:</span> <span class="hljs-string">Retrieve</span> <span class="hljs-string">a</span> <span class="hljs-string">registration</span> <span class="hljs-string">for</span> <span class="hljs-string">a</span> <span class="hljs-string">conference</span> <span class="hljs-string">session</span>
      <span class="hljs-attr">responses:</span>
        <span class="hljs-attr">'200':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Successfully</span> <span class="hljs-string">retrieved</span> <span class="hljs-string">the</span> <span class="hljs-string">registration</span>
    <span class="hljs-attr">patch:</span>
      <span class="hljs-attr">operationId:</span> <span class="hljs-string">updateSessionRegistration</span>
      <span class="hljs-attr">description:</span> <span class="hljs-string">Update</span> <span class="hljs-string">a</span> <span class="hljs-string">registration</span> <span class="hljs-string">for</span> <span class="hljs-string">a</span> <span class="hljs-string">conference</span> <span class="hljs-string">session</span>
      <span class="hljs-attr">responses:</span>
        <span class="hljs-attr">'200':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Registration</span> <span class="hljs-string">updated</span> <span class="hljs-string">successfully</span>
</code></pre>
<p>With the <code>operationId</code>, developers can directly refer to <code>getSessionRegistration</code> or <code>updateSessionRegistration</code> as function calls in code or API clients.</p>
<h3 id="heading-use-ref-for-reusable-components"><strong>Use</strong> <code>$ref</code> for Reusable Components</h3>
<p>The <code>$ref</code> keyword in OpenAPI lets you create and reuse components across your spec file. This technique is especially helpful when you have similar request bodies, responses, or parameters repeated in multiple endpoints.</p>
<p>By defining components in a single place and referencing them as needed, you avoid redundancy, reduce errors, and facilitate updates.</p>
<p>So, instead of updating the same parameter across multiple locations, you update it once in the reusable component, and every endpoint using it gets the update.</p>
<p>To use it, first define the reusable component in the components section of your OpenAPI file, then reference it elsewhere using <code>$ref</code>:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">components:</span>
  <span class="hljs-attr">schemas:</span>
    <span class="hljs-attr">RegistrationDetails:</span>
      <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
      <span class="hljs-attr">properties:</span>
        <span class="hljs-attr">userId:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">integer</span>
        <span class="hljs-attr">sessionId:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">integer</span>
        <span class="hljs-attr">seating:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
<span class="hljs-attr">paths:</span>
  <span class="hljs-string">/register-session:</span>
    <span class="hljs-attr">post:</span>
      <span class="hljs-attr">summary:</span> <span class="hljs-string">Register</span> <span class="hljs-string">for</span> <span class="hljs-string">a</span> <span class="hljs-string">session</span>
      <span class="hljs-attr">requestBody:</span>
        <span class="hljs-attr">required:</span> <span class="hljs-literal">true</span>
        <span class="hljs-attr">content:</span>
          <span class="hljs-attr">application/json:</span>
            <span class="hljs-attr">schema:</span>
              <span class="hljs-string">$ref:</span> <span class="hljs-string">'#/components/schemas/RegistrationDetails'</span>
      <span class="hljs-attr">responses:</span>
        <span class="hljs-attr">'201':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Successfully</span> <span class="hljs-string">registered</span> <span class="hljs-string">for</span> <span class="hljs-string">the</span> <span class="hljs-string">session</span>
</code></pre>
<p>In this file, <code>RegistrationDetails</code> is defined once and is referenced using the <code>$ref</code> keyword in the <code>/register-session</code> operation.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>In this article, you learned how to resolve some complex use cases you might encounter when documenting your API reference using an OpenAPI specification. We went through what to do when you have a single endpoint with multiple methods or duplicate endpoints.</p>
<p>Creating your API reference without an OpenAPI spec file is a manual approach that can become redundant if you have to replicate it across various platforms. But by relying on the tips from the article, you're sure to create better, more efficient, and more reusable OpenAPI specifications. And these, in turn, will lead to better API documentation.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
