<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nolan Di Mare Sullivan</title>
    <description>The latest articles on DEV Community by Nolan Di Mare Sullivan (@ndimares).</description>
    <link>https://dev.to/ndimares</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F880308%2Fca4300c5-64fc-42ae-8c75-6b740aa638d1.jpg</url>
      <title>DEV Community: Nolan Di Mare Sullivan</title>
      <link>https://dev.to/ndimares</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ndimares"/>
    <language>en</language>
    <item>
      <title>Everything You Need to Know About OpenAPI</title>
      <dc:creator>Nolan Di Mare Sullivan</dc:creator>
      <pubDate>Tue, 23 Apr 2024 09:39:22 +0000</pubDate>
      <link>https://dev.to/speakeasy/everything-you-need-to-know-about-openapi-17mh</link>
      <guid>https://dev.to/speakeasy/everything-you-need-to-know-about-openapi-17mh</guid>
      <description>&lt;p&gt;&lt;a href="**https://www.speakeasyapi.dev/openapi**"&gt;Speakeasy has released a comprehensive, Open Source reference to writing OpenAPI specifications&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The reference has AI-search built in to help you quickly answer any questions that you may have.&lt;/p&gt;

&lt;p&gt;If you are interested in contributing, &lt;a href="https://github.com/speakeasy-api/openapi-reference-documentation"&gt;the github repo can be found here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is OpenAPI Important?
&lt;/h2&gt;

&lt;p&gt;Because API design is important. An API that developers enjoy interacting with turns a SaaS business into a platform. However great design is only useful if it's well-documented and consistently represented across every API surface area (docs, SDKs, etc.).&lt;/p&gt;

&lt;p&gt;That is where OpenAPI comes in. Trying to manually create &amp;amp; maintain all your surfaces will inevitably lead to frustration and inconsistencies. Instead, if you are building a RESTful API, OpenAPI will be (should be) the source of truth that automates the creation of all your public surfaces (docs, SDKs, etc.).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxpq5c4ylckm40252u96r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxpq5c4ylckm40252u96r.png" alt="Depiction of OpenAPI-based workflow" width="800" height="707"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This documentation will help you understand the OpenAPI Specification.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is OpenAPI?
&lt;/h2&gt;

&lt;p&gt;When we refer to OpenAPI, we mean the OpenAPI Specification - a standardized document structure for describing HTTP APIs in a way that humans and computers can understand.&lt;/p&gt;

&lt;p&gt;OpenAPI files are written as JSON or YAML, describing your API using a standard vocabulary defined by the Specification - we'll call this JSON or YAML file an OpenAPI document.&lt;/p&gt;

&lt;p&gt;A valid OpenAPI document describes your RESTful API and serves as the instruction set for tooling that generates API documentation, SDKs, and more. We will refer to an app or tool that reads an OpenAPI document to perform an action as an OpenAPI tool. Speakeasy is one such tool, a full list can be found here.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenAPI Document Basics
&lt;/h2&gt;

&lt;p&gt;Your OpenAPI document is composed of keywords (some required, some optional). Together, the document covers the key elements of your API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What security is required to access it?&lt;/li&gt;
&lt;li&gt;Which endpoints expose which resources?&lt;/li&gt;
&lt;li&gt;How are those resources constructed?
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;openapi&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3.1.0&lt;/span&gt;
&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;The Speakeasy Bar&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.0.0&lt;/span&gt;
&lt;span class="na"&gt;servers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://speakeasy.bar&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;The production server&lt;/span&gt;
&lt;span class="na"&gt;security&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;
&lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;drinks&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Operations related to drinks&lt;/span&gt;
&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;/drinks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;drinks&lt;/span&gt;
      &lt;span class="na"&gt;operationId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;listDrinks&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get a list of drinks&lt;/span&gt;
      &lt;span class="na"&gt;responses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;200"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;A list of drinks&lt;/span&gt;
          &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;application/json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;array&lt;/span&gt;
                &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#/components/schemas/Drink"&lt;/span&gt;
&lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schemas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Drink&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
      &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Drink&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
        &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;number&lt;/span&gt;
  &lt;span class="na"&gt;securitySchemes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apiKey&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Authorization&lt;/span&gt;
      &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;header&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To break that down piece by piece:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;openapi&lt;/code&gt;: The version of the OpenAPI Specification that the document conforms to, should be one of the supported versions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;openapi&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3.1.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: Speakeasy tooling currently only supports OpenAPI Specification versions 3.0.x and 3.1.x.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;info&lt;/code&gt;: Contains information about the document including fields like &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;version&lt;/code&gt;, and &lt;code&gt;description&lt;/code&gt; that help to identify the purpose and owner of the document.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;The Speakeasy Bar&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.0.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;servers&lt;/code&gt;: Contains an optional list of servers the API is available on. If not provided, the default URL is assumed to be &lt;code&gt;/&lt;/code&gt;, a path relative to where the OpenAPI document is hosted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;servers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://speakeasy.bar&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;The production server&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;security&lt;/code&gt;: Contains an optional list of security requirements that apply to all operations in the API. If not provided, the default security requirements are assumed to be &lt;code&gt;[]&lt;/code&gt;, an empty array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;security&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;tags&lt;/code&gt;: Contains an optional list of tags that are generally used to group or categorize a set of Operations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;drinks&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Operations related to drinks&lt;/span&gt;
&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;/drinks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;drinks&lt;/span&gt;
      &lt;span class="na"&gt;operationId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;listDrinks&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get a list of drinks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;paths&lt;/code&gt;: Contains the paths and operations available within the API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;/drinks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;drinks&lt;/span&gt;
      &lt;span class="na"&gt;operationId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;listDrinks&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get a list of drinks&lt;/span&gt;
      &lt;span class="na"&gt;responses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;200"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;A list of drinks&lt;/span&gt;
          &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;application/json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;array&lt;/span&gt;
                &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
                    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#/components/schemas/Drink"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;components&lt;/code&gt;: Contains an optional list of reusable schemas that can be referenced from other parts of the document. This improves the readability and maintainability of the document by allowing common schemas to be defined once and reused in multiple places.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schemas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Drink&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
      &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Drink&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
        &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;number&lt;/span&gt;
  &lt;span class="na"&gt;securitySchemes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apiKey&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Authorization&lt;/span&gt;
      &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;header&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Format and File Structure
&lt;/h2&gt;

&lt;p&gt;An OpenAPI document is a JSON or YAML file that contains either an entire API definition or a partial definition of an API and/or its components. All field names in the specification are case-sensitive unless otherwise specified.&lt;/p&gt;

&lt;p&gt;A document can be split into multiple files, and the files can be in different formats. For example, you can have a JSON file that contains the API definition and a YAML file that contains the components, or a collection of files that contain partial definitions of the API and its components.&lt;/p&gt;

&lt;p&gt;Generally, the main API definition file is called &lt;code&gt;openapi.json&lt;/code&gt; or &lt;code&gt;openapi.yaml&lt;/code&gt;, and the component files are called &lt;code&gt;components.json&lt;/code&gt; or &lt;code&gt;components.yaml,&lt;/code&gt; though this is not a requirement.&lt;/p&gt;

&lt;p&gt;Some common organizational patterns for OpenAPI documents are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A single file that contains the entire API definition.&lt;/li&gt;
&lt;li&gt;A main file that contains the API definition and a components file that contains the components.

&lt;ul&gt;
&lt;li&gt;This is normally achieved by using the $ref keyword to reference the components file from the main file. Click here for more information on references.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;A collection of files that contain partial definitions of the API and its components.

&lt;ul&gt;
&lt;li&gt;Some tools support this pattern by allowing multiple files to be provided. Others, such as the Speakeasy Generator, require the individual files to be merged into a single file before being passed to the tool, which can be achieved using the Speakeasy CLI tool. Click here for more information on the Speakeasy CLI merge tool.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How is this different to the official OpenAPI documentation?
&lt;/h2&gt;

&lt;p&gt;The goal of this documentation is to provide a practioner's guide for developers interested in understanding the impact of OpenAPI design on their downstream API surfaces. This guide prioritizes approachability and practicality over technical completeness.&lt;/p&gt;

&lt;p&gt;We've structured the documentation according to the needs of OpenAPI users of any skill level.&lt;/p&gt;

&lt;p&gt;Which versions of the OpenAPI Specification does this documentation cover?&lt;/p&gt;

&lt;p&gt;There are several versions of the OpenAPI specification in circulation: 2.0 (also known as Swagger), 3.0, and 3.1. We recommend developers use OpenAPI version 3.1 for all projects. The advantage of using OpenAPI version 3.1 is that it is fully compatible with JSON Schema, which gives you access to a much larger ecosystem of tools and libraries.&lt;/p&gt;

&lt;p&gt;Speakeasy's documentation covers versions &lt;code&gt;3.0.x&lt;/code&gt; and &lt;code&gt;3.1.x&lt;/code&gt; of the OpenAPI specification. Where there is an important difference between the two versions, we call it out specifically, otherwise the documentation will apply to both versions.&lt;/p&gt;

</description>
      <category>openapi</category>
      <category>api</category>
      <category>rest</category>
    </item>
    <item>
      <title>SDK Best Practices</title>
      <dc:creator>Nolan Di Mare Sullivan</dc:creator>
      <pubDate>Sun, 16 Jul 2023 19:54:54 +0000</pubDate>
      <link>https://dev.to/speakeasy/sdk-best-practices-4nb5</link>
      <guid>https://dev.to/speakeasy/sdk-best-practices-4nb5</guid>
      <description>&lt;h2&gt;
  
  
  From API-First to SDK-First
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Do the best APIs have SDKs?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;One of the most significant trends in tech over the past ten years has been the proliferation and success of API-as-a-product companies (e.g. Twilio, Algolia, etc).&lt;/p&gt;

&lt;p&gt;But the term API-as-a-product obscures one of the most critical ingredients in these companies’ success. The secret that the most successful API companies have been hiding in plain sight, is that their APIs are not the actual interface beloved by users. It is their SDKs that are at the heart of the best-in-class developer experience these companies offer.  If you want to delight developers, don’t try to just be API-first, focus on becoming SDK-first.&lt;/p&gt;

&lt;p&gt;So why doesn’t every API company offer SDKs to their users? Up until now, it’s been really hard to sustainably build great SDKs. If you look at a list of &lt;a href="https://www.postman.com/explore/most-popular-apis-this-year"&gt;the most popular APIs&lt;/a&gt;, you’ll find that even some of the biggest API companies have failed to build out robust SDK programs. Many offer patchy or incomplete support.&lt;/p&gt;

&lt;p&gt;Change is coming though. Speakeasy is committed to giving every company access to an API experience platform on par with what the best API companies provide. A major component of that platform is a workflow to easily and sustainably build SDKs for all your REST APIs.&lt;/p&gt;

&lt;p&gt;In this piece, we’ll discuss why SDKs are important, what qualities make a great SDK, and how to overcome the common problems that typically plague SDK development and maintenance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Are SDKs Important?
&lt;/h2&gt;

&lt;p&gt;SDKs provide numerous unique benefits to your end-users’ developer experience, and to your team too:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reduce your team’s support burden&lt;/strong&gt;: By enabling type definitions in the IDE, SDKs reduce the likelihood of user error during user integration. That in turn means fewer support tickets for your team to manage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improve product discoverability&lt;/strong&gt;: SDKs put your product in the place where developers are most likely to look for solutions. Client libraries on Github and in popular package managers will help your product develop social proof as users encounter you in familiar environments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Increase revenue with a larger addressable market&lt;/strong&gt;: Every SDK you offer makes it easier to appeal to a new community of developers. That means an increase in users, and ultimately, more revenue.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course, releasing a badly-written SDK could do more harm than good. Let’s dive into what it means to build a great SDK.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8gEXFTMD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tcngfc8zcgwoemzurvnl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8gEXFTMD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tcngfc8zcgwoemzurvnl.png" alt="SDK meme" width="800" height="253"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices for a Great SDK
&lt;/h2&gt;

&lt;p&gt;We’ve talked to hundreds of developers on this topic. While there is always a bit of personal preference, these are the things that every developer wants to see in an SDK:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Type Safe&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Type safety might be the most important aspect of SDK creation. By making your API’s inputs and outputs explicit, the developers integrating the API into their application can better understand how the API is intended to be used — and massively reduce the number of incorrect requests being made to your API. Type safety will help developers debug in their IDE as they write the application code, and spare them the frustration of having to comb through the constructed data object to see where/ mistakes occurred.&lt;/p&gt;

&lt;p&gt;The more that you can include in your type definition, the more feedback developers will have when they are building with your SDK. Below you can see the benefit of working with a strongly-typed SDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Pet class generated by Speakeasy&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Pet&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;SpeakeasyBase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;SpeakeasyMetadata&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form, name=category;json=true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Expose&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;category&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Category&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Category&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;SpeakeasyMetadata&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form, name=id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Expose&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;SpeakeasyMetadata&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form, name=name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Expose&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;SpeakeasyMetadata&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form, name=photoUrls&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Expose&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;photoUrls&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;photoUrls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * pet status in the store
     */&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;SpeakeasyMetadata&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form, name=status&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Expose&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;PetStatus&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;SpeakeasyMetadata&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form, name=tags;json=true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;elemType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Tag&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Expose&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tags&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Note the &lt;code&gt;@SpeakeasyMetadata&lt;/code&gt; decorators that the Speakeasy SDK uses to serialize and deserialize data, depending on the use case. The example above includes instructions on how to serialize a &lt;code&gt;Pet&lt;/code&gt; object and its related objects, &lt;code&gt;Tag&lt;/code&gt; and &lt;code&gt;Category&lt;/code&gt;. The &lt;code&gt;@Expose&lt;/code&gt; and &lt;code&gt;@Type&lt;/code&gt; decorators expose types at runtime.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Abstracted
&lt;/h3&gt;

&lt;p&gt;SDKs spare your users from having to worry about the minutiae of how your API works. You can abstract away details like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Networking code&lt;/strong&gt;: SDKs can handle the details of making network requests to an API. This allows developers to focus on the functionality they want to implement, rather than the details of how to communicate with the API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request and response formatting&lt;/strong&gt;: SDKs can handle the details of formatting requests and parsing responses in a format that is specific to the API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error handling&lt;/strong&gt;: SDKs can interpret the details of errors that occur when using an API, allowing developers to focus on their application logic rather than error handling.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;Human Readable&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This one is pretty self-explanatory. AI hasn’t made developers obsolete yet, so there is going to be another person on the other side of your SDK code. When code is well-organized, well-commented, and easy to read, developers are more likely to be able to understand how the SDK works and how to use it correctly.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. &lt;strong&gt;Limited Dependencies&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Codebases are jungles, and SDKs are another component of this complex ecosystem. When an SDK has a large number of dependencies, it can be more difficult to use the SDK with other libraries and frameworks. If a developer has to try and resolve incompatible dependencies before they can use your SDK, there is a high risk they will abandon the API integration altogether.&lt;/p&gt;

&lt;p&gt;Limiting the number of external dependencies in your SDK is therefore critical to ensure compatibility.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Enterprise Features
&lt;/h3&gt;

&lt;p&gt;We think that features like retries, pagination, and security helpers should come in every SDK. These aren’t things that are strictly required at the earliest stages of integration, but as soon as users want to use your API for production use cases, this matters — and their absence can slow down integrations significantly. It’s just another case where a user doesn’t want to have to think about how to do this well — and may not have enough product context do to so optimally.&lt;/p&gt;

&lt;p&gt;The API creator is in a much better position to set sensible defaults here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;speakeasy&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;retries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nx"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;backoff&lt;/span&gt;
  &lt;span class="nx"&gt;backoff&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;initialInterval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;        &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="nx"&gt;milliseconds&lt;/span&gt;
    &lt;span class="nx"&gt;maxInterval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60000&lt;/span&gt;          &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="nx"&gt;seconds&lt;/span&gt;
    &lt;span class="nx"&gt;maxElapsedTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3600000&lt;/span&gt;     &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="nx"&gt;minutes&lt;/span&gt;
    &lt;span class="nx"&gt;exponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt;
  &lt;span class="nx"&gt;statusCodes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="nx"&gt;XX&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Language Idiomatic&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This is a meaty topic, and it’s hard to discuss in generalities (it’s different for every language), so let’s walk through an example of the type of design choices that a developer might expect of a Go SDK:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Minimal dependencies&lt;/strong&gt; and relying on the Go standard library as much as possible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Struct tags and reflection-based (de)serializers&lt;/strong&gt; to define how the types we generate are correctly serialized based on the OpenAPI document.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pointers for optional objects&lt;/strong&gt; including fields/parameters/response and request bodies to ensure that the user can differentiate between a field not being set and a field being set to a zero value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A Utils package&lt;/strong&gt; that improves readability by bundling the methods for configuring the SDK and serializing/deserializing the types we generate into a shared package, avoiding the need to duplicate in each method.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;shared&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;PetStatusEnum&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;PetStatusEnumAvailable&lt;/span&gt; &lt;span class="n"&gt;PetStatusEnum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"available"&lt;/span&gt;
    &lt;span class="n"&gt;PetStatusEnumPending&lt;/span&gt;   &lt;span class="n"&gt;PetStatusEnum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"pending"&lt;/span&gt;
    &lt;span class="n"&gt;PetStatusEnumSold&lt;/span&gt;      &lt;span class="n"&gt;PetStatusEnum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"sold"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Pet&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Category&lt;/span&gt;  &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Category&lt;/span&gt;      &lt;span class="s"&gt;`json:"category,omitempty"`&lt;/span&gt;
    &lt;span class="n"&gt;ID&lt;/span&gt;        &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;int64&lt;/span&gt;         &lt;span class="s"&gt;`json:"id,omitempty"`&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt;      &lt;span class="kt"&gt;string&lt;/span&gt;         &lt;span class="s"&gt;`json:"name"`&lt;/span&gt;
    &lt;span class="n"&gt;PhotoUrls&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;       &lt;span class="s"&gt;`json:"photoUrls"`&lt;/span&gt;
    &lt;span class="n"&gt;Status&lt;/span&gt;    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;PetStatusEnum&lt;/span&gt; &lt;span class="s"&gt;`json:"status,omitempty"`&lt;/span&gt;
    &lt;span class="n"&gt;Tags&lt;/span&gt;      &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;Tag&lt;/span&gt;          &lt;span class="s"&gt;`json:"tags,omitempty"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you make sure your SDK is type safe, idiomatic, and compatible with their existing environment you’re going to attract developers and inspire loyalty. At this point the last thing that needs to be solved is building a sustainable program for your SDK development.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Sustainably Build SDKs
&lt;/h2&gt;

&lt;p&gt;When you start building SDKs you will quickly run into two major issues:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;There are a lot of languages that you’ll need to build support for&lt;/li&gt;
&lt;li&gt;You need a way to update SDKs as your API changes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s walkthrough how you can overcome these two blockers.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Long-tail of languages and runtimes
&lt;/h3&gt;

&lt;p&gt;In 2022, &lt;a href="https://octoverse.github.com/2022/top-programming-languages"&gt;the proliferation of programming languages accelerated&lt;/a&gt; — and there’s no sign of that trend abating anytime soon.&lt;/p&gt;

&lt;p&gt;That’s great for nerding out over language design, but it’s a pain if you’re on the hook for building your API’s SDKs. Whereas 15 years ago you could have covered most programmers with 3-4 libraries, it’s now probably closer to 8-10. Languages can also have multiple popular frameworks, each of which require idiosyncratic tweaks to get the developer experience correct.&lt;/p&gt;

&lt;p&gt;This fragmentation of languages &amp;amp; runtimes makes it harder to provide the same level of service to your users and makes it hard to keep track of how users are interfacing with your product.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FynLA7Md--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/urw94rapdewoec83bug4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FynLA7Md--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/urw94rapdewoec83bug4.png" alt="Popular programming languages" width="800" height="850"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s not reasonable to expect that every company will have the language expertise internally to be able to support every language &amp;amp; runtime. That’s why we’ve built the Speakeasy generator. We are committed to building out support for every popular language &amp;amp; runtime so that you don’t have to. And in cases where people need something very specific, we offer the ability to right a custom template for the generator to use.&lt;/p&gt;

&lt;h3&gt;
  
  
  Preventing SDK Drift
&lt;/h3&gt;

&lt;p&gt;Too many companies think of building SDKs as a one-time project. They assign developers or contractors to handroll SDKs in the languages they know best, and punt on a long-term support system. This works for a while, but inevitably an expanding API definition, and an increasing number of libraries saddles the engineering team with a constant stream of tedious refactoring work. This refactoring may or may not be prioritized leading to SDKs with divergent behavior.&lt;/p&gt;

&lt;p&gt;The best API companies have built SDK generators and workflows that update the SDK automatically as part of their CI/CD pipeline. Whenever a new version of the API is published, a new version of their SDKs is released.&lt;/p&gt;

&lt;p&gt;This is a huge lift for any company to undertake — which is where Speakeasy comes in. With Speakeasy, any development team can have the same API infrastructure as the world’s best API companies. Producing idiomatic SDKs as part of your CI/CD is now available with our generator and a simple Github action:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wAEQV4QC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t4s324yajq9zfh8pgltt.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wAEQV4QC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t4s324yajq9zfh8pgltt.gif" alt="Workflow" width="800" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;What’s considered table stakes for developer experience has never been higher. And as the primary interface for APIs, your SDKs are perhaps the single most important component of your developer experience. At the same time, the proliferation in languages &amp;amp; runtimes being used in production applications means it’s never been harder to support the developer community.&lt;/p&gt;

&lt;p&gt;When you are building SDKs, make sure you build something that developers will actually want to use. That means making sure your SDKs are type-safe and idiomatic. Finally, make sure that your SDK development is sustainable. Make sure you have a plan to provide ongoing support to the SDKs you build, otherwise, you risk developers losing trust in the product when SDKs are not at parity with the API.&lt;/p&gt;

&lt;p&gt;If you want to make your SDK creation and ongoing support easy to manage, consider trying out the Speakeasy pipeline.&lt;/p&gt;

</description>
      <category>api</category>
      <category>sdk</category>
    </item>
    <item>
      <title>APIs vs SDKs: Why you should always have both</title>
      <dc:creator>Nolan Di Mare Sullivan</dc:creator>
      <pubDate>Mon, 22 May 2023 22:18:16 +0000</pubDate>
      <link>https://dev.to/speakeasy/apis-vs-sdks-why-you-should-always-have-both-4ahh</link>
      <guid>https://dev.to/speakeasy/apis-vs-sdks-why-you-should-always-have-both-4ahh</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;What are APIs and SDKs? We explore the different use cases that each addresses, what great APIs or SDKs look like, and explain why you need both.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What’s an API?​
&lt;/h2&gt;

&lt;p&gt;An API, or Application Programming Interface, allows different software applications to communicate with each other. For example, a developer in an e-commerce company might create an “orders” API that enables other services to easily retrieve orders made by a certain user. Consumers of the API may be external developers or even other developers within the same company.&lt;/p&gt;

&lt;p&gt;There are several different API technologies. Let’s look into the most popular in 2023: REST, GraphQL and gRPC&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;REST (Representational State Transfer)&lt;/strong&gt;: REST is the most widely used approach for creating APIs, primarily due to its simplicity and compatibility with HTTP. It dictates structured access to resources via well-known CRUD (Create/Read/Update/Delete) patterns. A common pattern in modern web development is to create a front-end written in React or a similar framework, which fetches data from and communicates with a back-end server via a REST API.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GraphQL&lt;/strong&gt;: GraphQL is a newer API technology that enables API consumers to request only the data they need. This reduces bandwidth required and improves performance, and is particularly suitable in situations where a REST API returns large amounts of unnecessary data. However, GraphQL is more complex to implement and maintain, and users need to have a deeper understanding of the underlying data models and relationships in order to construct the right queries.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;gRPC (Google Remote Procedure Call)&lt;/strong&gt;: gRPC is a high-performance, open-source framework designed for low-latency and highly-scalable communication between microservices. gRPC is strongly-typed, which helps catch errors earlier in the development process and improves reliability. However, gRPC ideally requires support for HTTP/2 and protocol buffers, which many web and mobile clients may not support natively. Also note that far fewer developers are familiar with gRPC than REST, which can limit adoption. For these reasons, gRPC is mainly used for internal microservice communications.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In summary, REST remains the most popular API technology due to its simplicity and widespread adoption. GraphQL and gRPC are popular for specific use cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why are APIs important?​
&lt;/h2&gt;

&lt;p&gt;There are several use cases for APIs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Leverage the Best&lt;/strong&gt;: Third party APIs provide developers easy access to services that would take years to build. With just a few lines of code, developers can add payment capabilities (e.g. &lt;a href="https://speakeasyapi.dev/post/apis-vs-sdks-difference/www.stripe.com/" rel="noopener noreferrer"&gt;Stripe&lt;/a&gt;), mapping data (e.g. &lt;a href="https://developers.google.com/maps/documentation/places/web-service/overview" rel="noopener noreferrer"&gt;Google Places API&lt;/a&gt;), or transactional email (e.g. &lt;a href="http://www.resend.com/" rel="noopener noreferrer"&gt;Resend&lt;/a&gt;) into their application, all powered by 3rd parties. This allows developers to stay focused on the value add of their app, and outsource the rest.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scale Applications&lt;/strong&gt;: By providing a well-defined programmatic interface, APIs allow developers to build their applications more efficiently, as they can update or replace individual components without affecting the entire system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Collaborate Efficiently&lt;/strong&gt;: APIs can be used internally to facilitate collaboration between teams by standardizing communication and providing a clear contract for developers to work against. Common functionalities are developed into services and APIs so that they can be leveraged by every team – accelerating the development of new applications and reducing duplicative work.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F24h1tkqybnzh0vs5p9rg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F24h1tkqybnzh0vs5p9rg.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is an SDK?​
&lt;/h2&gt;

&lt;p&gt;An SDK, or Software Development Kit, contains libraries, documentation, and other tools that aim to make it easy to integrate with an API in a specific language. It simplifies integrating with an API – without the developer needing to dig through docs to patch together details like how the API handles auth, query parameters, or returns response objects.&lt;/p&gt;

&lt;p&gt;The idea is that providing an intuitive and streamlined way to integrate with an API enables developers to quickly and easily access the API's functionality. Which is, after all, the goal of an API developer right?&lt;/p&gt;

&lt;p&gt;If this sounds a little academic, hang on for the code examples shortly.&lt;/p&gt;

&lt;p&gt;A typical SDK contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API client&lt;/strong&gt;: A pre-built client that handles communication with the API, taking care of low-level details like HTTP requests, authentication, and error handling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data models&lt;/strong&gt;: Classes or structures that represent the API's data objects, providing a strongly-typed and native representation of the data returned by an API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Helper functions and utilities&lt;/strong&gt;: These assist developers in performing common tasks related to the API, such as data serialization, pagination, or error handling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation and code samples&lt;/strong&gt;: These should be written specifically in the SDK’s language.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why use an SDK? Why are they important?​
&lt;/h2&gt;

&lt;p&gt;It’s worth diving a bit deeper on why SDKs for an API are so important.&lt;/p&gt;

&lt;p&gt;Without an SDK, developers have to manually handle the API integration process. This is time-consuming and error-prone. For example, developers need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read the API documentation, and especially the expected request and response data formats.&lt;/li&gt;
&lt;li&gt;Write code to form and send HTTP requests to the relevant API endpoints, parse the responses into the right data structures, and handle any errors that might occur.&lt;/li&gt;
&lt;li&gt;Manage the low-level details of authentication, retries, and rate limiting. The latter often are left out in the initial stages of integrating with an API, but are incredibly important if the API is being used in critical workflows.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall, this approach tends to resemble “trial and error”, which creates a high risk of users introducing bugs into the integration. API users end up frustrated and less motivated to use the API.&lt;/p&gt;

&lt;p&gt;However, with a language-idiomatic SDK, developers can avoid these common pitfalls. This results in a faster integration process, and greater API usage as API consumers are provided with a reliable, well-supported – even enjoyable! – experience.&lt;/p&gt;

&lt;p&gt;SDKs offer several advantages over the APIs alone:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Faster development&lt;/strong&gt;: SDKs provide pre-built functions that help developers accomplish tasks quickly. For example, an SDK for an e-commerce API might include a pre-built function and parameters for placing an order.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standardized / streamlined data and type definitions&lt;/strong&gt;: SDKs can ensure that data returned by an API is handled in a standard and recommended manner.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency&lt;/strong&gt;: By offering standardized components and best practices, SDKs promote consistency, making maintenance and management much easier. This might be particularly important for internal APIs, where you don’t want every API user to create an idiosyncratic implementation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Breaking change mitigation&lt;/strong&gt;: When an API introduces breaking changes, the SDK can in some cases be updated to accommodate those changes “under-the-hood” – while maintaining a consistent interface for the developers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streamlined documentation&lt;/strong&gt;: SDK docs focus on achieving specific outcomes and abstract away many low-level details, making them more usable, easier to understand, and ultimately more effective for developers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example of integrating to an API with and without an SDK​
&lt;/h2&gt;

&lt;p&gt;To highlight these differences, let’s look at an example of what integrating with an e-commerce API might look like, first without an SDK and then with one.&lt;/p&gt;

&lt;p&gt;The use case will be enabling a new customer to place an order. This requires fetching information about the product being ordered, creating a new customer, and creating the order itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First, here’s what integrating might look like without an SDK:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node-fetch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;your_api_key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.ecommerce.com/v1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Awesome Widget&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;John&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Doe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;john.doe@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;placeOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Step 1: Get product information&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/products`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Could not fetch products. Status code: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;productResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;productResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;productData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;productName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Product '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;productName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' not found.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Step 2: Create a new customer&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customerResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/customers`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customerResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Could not create customer. Status code: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;customerResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customerData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;customerResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customerId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;customerData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Step 3: Place the order&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;orderResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/orders`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nx"&gt;quantity&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orderResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Could not place order. Status code: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;orderResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Order placed successfully!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;placeOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the API consumer would need to construct all this code themself. They would need to refer to the API documentation to figure out which APIs should be called, what the response data structures look like, which data needs to be extracted, how to handle auth, what error cases might arise and how to handle them… oof!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now here’s the SDK version of this code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;EcommerceClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ecommerce-sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;your_api_key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EcommerceClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Awesome Widget&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;John&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Doe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;john.doe@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;placeOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;placeOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Order placed successfully!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;placeOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how much simpler and concise it is. Authentication is handled automatically with the developer just needing to copy in their key. Pre-built functions mean the developer doesn’t need to parse through pages of API docs to stitch together the required calls and associated data extraction themselves. Error handling and retries are built-in.&lt;/p&gt;

&lt;p&gt;Overall, a far easier and superior experience.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu0qkti4e90wv2oii7bv0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu0qkti4e90wv2oii7bv0.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s the difference between SDKs and APIs?​
&lt;/h2&gt;

&lt;p&gt;In summary, APIs &amp;amp; SDKs are symbiotic. Let’s talk about coffee to draw the analogy better.&lt;/p&gt;

&lt;p&gt;You can think of APIs as the fundamental, bare metal interfaces that enable applications or services to communicate. In our analogous example, APIs are like going to a coffee shop and getting a bag of beans, a grinder, a scale, filter paper, a coffemaker/brewer, kettle, and an instruction guide. Good luck making a delicious brew!&lt;/p&gt;

&lt;p&gt;SDKs on the other hand are critical to enabling APIs to reach their full potential, by providing a rapid, ergonomic way to access the API’s underlying functionality. In our coffee example, SDKs are more akin to telling a skilled barista “I’d like a latte please”. The barista does all of the work of assembling the ingredients, and you get to focus on the end result.&lt;/p&gt;

&lt;h2&gt;
  
  
  API and SDK best practices​
&lt;/h2&gt;

&lt;p&gt;Now we know what APIs and SDKs do, what should you keep in mind as you’re building them, to ensure they fulfill the promises we’ve outlined above?&lt;/p&gt;

&lt;p&gt;Here are some “gotchas!” to watch out for when building awesome APIs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Design carefully&lt;/strong&gt;: It can be extremely difficult to get users to change how they use an API once it’s in production. Avoiding unnecessary breaking changes, where possible, will save you many headaches and irate users later.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation&lt;/strong&gt;: In addition to an “API reference” that details every endpoint and response, consider creating a “usage guide” that walks users through how to use APIs in sequence to accomplish certain tasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentication&lt;/strong&gt;: Creating and sending users API keys manually works fine for an MVP, but has obvious security and scalability challenges. An ideal solution is to offer a self-service experience where end-users can generate and revoke keys themselves. For more on API auth, &lt;a href="https://speakeasyapi.dev/post/api-auth-guide/" rel="noopener noreferrer"&gt;check out our guide&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Troubleshooting and support&lt;/strong&gt;: Users will inevitably run into issues. It’s easy for members of the team to quickly get inundated with support requests. Try to provide self-service tools for troubleshooting API issues, such as logging and monitoring, and community support channels.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Building great SDKs presents a different set of considerations. Keep these in mind if you want to offer a great SDK to your users:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;How stable is the underlying API?&lt;/strong&gt; If the API is undergoing frequent changes, it might be particularly challenging to manually keep the SDKs up-to-date and in sync with the API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Creation and maintenance cost&lt;/strong&gt;: Creating native language SDKs for all your customers’ preferred languages can be a huge hiring and skills challenge. Each language SDK also has to be updated every time the API changes – ideally in lockstep to avoid the SDK and API being out of sync. This is time-consuming and costly. Many companies have deprecated or scaled back their SDKs after misjudging the work required.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing and validation&lt;/strong&gt;: Plan for thorough testing of the SDKs across different platforms and languages, including unit tests, integration tests, and end-to-end tests, to ensure the SDKs are reliable and compatible with the API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation&lt;/strong&gt;: Provide clear examples and code snippets in each language to make the SDKs easy to use and understand.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  APIs, SDKs and Speakeasy​
&lt;/h2&gt;

&lt;p&gt;As discussed above, APIs and SDKs are both incredibly important tools.&lt;/p&gt;

&lt;p&gt;Unfortunately however, creating SDKs for every API has been out of reach for most teams, given their cost, the skills required, and distraction from the core product roadmap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Until now.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At Speakeasy, we believe that every API deserves an amazing SDK – one that is idiomatic, reliable, low-cost, always-in-sync, and easy to manage.&lt;/p&gt;

&lt;p&gt;And that’s exactly what you get with Speakeasy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Idiomatic&lt;/strong&gt;: we know how important it is that the SDK feels ergonomic. That’s why we built a generator from the ground up with a focus on creating idiomatic SDKs in a range of languages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reliable&lt;/strong&gt;: our SDKs have been battle-tested in real-world scenarios through our customers. And we handle all support and troubleshooting – so you don’t have to.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low-cost&lt;/strong&gt;: until now, only the largest companies have been able to afford an engineering team to focus on SDKs. Speakeasy provides access to best-in-class SDKs for a fraction of the price of a full engineering team.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Always-in-sync&lt;/strong&gt;: our SDKs rebuild after every API spec change. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy to manage&lt;/strong&gt;: we offer a fully-featured web application so you can always understand the current status of your SDK generation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://speakeasyapi.dev/post/case-study-vessel/" rel="noopener noreferrer"&gt;Read our case study with Vessel&lt;/a&gt;, a unified API for CRM and sales, to learn more about us.&lt;/p&gt;

&lt;p&gt;If you have an API spec, why not try it for yourself now? Just click the button below to generate your first SDKs for free.&lt;/p&gt;

</description>
      <category>api</category>
      <category>sdk</category>
      <category>openapi</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Idiomatic SDKs for OpenAPI</title>
      <dc:creator>Nolan Di Mare Sullivan</dc:creator>
      <pubDate>Tue, 06 Dec 2022 15:31:24 +0000</pubDate>
      <link>https://dev.to/speakeasy/idiomatic-sdks-for-openapi-4i94</link>
      <guid>https://dev.to/speakeasy/idiomatic-sdks-for-openapi-4i94</guid>
      <description>&lt;h2&gt;
  
  
  Our SDK Generator
&lt;/h2&gt;

&lt;p&gt;Client SDKs are a bit like vodka, they’re either handcrafted and very expensive, or they’re cheap and leave you with nothing but regrets and a hangover. To get an SDK with a good developer experience, companies have historically needed to invest significant resources in handrolling their own. The only other option is using bare-bones OSS offerings. &lt;/p&gt;

&lt;p&gt;We’re providing a middle path; a free-to-use SDK generator that provides the foundation for a great developer experience with no investment required. &lt;strong&gt;We've launched with support for Go, Python, Typescript, and Java (alpha)&lt;/strong&gt;. We plan to add Ruby as well as other languages soon!&lt;/p&gt;

&lt;p&gt;What constitutes a great developer experience is of course subjective, but we focused on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fully typed&lt;/strong&gt; so that SDKs feel like they have been written by a human: easy to read and debug.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batteries included&lt;/strong&gt; where everything from retries to pagination of your APIs is handled as part of the SDK generation (Available in our closed beta).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy to use&lt;/strong&gt;, our SDK generators are fault tolerant, and designed to always output usable SDKs where possible. If we can't output a usable SDK, we will provide human readable error messaging about why validation of your OpenAPI spec failed (instead of just failing silently or with obscure error messages or giving you a broken SDK)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full OpenAPI Coverage&lt;/strong&gt;, We plan to have broad coverage of the OpenAPI specs while having a deep focus on the most common ways of defining an API and ensuring you have a nice to use SDK as the output.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To illustrate the differences between our SDK generator and the open source examples, we’ve run the canonical Petshop Example through both and compared the output. But the TLDR is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Speakeasy SDK generator is installed with brew with no additional dependencies&lt;/li&gt;
&lt;li&gt;No NPM. No Java. Everything is packaged in a self-contained binary&lt;/li&gt;
&lt;li&gt;We support less languages for now but the ones we do are more idiomatic, so that usage feels natural per language.&lt;/li&gt;
&lt;li&gt;We provide a simple API to the SDKs that is easy to mock and test.&lt;/li&gt;
&lt;li&gt;The SDKs we produce have fully typed outputs including enums and more.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The generator has been battle tested on thousands of APIs and we are sharing &lt;a href="https://github.com/speakeasy-api/openapi-directory" rel="noopener noreferrer"&gt;the results in our github repo&lt;/a&gt;. If you want to try it out on your own, &lt;a href="https://github.com/speakeasy-api/speakeasy" rel="noopener noreferrer"&gt;download the CLI&lt;/a&gt; or brew install and get started in minutes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;brew install speakeasy-api/homebrew-tap/speakeasy&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&lt;em&gt;speakeasy generate sdk -s openapi.yaml -o ./sdk -l go&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Our Experience using OpenAPI generator
&lt;/h2&gt;

&lt;p&gt;Before we get into the details of how our SDK generator compares with others, we wanted to walk through the experience with the OpenAPI generator that led to this product being created in the first place. It will probably be quite familiar to those who have used the OpenAPI tooling.&lt;/p&gt;
&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;p&gt;Ours was a struggle from the word go.  If you look at the &lt;a href="https://openapi-generator.tech/#try" rel="noopener noreferrer"&gt;openapi-generator website&lt;/a&gt;, it will direct you to ‘Try NPM’ to install the CLI. The key word here is ‘Try’, because there is no guarantee of success. We had multiple issues not already being setup for using NPM:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Had permissions issues installing globally via NPM&lt;/li&gt;
&lt;li&gt;Got an error “Error: /bin/sh: 1: java: not found”  when first run. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To resolve the issue, we had to install both NPM and Java before we could get the installation working. We had better luck with the homebrew install instruction further down the page. They worked on the first attempt at installation, downloading all the required dependencies.&lt;/p&gt;
&lt;h3&gt;
  
  
  Schema Validation
&lt;/h3&gt;

&lt;p&gt;Validating our OpenAPI spec worked fine and was a great start to using the openapi-generator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openapi-generator validate &lt;span class="nt"&gt;-i&lt;/span&gt; openapi.yaml           
Validating spec &lt;span class="o"&gt;(&lt;/span&gt;openapi.yaml&lt;span class="o"&gt;)&lt;/span&gt;
No validation issues detected.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SDK Generation
&lt;/h3&gt;

&lt;p&gt;Even though our schema had been deemed valid by the tool, we ran into issues when we began trying to generate a Go SDK from our &lt;a href="https://docs.speakeasyapi.dev/openapi.yaml" rel="noopener noreferrer"&gt;openapi yaml&lt;/a&gt;. The error that we received was most unhelpful and we weren’t able to determine why our spec might have been causing issues.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openapi-generator generate &lt;span class="nt"&gt;-i&lt;/span&gt; openapi.yaml &lt;span class="nt"&gt;-g&lt;/span&gt; go &lt;span class="nt"&gt;-o&lt;/span&gt; ../speakeasy-client-sdk-go-openapi-gen
...
Exception: Property and is missing from getVars
        at org.openapitools.codegen.DefaultGenerator.processOperation&lt;span class="o"&gt;(&lt;/span&gt;DefaultGenerator.java:1187&lt;span class="o"&gt;)&lt;/span&gt;
        at org.openapitools.codegen.DefaultGenerator.processPaths&lt;span class="o"&gt;(&lt;/span&gt;DefaultGenerator.java:1078&lt;span class="o"&gt;)&lt;/span&gt;
        at org.openapitools.codegen.DefaultGenerator.generateApis&lt;span class="o"&gt;(&lt;/span&gt;DefaultGenerator.java:580&lt;span class="o"&gt;)&lt;/span&gt;
        at org.openapitools.codegen.DefaultGenerator.generate&lt;span class="o"&gt;(&lt;/span&gt;DefaultGenerator.java:915&lt;span class="o"&gt;)&lt;/span&gt;
        at org.openapitools.codegen.cmd.Generate.execute&lt;span class="o"&gt;(&lt;/span&gt;Generate.java:465&lt;span class="o"&gt;)&lt;/span&gt;
        at org.openapitools.codegen.cmd.OpenApiGeneratorCommand.run&lt;span class="o"&gt;(&lt;/span&gt;OpenApiGeneratorCommand.java:32&lt;span class="o"&gt;)&lt;/span&gt;
        at org.openapitools.codegen.OpenAPIGenerator.main&lt;span class="o"&gt;(&lt;/span&gt;OpenAPIGenerator.java:66&lt;span class="o"&gt;)&lt;/span&gt;
Caused by: java.lang.RuntimeException: Property and is missing from getVars
        at org.openapitools.codegen.DefaultCodegen.addRequiredVarsMap&lt;span class="o"&gt;(&lt;/span&gt;DefaultCodegen.java:7319&lt;span class="o"&gt;)&lt;/span&gt;
        at org.openapitools.codegen.DefaultCodegen.addVarsRequiredVarsAdditionalProps&lt;span class="o"&gt;(&lt;/span&gt;DefaultCodegen.java:7354&lt;span class="o"&gt;)&lt;/span&gt;
        at org.openapitools.codegen.DefaultCodegen.fromParameter&lt;span class="o"&gt;(&lt;/span&gt;DefaultCodegen.java:4972&lt;span class="o"&gt;)&lt;/span&gt;
        at org.openapitools.codegen.DefaultCodegen.fromOperation&lt;span class="o"&gt;(&lt;/span&gt;DefaultCodegen.java:4394&lt;span class="o"&gt;)&lt;/span&gt;
        at org.openapitools.codegen.DefaultGenerator.processOperation&lt;span class="o"&gt;(&lt;/span&gt;DefaultGenerator.java:1155&lt;span class="o"&gt;)&lt;/span&gt;
        ... 6 more
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Direct Comparison with Petshop Example
&lt;/h2&gt;

&lt;p&gt;Let’s move onto an example that does work with the openapi-generator, the canonical Petstore API from the OpenAPI specification. First let’s take a look at the commands used to generate SDKs:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OpenAPI&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openapi-generator generate &lt;span class="nt"&gt;-i&lt;/span&gt; petstore.yaml &lt;span class="nt"&gt;-g&lt;/span&gt; go &lt;span class="nt"&gt;-o&lt;/span&gt; ./openapi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Speakeasy&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;speakeasy generate sdk &lt;span class="nt"&gt;-s&lt;/span&gt; petstore.yaml &lt;span class="nt"&gt;-o&lt;/span&gt; ./speakeasy &lt;span class="nt"&gt;-l&lt;/span&gt; go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The OpenAPI and Speakeasy generators both output usable SDKs for the Petstore API. We’re using Go for this example, but our generator supports Go, Python and Typescript. Note that the openapi generator supports a large array of additional languages that we plan to add to the speakeasy generator down the road.&lt;/p&gt;

&lt;p&gt;While the OpenAPI generator does support many languages, as we were looking through we felt that the SDKs for languages like Go were actually quite Java-like and less idiomatic to the Go language: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OpenAPI&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;openapi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewAPIClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;openapi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewConfiguration&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;openapi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContextAccessToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"special-key"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PetApi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FindPetsByStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pending"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;pets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"unexpected status code: %d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Marshal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Speakeasy&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;sdk&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;speakeasy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;operations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FindPetsByStatusStatusEnumPending&lt;/span&gt;
  &lt;span class="n"&gt;queryParams&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;operations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FindPetsByStatusQueryParams&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;security&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;operations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FindPetsByStatusSecurity&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;PetstoreAuth&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SchemePetstoreAuth&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;Authorization&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Bearer special-key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sdk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FindPetsByStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;operations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FindPetsByStatusRequest&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;QueryParams&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;queryParams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Security&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;security&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"unexpected status code: %d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Marshal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The openapi-generator’s SDK is also harder to mock due to the multiple method calls required to set up a request and execute it.  With the Speakeasy SDK, a single method call is sufficient.&lt;/p&gt;

&lt;p&gt;It’s also worth looking at the formatting and styling for each of the SDKs generated where there are some differences: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The openapi-generator outputs comments to help with usage (coming soon for the speakeasy generator). &lt;/li&gt;
&lt;li&gt;The openapi-generator generated a lot of additional getter/setter, instantiation and serialization methods that aren’t required and just reduce the readability of the SDKs code.&lt;/li&gt;
&lt;li&gt;The OpenAPI SDK code lacks formatting, whereas our SDK code is formatted idiomatically. &lt;/li&gt;
&lt;li&gt;Our generator generated full types for everything (where possible). &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last point we feel is quite important. For example, OpenAPI treats enums as strings, whereas we generate typed enums reducing usage errors. Our support for enums comes at the cost of supporting a single edge case of the Petstore API, which allows status enums to be provided as comma separated strings to filter on multiple status, this could be overcome by defining in the OpenAPI spec that the type is an array of enums instead of just a string.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OpenAPI&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Pet struct for Pet&lt;/span&gt;
  &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Pet&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;int64&lt;/span&gt; &lt;span class="s"&gt;`json:"id,omitempty"`&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"name"`&lt;/span&gt;
    &lt;span class="n"&gt;Category&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Category&lt;/span&gt; &lt;span class="s"&gt;`json:"category,omitempty"`&lt;/span&gt;
    &lt;span class="n"&gt;PhotoUrls&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"photoUrls"`&lt;/span&gt;
    &lt;span class="n"&gt;Tags&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;Tag&lt;/span&gt; &lt;span class="s"&gt;`json:"tags,omitempty"`&lt;/span&gt;
    &lt;span class="c"&gt;// pet status in the store&lt;/span&gt;
    &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"status,omitempty"`&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;// NewPet instantiates a new Pet object&lt;/span&gt;
  &lt;span class="c"&gt;// This constructor will assign default values to properties that have it defined,&lt;/span&gt;
  &lt;span class="c"&gt;// and makes sure properties required by API are set, but the set of arguments&lt;/span&gt;
  &lt;span class="c"&gt;// will change when the set of required properties is changed&lt;/span&gt;
  &lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewPet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;photoUrls&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Pet&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;this&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Pet&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
    &lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PhotoUrls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;photoUrls&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;// NewPetWithDefaults instantiates a new Pet object&lt;/span&gt;
  &lt;span class="c"&gt;// This constructor will only assign default values to properties that have it defined,&lt;/span&gt;
  &lt;span class="c"&gt;// but it doesn't guarantee that properties required by API are set&lt;/span&gt;
  &lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewPetWithDefaults&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Pet&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;this&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Pet&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;// GetId returns the Id field value if set, zero value otherwise.&lt;/span&gt;
  &lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Pet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;GetId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;int64&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="kt"&gt;int64&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ret&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;// GetIdOk returns a tuple with the Id field value if set, nil otherwise&lt;/span&gt;
  &lt;span class="c"&gt;// and a boolean to check if the value has been set.&lt;/span&gt;
  &lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Pet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;GetIdOk&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;// ... And continues with getters/setters for every field&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Speakeasy&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;PetStatusEnum&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;

  &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;PetStatusEnumAvailable&lt;/span&gt; &lt;span class="n"&gt;PetStatusEnum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"available"&lt;/span&gt;
    &lt;span class="n"&gt;PetStatusEnumPending&lt;/span&gt;   &lt;span class="n"&gt;PetStatusEnum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"pending"&lt;/span&gt;
    &lt;span class="n"&gt;PetStatusEnumSold&lt;/span&gt;      &lt;span class="n"&gt;PetStatusEnum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"sold"&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Pet&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Category&lt;/span&gt;  &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Category&lt;/span&gt;      &lt;span class="s"&gt;`json:"category,omitempty"`&lt;/span&gt;
    &lt;span class="n"&gt;ID&lt;/span&gt;        &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;int64&lt;/span&gt;         &lt;span class="s"&gt;`json:"id,omitempty"`&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt;      &lt;span class="kt"&gt;string&lt;/span&gt;         &lt;span class="s"&gt;`json:"name"`&lt;/span&gt;
    &lt;span class="n"&gt;PhotoUrls&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;       &lt;span class="s"&gt;`json:"photoUrls"`&lt;/span&gt;
    &lt;span class="n"&gt;Status&lt;/span&gt;    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;PetStatusEnum&lt;/span&gt; &lt;span class="s"&gt;`json:"status,omitempty"`&lt;/span&gt;
    &lt;span class="n"&gt;Tags&lt;/span&gt;      &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;Tag&lt;/span&gt;          &lt;span class="s"&gt;`json:"tags"`&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Hope people found that comparison to be interesting/useful. We look forward to hearing feedback on how we can improve our SDK generators and what we should build next. If you have any questions, &lt;a href="https://join.slack.com/t/speakeasy-dev/shared_invite/zt-1cwb3flxz-lS5SyZxAsF_3NOq5xc8Cjw" rel="noopener noreferrer"&gt;please join our slack community&lt;/a&gt; and you can message us directly. &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Definitive Guide to API DevEx Portals</title>
      <dc:creator>Nolan Di Mare Sullivan</dc:creator>
      <pubDate>Tue, 29 Nov 2022 13:57:14 +0000</pubDate>
      <link>https://dev.to/ndimares/definitive-guide-to-api-devex-portals-49bf</link>
      <guid>https://dev.to/ndimares/definitive-guide-to-api-devex-portals-49bf</guid>
      <description>&lt;h2&gt;
  
  
  Why You Need A Portal If You Want to Serve Developers
&lt;/h2&gt;

&lt;p&gt;When you’re teaching a kid to drive, you don’t hand them a car key, the operation manual for the car and then leave them to figure it out. Of course you could, but not everyone would get the car moving, and there might be some easily avoided accidents along the way.&lt;/p&gt;

&lt;p&gt;But this is exactly how many companies teach people to use their APIs: they hand over a stack of documentation, an API key and call it a day. Of course, those tools are important; without them, your users are stuck at ‘Go’. But these tools alone don’t really deliver any sort of experience to users. It’s basic. It’s lackluster. It’s &lt;em&gt;an&lt;/em&gt; experience, but probably not &lt;em&gt;&lt;strong&gt;THE&lt;/strong&gt;&lt;/em&gt; developer experience you want to give your users. It’s certainly not a delightful experience or memorable enough to tell others about. And that lackluster experience has a material impact on the adoption and usage of your API.&lt;/p&gt;

&lt;p&gt;If developer experience has been underinvested in, a common set of issues begin to pop up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The average time gap between user signup and first successful request is longer than one day.&lt;/li&gt;
&lt;li&gt;Your team spends hours every week troubleshooting client integrations.&lt;/li&gt;
&lt;li&gt;The average number of API calls &amp;amp; endpoints made by users isn’t expanding over time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Resulting in: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Decreased API adoption and usage&lt;/li&gt;
&lt;li&gt;A higher cost to support for each user.&lt;/li&gt;
&lt;li&gt;A reduced LTV for each user. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To address these issues you need to vastly improve your API’s DevEx. &lt;strong&gt;The key to a great user experience is providing your users with an API DevEx Portal&lt;/strong&gt; which makes your API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Accessible&lt;/strong&gt;: there is zero friction to begin sending API requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Understandable&lt;/strong&gt;: users are able to get immediate feedback on their API usage – and to self-service troubleshoot issues when they do occur.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Usable&lt;/strong&gt;: It is trivially easy for users to discover and test out new use cases for your API that naturally expand their usage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But what tooling gets you to this point? Let’s walk through each of the above criteria and discuss the specific tools that can help you give your API users the DevEx they deserve.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Tooling Does Your API Portal Need
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Accessible
&lt;/h3&gt;

&lt;p&gt;Making the API accessible means making it as easy as possible for users to make that first API call. 99% of companies are still stuck somewhere in this first stage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Capabilities&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API Documentation&lt;/strong&gt; - There has been a pervasive view that documentation equals  great DevEx. We think that documentation is only the first step: it’s critical, but on its own it will still leave your API users wanting. Documentation should be comprehensive, easy to navigate / search, and have code snippets / examples embedded. Ideally, docs should enable users to try the API without any additional tooling or configuration. API docs should also differentiate between API reference (a full list of all endpoints, parameters, response codes, etc.) and usage guides (tutorials that take users step-by-step through how they can use the API to accomplish key tasks).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auth Login&lt;/strong&gt; - If you want to offer developers a more personalized experience, auth is the prerequisite. You need to know who someone is before you can issue them an API key, and start giving them tools to help them understand and track their usage. Login should of course be managed by a single shared identity service e.g. auth0 or other system of record – you don’t want to build a separate user management system for your application and your API for example.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Key Management&lt;/strong&gt; - Nobody wants to have to fill in a typeform and wait for customer support to review before they can get started with an API. If there’s no way for a developer to create keys on their own, most will never convert into users of your product. By the time someone has reviewed their access request, they will have moved on to a new priority, or found an alternative solution. If the API interfaces with sensitive data, and a review process is a legal requirement for production credentials, then enable users to provision sandbox keys without review (more on Sandboxes below).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftfra3v378v3z8fjr3q8e.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftfra3v378v3z8fjr3q8e.gif" alt="Key Management in the Speakeasy API Portal" width="480" height="294"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Understandable
&lt;/h3&gt;

&lt;p&gt;Even companies where APIs are the primary surface area often struggle to make the investment required to advance their developer portal to being understandable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Capabilities&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API Request Viewer&lt;/strong&gt; - When debugging, there’s no substitute for being able to step through things one at a time. A request viewer makes it possible for your users to view the full list of requests they’ve sent to your API – without creating additional work for your team to pull logs, send screenshots via email or Slack, or jump on a Zoom call. Without a self-service request viewer, broken integrations create poor API experience and leads to churned clients. A request viewer should provide users the ability to filter by time, response code, endpoint and more, and ideally allow users to edit and replay the request for quick debugging.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6h3ze4s69x63kbnh9ptl.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6h3ze4s69x63kbnh9ptl.gif" alt="Request Viewer in the Speakeasy API Portal" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;-‍ &lt;strong&gt;API Usage Metrics&lt;/strong&gt; - A request viewer is only useful to developers if they know there’s an issue to investigate. That is why it’s important to surface key usage metrics in real time – so that users know the overall health of their integration. Usage metrics should place an emphasis on error reporting and significant changes in usage so that your users can take corrective action to any errors or unintended changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl9z35kmj0237e2wj5y75.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl9z35kmj0237e2wj5y75.gif" alt="Usage Dashboard in the Speakeasy API Portal" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API Status Page&lt;/strong&gt; - Developers need a place to check if APIs are experiencing downtime. Nothing is more frustrating than having to email a company, “is your API working?” An API status page brings transparency, and transparency is important for building trust with your users. &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Usable
&lt;/h3&gt;

&lt;p&gt;Usability tooling is focused on making it easy to test out new API use cases and also making those new use cases easy to find. Usability tooling shines as APIs become larger. Early on an API will serve a single use case, and documentation will focus on supporting that use case. As the API’s surface area grows, documentation becomes denser, and isolating relevant instructions becomes challenging. Usability tooling will help insulate users against this by providing structure for the documentation, and making it easier to test out new use cases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Capabilities&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Client SDKs&lt;/strong&gt; - You need to meet your developers where they already are. Providing client SDKs makes it easier for developers to get started with your API by grounding them in the familiarity of their favorite language, and significantly reducing the amount of boilerplate they need to write. This is especially true if your SDKs can handle auth, pagination, and retries and others. They are therefore great at helping maximize your audience while minimizing support costs. But it’s not enough to have SDKs, it’s critical that the SDKs are developer-friendly, meaning that they are language idiomatic and human readable. Unfortunately, creating client SDKs is prohibitively expensive for most API teams, since they need to be created and updated by hand. While open source generators exist, the SDKs they output are often buggy and not ergonomic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Runbooks&lt;/strong&gt; - We think of runbooks as live usage guides. They take users step-by-step through the process of using your API to accomplish specific tasks, but also show relevant, live API requests in real-time. This helps to focus developers on the key use cases required to complete API integrations. Your customers can use them to grow their usage of your API. As an API builder, runbooks also help you understand the maturity of your customer base: you can begin to understand your API usage as a customer funnel, and start to measure where and why users drop out of the funnel.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Sandbox&lt;/strong&gt; - Probably nothing helps more with adoption than giving developers an easy way to play with your API. A sandbox can give prospective users a way to use your APIs without needing to sign up for an account. Developers are more likely to trust an API if they’ve seen it working before needing to hand over their information. And a sandbox can give existing users a way to learn by doing, and without any risk of corrupting production workflows. This enables users to easily expand their use cases for your API.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to get to Best-In-Class: Build or Buy?
&lt;/h2&gt;

&lt;p&gt;The list above is presented as a rough roadmap. To improve your DevEx, just build each of the tools listed above in order, and you can progress from having no tooling, to having a great Developer Portal.&lt;/p&gt;

&lt;p&gt;But as any PM or engineer will tell you, knowing what to build is only the beginning. Finding the resources required to build is the real battle. Great DevEx is extremely important for a successful API, but building all of the above is a huge commitment of resources, requires significant ongoing maintenance, and likely isn’t a core competency for your organization. As a result, investing in Developer Experience continues to be the project that is slated for next quarter. &lt;/p&gt;

&lt;p&gt;For almost every company therefore, investing in a best-of-breed solution makes more sense. With an API DevEx Portal from a company like Speakeasy, your customers get a world-class API Developer Experience in days instead of quarters, your product roadmap has negligible impact, and your eng teams don’t need to reinvent the wheel.&lt;/p&gt;

&lt;p&gt;Furthermore, our product is designed with maximum flexibility in mind, giving you the best of both worlds. Every tool in our API DevEx Portal is a React component, and can be customized, branded and extended as you need. Similarly, our platform can be self-hosted or run in our Speakeasy Cloud depending on your requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;For a long time, companies have been able to get by with substandard developer experiences, but that is beginning to change. Developer Experience is now getting the attention it deserves, and we are rapidly reaching an inflection point. What has been previously considered great DevEx is becoming table stakes for developer products. &lt;/p&gt;

&lt;p&gt;We know that DevEx isn’t ignored because companies don’t see the value. Rather, it’s the result of painful prioritization decisions. That’s why Speakeasy exists. We don’t want anyone to have to ever make that tradeoff. With Speakeasy you can get a best-in-class, composable developer portal up and running in a matter of minutes. If you want to learn more, &lt;a href="https://www.speakeasyapi.dev/request-access" rel="noopener noreferrer"&gt;join our beta program&lt;/a&gt; or &lt;a href="https://join.slack.com/t/speakeasy-dev/shared_invite/zt-1cwb3flxz-lS5SyZxAsF_3NOq5xc8Cjw" rel="noopener noreferrer"&gt;come chat with us in our Slack!&lt;/a&gt;&lt;/p&gt;

</description>
      <category>emptystring</category>
    </item>
    <item>
      <title>An Improved OpenAPI SDK Generator</title>
      <dc:creator>Nolan Di Mare Sullivan</dc:creator>
      <pubDate>Wed, 31 Aug 2022 16:37:58 +0000</pubDate>
      <link>https://dev.to/ndimares/an-improved-openapi-sdk-generator-4ef7</link>
      <guid>https://dev.to/ndimares/an-improved-openapi-sdk-generator-4ef7</guid>
      <description>&lt;h2&gt;
  
  
  What We’ve Built
&lt;/h2&gt;

&lt;p&gt;We’re excited to &lt;a href="https://easysdk.xyz/"&gt;publicly launch an SDK generator&lt;/a&gt; that improves upon the &lt;a href="https://github.com/OpenAPITools/openapi-generator"&gt;OpenAPI service&lt;/a&gt;.  In our view the biggest problem with the OpenAPI generator was that it produced client libraries that were untyped and unopinionated. That’s why we focused on building a generator that is able to handle typing correctly and is opinionated about what makes for a good SDK:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Low-dependency&lt;/strong&gt; - To try and keep the SDK isomorphic (i.e. available both for Browsers and Node.JS servers), we wrap axios, but that’s it.This is intended to be idiomatic typescript; very similar to code a human would write; with the caveat that the typing is only as strict as the OpenAPI specification.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Static Typing&lt;/strong&gt; - At this point static typing is everywhere. So wherever possible, we generate typed structures, construct path variables automatically, pass through query parameters, and expose strictly typed input / output body types.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Language idiomatic &amp;amp; opinionated&lt;/strong&gt; - There’s value in being neutral, but we felt like there is more value in being opinionated. We’ve made choices for how things like Pagination, Retries (Backoff/Jitter etc), Auth integrations, should be handled in the SDK.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why We Think SDKs Are Important
&lt;/h2&gt;

&lt;p&gt;A good developer experience means flattening the learning curve by meeting developers where they already; that’s why every company with an API platform should strive to offer SDKs for integrating with their APIs.  Language-idiomatic SDKs improve user productivity by removing the need for writing the boilerplate required to send API requests and parse response objects.  Companies that offer SDKs get faster adoption, spend less time troubleshooting, and provide an overall better developer experience.&lt;/p&gt;

&lt;p&gt;We look forward to hearing from the community what they think of the service. We’d love to know what else people would want to see included in a generator. What other languages would people want to see supported?&lt;/p&gt;

</description>
      <category>openapi</category>
      <category>api</category>
      <category>typescript</category>
    </item>
    <item>
      <title>The REST Template Project</title>
      <dc:creator>Nolan Di Mare Sullivan</dc:creator>
      <pubDate>Wed, 03 Aug 2022 15:50:45 +0000</pubDate>
      <link>https://dev.to/ndimares/the-rest-template-project-1f4g</link>
      <guid>https://dev.to/ndimares/the-rest-template-project-1f4g</guid>
      <description>&lt;h2&gt;
  
  
  Github Repository: &lt;a href="https://github.com/speakeasy-api/rest-template-go"&gt;speakeasy-api/rest-template-go&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Building a RESTful API can be daunting for developers who have never done it before (and even those who have). There are a number of choices and best practices that need to be considered as part of an API’s implementation; it can be hard to know where to start. That’s why we’re pleased to announce the &lt;a href="https://www.speakeasyapi.dev/templates"&gt;RESTful API template project&lt;/a&gt;. Over the coming months, we will release RESTful API templates for the most used programming languages – &lt;a href="https://github.com/speakeasy-api/rest-template-go"&gt;starting today with Go&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is the template that our team forks from when we are building new APIs. The repo contains a CRUD API for a ‘user’ resource which incorporates the best practices needed for a basic REST service. Our hope is that developers can use this as a foundation upon which to build their own sets of APIs.&lt;/p&gt;

&lt;p&gt;The service represents our team’s opinionated stance about what makes for good RESTful API code. Specifically, the template gives you a service which is: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Entity-based&lt;/strong&gt;: The resources available should represent the domain model. Each resource should have the CRUD methods implemented (even if not all are available to API consumers). In our template, we have a single resource defined (users.go). However other resources could be easily added by copying the template and changing the logic of the service layer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Properly Abstracted&lt;/strong&gt;: The transport, service, and data layers are all cleanly abstracted from one another. This makes it easy to apply updates to your API endpoints.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Consistent&lt;/strong&gt;: It's important that consumers of a service have guaranteed consistency across the entire range of API endpoints and methods. In this service, responses are consistently formatted whether successfully returning a JSON object or responding with an error code. All the service's methods use shared response (http.go) and error (errors.go) handler functions to ensure consistency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tested&lt;/strong&gt;: We believe that a blend of unit and integration testing is important for ensuring that the service maintains its contract with consumers. The service repo therefore contains a collection of unit and integration tests for the various layers of the service.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Explorable&lt;/strong&gt;: It is important for developers to be able to play with an endpoint in order to understand it. We have provided Postman collections for testing out the REST endpoints exposed by the service. There is a Bootstrap Users collection that can be run using the Run collection tool in Postman that will create 100 users to test the search endpoint with.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We look forward to hearing from the community what they think of the repo. We’d love to know what else people would want to see included in a template: versioning, pagination, authentication?  Would people want to see more advanced features?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>go</category>
      <category>api</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Choosing Between SASS vs CSS Modules vs CSS-In-JS</title>
      <dc:creator>Nolan Di Mare Sullivan</dc:creator>
      <pubDate>Mon, 18 Jul 2022 16:32:50 +0000</pubDate>
      <link>https://dev.to/ndimares/choosing-between-sass-vs-css-modules-vs-css-in-js-2p9h</link>
      <guid>https://dev.to/ndimares/choosing-between-sass-vs-css-modules-vs-css-in-js-2p9h</guid>
      <description>&lt;p&gt;We are in the midst of building the first version of our API Ops platform. One of the components which we are actively building is a developer console where devs can get an overview of their APIs and each endpoint’s performance. While building our UI, we are debating many of the fundamental front end architecture decisions that will shape our UI development. One of the biggest debates thus far was whether we would use SASS or CSS-In-JS for our styling.&lt;/p&gt;

&lt;p&gt;We know developers are facing these same questions every day, so we want to publish our thoughts in case they are useful to anyone else. Ultimately, we chose to move forward with CSS-In-JS as our primary styling mechanism for reasons specific to our business:we considered it important for the UIs we build to be easily embeddable within external UIs. We felt that CSS-In-JS was the best option for embeds, because integrators wouldn’t need to worry about dealing with style injection, and could theme our components into their style.&lt;/p&gt;

&lt;p&gt;We’d love to know your thoughts and how you reached decisions about styling your UI? Have you had any bad experiences with CSS-In-JS? &lt;/p&gt;

&lt;p&gt;Below is our full internal conversation only minimally edited for brevity:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--opuaJ3RM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/suon7bxl73ubu7i0qbt7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--opuaJ3RM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/suon7bxl73ubu7i0qbt7.png" alt="original slack question" width="800" height="383"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--u7AWiQg1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pwoj7tgkktwdtpwj0tgf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--u7AWiQg1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pwoj7tgkktwdtpwj0tgf.png" alt="Value of Sass" width="800" height="463"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--d2ml-CFJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wapyc019v59k802e4cfw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--d2ml-CFJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wapyc019v59k802e4cfw.png" alt="Downsides of Sass" width="800" height="444"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bypyLYZP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/elmght0kj3hrrp022vum.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bypyLYZP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/elmght0kj3hrrp022vum.png" alt="Another Angle" width="800" height="164"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lF4nDEiO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/992iv2ifnuw0al1vyd3s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lF4nDEiO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/992iv2ifnuw0al1vyd3s.png" alt="Conclusion" width="800" height="530"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re interested in learning more about what we’re up to, &lt;a href="https://www.speakeasyapi.dev/request-access"&gt;please reach out!&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>css</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
