Background image

In Depth: Speakeasy vs APIMatic

Nolan Sullivan

Nolan Sullivan

September 30, 2024

Featured blog post image

At Speakeasy, we create idiomatic SDKs in the most popular languages. Our generators follow principles that ensure we create SDKs that offer the best developer experience so that you can focus on building your API, and your developer-users can focus on delighting their users.

In this post, we'll compare TypeScript SDKs created by Speakeasy to those generated by APIMatic.

TL;DR? Here's what we found:

  • Ease of installation: The Speakeasy CLI has a straightforward installation process, especially for macOS users, with a single dependency-free binary. APIMatic, while also straightforward, requires Node.js and is installed using npm, which might add complexity for some users.
  • Code generation: In our test, the SDK generated by APIMatic via CLI had issues with the src/clientInterface.ts file. Although the APIMatic support team responded, the problem persisted in our Docker environment test. By contrast, Speakeasy generated a complete and error-free SDK on our first attempt.
  • Documentation and code structure: The Speakeasy-created SDK includes comprehensive documentation, and relies on Zod for runtime data parsing, making it easier for developers to understand and use. The structure and organization of the SDK were clear and intuitive.
  • Developer experience and flexibility: Speakeasy seems to place a significant emphasis on the developer experience, offering more customization options and a focus on the API-Developer Experience (DevEx). This could be particularly beneficial for teams looking for greater control over their SDKs.
  • Response to issues: While the APIMatic support team was responsive, the resolution provided did not address the issue effectively in our case. Since we encountered no issues creating an SDK with Speakeasy, there was no need to similarly test the Speakeasy support team's responsiveness.
  • Bundle sizes and browser compatibility: Even when including Zod for runtime type checking, bundles using Speakeasy's SDKs are smaller than those created with APIMatic SDKs. This could be beneficial for developers who need to keep their bundle sizes small. Speakeasy's SDKs are also compatible with modern browsers, which is essential for web developers.
Info Icon

NOTE

Speakeasy and APIMatic each have their strengths, but Speakeasy's ease of installation, reliable code generation, and developer-focused features give it the edge.

Of course, individual experiences may vary based on specific needs and use cases, so you might want to follow our process below to test-drive both tools.

Comparing Speakeasy and APIMatic

Before we get into the technical walkthrough, let's see whether each platform targets the languages your users require, offers features you want, and provides the support you can rely on.

SDK Generation Targets

At Speakeasy, we believe it is crucial to meet your users where they are by supporting SDKs in languages your users depend on. Anyone who has had to maintain custom SDK code because a vendor doesn't support their tech stack knows how frustrating this can be.

This table shows the current, as of September 2024, languages and platforms targeted by Speakeasy and APIMatic. These lists will change over time, so check the official documentation for the latest language support.

LanguageSpeakeasyAPIMatic
Python
TypeScript
Go✅ (Alpha)
C# (.NET)
PHP
Ruby
Java
Kotlin⚠ Java is Kotlin-compatible
Terraform provider
Swift
Unity
Postman Collection

We're always open to expanding our language support, but would only ever do this if we have the in-house experience to create idiomatic, best-in-class SDKs for a given language. Let us know (opens in a new tab) if you would like to suggest a language or platform to support.

SDK Features

The table below compares the current SDK features offered by Speakeasy and APIMatic as of September 2024. Both Speakeasy and APIMatic are under active development, so these features may change over time.

FeatureSpeakeasyAPIMatic
Union types
Discriminated union types⚠ non-OpenAPI standard
Server-sent events
Retries
Pagination
Async support
Streaming uploads
OAuth 2.0
Custom SDK naming
Customize SDK structure
Custom dependency injection

APIMatic lacks advanced SDK customization features, and we couldn't find any code or documentation related to pagination. These are features Speakeasy's users rely on.

Platform Features

Speakeasy's primary interface is an open-source, full-featured, and portable CLI. Developers use our CLI to experiment and iterate locally and to customize their CI/CD workflows.

APIMatic's CLI depends on Node.js and several packages. This makes it much less portable. In testing, we also found that it does not generate SDKs as reliably as the APIMatic web interface.

FeatureSpeakeasyAPIMatic
GitHub CI/CD
CLI
Web interface
Package publishing
OpenAPI linting
Documentation generation
Test generation
OpenAPI overlays
Change detection
Developer portal

Enterprise Support

Both Speakeasy and APIMatic offer support for Enterprise customers. This includes features like concierge onboarding, private Slack channels, and enterprise SLAs.

FeatureSpeakeasyAPIMatic
Concierge onboarding
Private Slack channel
Enterprise SLAs
User issues triage

Pricing

Speakeasy offers a free plan, while APIMatic offers a limited free trial.

PlanSpeakeasyAPIMatic
Free1 free Published SDK, 50 endpointsTrial only
Startup1 free + $250/mo/SDK, 50 endpoints eachN/A
Lite: StarterN/A$15/mo. 1 API, 10 endpoints, no team members.
Lite: BasicN/ACustom. 1 API, 20 endpoints, 2 team members.
BusinessN/ACustom. Up to 50 APIs, 100 endpoints each, 15 team members.
EnterpriseCustomCustom

Speakeasy's free plan is more generous than both Lite plans offered by APIMatic.

Speakeasy vs APIMatic Technical Walkthrough

Let's create SDKs with Speakeasy and APIMatic from a single API specification, to compare the output and customization features.

We've created an OpenAPI document that describes a fictional bookstore API. You can find the complete OpenAPI document in the example repository (opens in a new tab), but let's take a look at what's included.

Our bookstore OpenAPI document is compliant with OpenAPI 3.1, which is supported by both Speakeasy and APIMatic. We define a basic info section and add a single development server.

openapi.yaml

openapi: 3.1.0
info:
title: Bookstore API
description: API for a bookstore with categories Programming, Fantasy, and Sci-fi
version: 1.0.0
contact:
name: John Doe
email: john@example.com
url: https://example.com
license:
name: MIT
url: https://opensource.org/licenses/MIT
servers:
- url: http://127.0.0.1:4010
description: Local Prism server
tags:
- name: Books
description: Operations related to books
- name: Orders
description: Operations related to orders
security:
- apiKey: []
paths:
/books:
get:
summary: Get all books
operationId: getAllBooks
description: Returns a list of books
security:
- clientCredentials:
- books.read
tags:
- Books
responses:
"200":
description: A list of books
content:
application/json:
schema:
type: array
items:
oneOf:
- $ref: "#/components/schemas/ProgrammingBook"
- $ref: "#/components/schemas/FantasyBook"
- $ref: "#/components/schemas/SciFiBook"
discriminator:
propertyName: category
mapping:
Programming: "#/components/schemas/ProgrammingBook"
Fantasy: "#/components/schemas/FantasyBook"
Sci-fi: "#/components/schemas/SciFiBook"
examples:
example1:
summary: Programming book example
value:
- id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
cover_image: https://example.com/covers/cleancode.jpg
- id: 2
title: The Hobbit
description: A fantasy novel by J.R.R. Tolkien
price: 1599
category: Fantasy
author:
id: 2
name: J.R.R. Tolkien
photo: https://example.com/photos/tolkien.jpg
biography: John Ronald Reuel Tolkien was an English writer, poet, philologist, and academic...
cover_image: https://example.com/covers/thehobbit.jpg
post:
summary: Add a new book
operationId: addBook
description: Adds a new book to the bookstore
security:
- apiKey: []
tags:
- Books
requestBody:
description: Book object to be added
required: true
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/ProgrammingBook"
- $ref: "#/components/schemas/FantasyBook"
- $ref: "#/components/schemas/SciFiBook"
discriminator:
propertyName: category
mapping:
Programming: "#/components/schemas/ProgrammingBook"
Fantasy: "#/components/schemas/FantasyBook"
Sci-fi: "#/components/schemas/SciFiBook"
examples:
example:
summary: Example book
value:
title: New Sci-Fi Book
description: A new Sci-Fi book description
price: 1999
category: Sci-fi
author:
name: New Author
photo: https://example.com/photos/newauthor.jpg
biography: New Author is an upcoming writer in the Sci-Fi genre...
cover_image: https://example.com/covers/newbook.jpg
responses:
"201":
description: Book created successfully
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/ProgrammingBook"
- $ref: "#/components/schemas/FantasyBook"
- $ref: "#/components/schemas/SciFiBook"
discriminator:
propertyName: category
mapping:
Programming: "#/components/schemas/ProgrammingBook"
Fantasy: "#/components/schemas/FantasyBook"
Sci-fi: "#/components/schemas/SciFiBook"
examples:
example:
summary: Example book
value:
id: 3
title: New Sci-Fi Book
description: A new Sci-Fi book description
price: 1999
category: Sci-fi
author:
name: New Author
photo: https://example.com/photos/newauthor.jpg
biography: New Author is an upcoming writer in the Sci-Fi genre...
cover_image: https://example.com/covers/newbook.jpg
/books/{bookId}:
get:
summary: Get a book by ID
operationId: getBookById
description: Returns a single book
security: []
tags:
- Books
parameters:
- name: bookId
description: ID of the book to return
in: path
required: true
schema:
type: integer
example: 1
responses:
"200":
description: A single book
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/ProgrammingBook"
- $ref: "#/components/schemas/FantasyBook"
- $ref: "#/components/schemas/SciFiBook"
discriminator:
propertyName: category
mapping:
Programming: "#/components/schemas/ProgrammingBook"
Fantasy: "#/components/schemas/FantasyBook"
Sci-fi: "#/components/schemas/SciFiBook"
examples:
example1:
summary: Programming book example
value:
id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
cover_image: https://example.com/covers/cleancode.jpg
example2:
summary: Fantasy book example
value:
id: 2
title: The Hobbit
description: A fantasy novel by J.R.R. Tolkien
price: 1599
category: Fantasy
author:
id: 2
name: J.R.R. Tolkien
photo: https://example.com/photos/tolkien.jpg
biography: John Ronald Reuel Tolkien was an English writer, poet, philologist, and academic...
cover_image: https://example.com/covers/thehobbit.jpg
/books/{bookId}/cover:
put:
summary: Update a book cover by ID
operationId: updateBookCoverById
description: Updates a single book cover
security:
- apiKey: []
tags:
- Books
parameters:
- name: bookId
description: ID of the book to update
in: path
required: true
schema:
type: integer
example: 1
requestBody:
description: Book cover
required: true
content:
multipart/form-data:
schema:
type: object
properties:
cover:
type: string
format: binary
responses:
"200":
description: Book cover updated successfully
/orders:
get:
summary: Get all orders
operationId: getAllOrders
description: Returns a list of orders
tags:
- Orders
security:
- clientCredentials:
- orders.read
responses:
"200":
description: A list of orders
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Order"
examples:
example:
summary: Example response
value:
- id: 1
date: 2023-05-17T09:24:00Z
status: pending
user:
id: 1
email: test@example.com
name: John Doe
products:
- id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
cover_image: https://example.com/covers/cleancode.jpg
post:
summary: Create a new order
operationId: createOrder
description: Creates a new order
tags:
- Orders
security:
- clientCredentials:
- orders.write
requestBody:
description: Order object to be created
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/NewOrder"
examples:
example:
summary: Example order
value:
user: 1
products:
- 1
- 3
status: pending
responses:
"201":
description: Order created successfully
content:
application/json:
schema:
$ref: "#/components/schemas/Order"
/orders/{orderId}:
get:
summary: Get an order by ID
operationId: getOrderById
description: Returns a single order
tags:
- Orders
security:
- clientCredentials:
- orders.read
parameters:
- name: orderId
description: ID of the order to return
in: path
required: true
schema:
type: integer
example: 1
responses:
"200":
description: A single order
content:
application/json:
schema:
$ref: "#/components/schemas/Order"
examples:
example:
summary: Example response
value:
id: 1
date: 2023-05-17T09:24:00Z
status: pending
user:
id: 1
email: user@example.com
products:
- id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
cover_image: https://example.com/covers/cleancode.jpg
/orderstream:
get:
summary: Get a stream of orders
operationId: getOrderStream
description: Returns a stream of orders
tags:
- Orders
security:
- apiKey: []
responses:
"200":
description: A stream of orders
content:
text/event-stream:
schema:
$ref: "#/components/schemas/OrderStreamMessage"
components:
schemas:
ProductId:
type: integer
example: 1
Book:
type: object
required:
- title
- description
- price
- category
- author
properties:
id:
$ref: "#/components/schemas/ProductId"
title:
type: string
example: Clean Code
description:
type: string
example: A Handbook of Agile Software Craftsmanship
price:
type: integer
description: Price in USD cents
example: 2999
category:
type: string
enum:
- Sci-fi
- Fantasy
- Programming
example: Programming
author:
$ref: "#/components/schemas/Author"
cover_image:
type: string
example: https://example.com/covers/cleancode.jpg
example:
id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
category: Programming
SciFiBook:
allOf:
- $ref: "#/components/schemas/Book"
- type: object
properties:
category:
type: string
const: Sci-fi
example: Sci-fi
example:
id: 3
title: New Sci-Fi Book
description: A new Sci-Fi book description
price: 1999
category: Sci-fi
cover_image: https://example.com/covers/newbook.jpg
author:
name: New Author
photo: https://example.com/photos/newauthor.jpg
biography: New Author is an upcoming writer in the Sci-Fi genre...
FantasyBook:
allOf:
- $ref: "#/components/schemas/Book"
- type: object
properties:
category:
type: string
const: Fantasy
example: Fantasy
example:
id: 2
title: The Hobbit
description: A fantasy novel by J.R.R. Tolkien
price: 1599
category: Fantasy
ProgrammingBook:
allOf:
- $ref: "#/components/schemas/Book"
- type: object
properties:
category:
type: string
const: Programming
example: Programming
example:
id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
AuthorId:
type: integer
example: 1
Author:
type: object
properties:
id:
$ref: "#/components/schemas/AuthorId"
name:
type: string
example: Robert C. Martin
photo:
type: string
example: https://example.com/photos/robert.jpg
biography:
type: string
example: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
anyOf:
- required:
- name
title: Author with name
- required:
- id
title: Author with ID
example:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
OrderId:
type: integer
example: 1
Order:
type: object
required:
- id
- date
- status
- user
- products
properties:
id:
$ref: "#/components/schemas/OrderId"
date:
type: string
format: date-time
example: 2023-05-17T09:24:00Z
status:
type: string
enum:
- pending
- shipped
- delivered
example: pending
user:
$ref: "#/components/schemas/User"
products:
type: array
items:
oneOf:
- $ref: "#/components/schemas/FantasyBook"
- $ref: "#/components/schemas/ProgrammingBook"
- $ref: "#/components/schemas/SciFiBook"
discriminator:
propertyName: category
mapping:
Programming: "#/components/schemas/ProgrammingBook"
Fantasy: "#/components/schemas/FantasyBook"
Sci-fi: "#/components/schemas/SciFiBook"
example:
id: 1
date: 2023-05-17T09:24:00Z
status: pending
user:
id: 1
email: user@example.com
name: John Doe
products:
- id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
- id: 2
title: The Hobbit
description: A fantasy novel by J.R.R. Tolkien
price: 1599
category: Fantasy
author:
id: 2
name: J.R.R. Tolkien
photo: https://example.com/photos/tolkien.jpg
biography: John Ronald Reuel Tolkien was an English writer, poet, philologist, and academic...
OrderStreamMessage:
type: object
description: A message in the order stream
required:
- id
- event
- data
properties:
id:
type: string
title: Message ID
format: UUID
example: 123e4567-e89b-12d3-a456-426614174000
event:
title: Event type
type: string
example: order_created
data:
$ref: "#/components/schemas/Order"
NewOrder:
type: object
required:
- user
- products
- status
properties:
user:
$ref: "#/components/schemas/UserId"
products:
type: array
items:
$ref: "#/components/schemas/ProductId"
example:
user: 1
products:
- 1
- 3
- 5
status: pending
UserId:
type: integer
example: 1
User:
type: object
properties:
id:
$ref: "#/components/schemas/UserId"
email:
type: string
example: user@example.com
name:
type: string
example: John Doe
example:
id: 1
email: user@example.com
name: John Doe
securitySchemes:
apiKey:
type: apiKey
in: header
name: X-API-Key
clientCredentials:
type: oauth2
flows:
clientCredentials:
tokenUrl: https://api.bookstore.com/oauth/token
refreshUrl: https://api.bookstore.com/oauth/refresh
scopes: {}

Here we define two tags to organize our operations with: Books and Orders.

openapi.yaml

openapi: 3.1.0
info:
title: Bookstore API
description: API for a bookstore with categories Programming, Fantasy, and Sci-fi
version: 1.0.0
contact:
name: John Doe
email: john@example.com
url: https://example.com
license:
name: MIT
url: https://opensource.org/licenses/MIT
servers:
- url: http://127.0.0.1:4010
description: Local Prism server
tags:
- name: Books
description: Operations related to books
- name: Orders
description: Operations related to orders
security:
- apiKey: []
paths:
/books:
get:
summary: Get all books
operationId: getAllBooks
description: Returns a list of books
security:
- clientCredentials:
- books.read
tags:
- Books
responses:
"200":
description: A list of books
content:
application/json:
schema:
type: array
items:
oneOf:
- $ref: "#/components/schemas/ProgrammingBook"
- $ref: "#/components/schemas/FantasyBook"
- $ref: "#/components/schemas/SciFiBook"
discriminator:
propertyName: category
mapping:
Programming: "#/components/schemas/ProgrammingBook"
Fantasy: "#/components/schemas/FantasyBook"
Sci-fi: "#/components/schemas/SciFiBook"
examples:
example1:
summary: Programming book example
value:
- id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
cover_image: https://example.com/covers/cleancode.jpg
- id: 2
title: The Hobbit
description: A fantasy novel by J.R.R. Tolkien
price: 1599
category: Fantasy
author:
id: 2
name: J.R.R. Tolkien
photo: https://example.com/photos/tolkien.jpg
biography: John Ronald Reuel Tolkien was an English writer, poet, philologist, and academic...
cover_image: https://example.com/covers/thehobbit.jpg
post:
summary: Add a new book
operationId: addBook
description: Adds a new book to the bookstore
security:
- apiKey: []
tags:
- Books
requestBody:
description: Book object to be added
required: true
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/ProgrammingBook"
- $ref: "#/components/schemas/FantasyBook"
- $ref: "#/components/schemas/SciFiBook"
discriminator:
propertyName: category
mapping:
Programming: "#/components/schemas/ProgrammingBook"
Fantasy: "#/components/schemas/FantasyBook"
Sci-fi: "#/components/schemas/SciFiBook"
examples:
example:
summary: Example book
value:
title: New Sci-Fi Book
description: A new Sci-Fi book description
price: 1999
category: Sci-fi
author:
name: New Author
photo: https://example.com/photos/newauthor.jpg
biography: New Author is an upcoming writer in the Sci-Fi genre...
cover_image: https://example.com/covers/newbook.jpg
responses:
"201":
description: Book created successfully
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/ProgrammingBook"
- $ref: "#/components/schemas/FantasyBook"
- $ref: "#/components/schemas/SciFiBook"
discriminator:
propertyName: category
mapping:
Programming: "#/components/schemas/ProgrammingBook"
Fantasy: "#/components/schemas/FantasyBook"
Sci-fi: "#/components/schemas/SciFiBook"
examples:
example:
summary: Example book
value:
id: 3
title: New Sci-Fi Book
description: A new Sci-Fi book description
price: 1999
category: Sci-fi
author:
name: New Author
photo: https://example.com/photos/newauthor.jpg
biography: New Author is an upcoming writer in the Sci-Fi genre...
cover_image: https://example.com/covers/newbook.jpg
/books/{bookId}:
get:
summary: Get a book by ID
operationId: getBookById
description: Returns a single book
security: []
tags:
- Books
parameters:
- name: bookId
description: ID of the book to return
in: path
required: true
schema:
type: integer
example: 1
responses:
"200":
description: A single book
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/ProgrammingBook"
- $ref: "#/components/schemas/FantasyBook"
- $ref: "#/components/schemas/SciFiBook"
discriminator:
propertyName: category
mapping:
Programming: "#/components/schemas/ProgrammingBook"
Fantasy: "#/components/schemas/FantasyBook"
Sci-fi: "#/components/schemas/SciFiBook"
examples:
example1:
summary: Programming book example
value:
id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
cover_image: https://example.com/covers/cleancode.jpg
example2:
summary: Fantasy book example
value:
id: 2
title: The Hobbit
description: A fantasy novel by J.R.R. Tolkien
price: 1599
category: Fantasy
author:
id: 2
name: J.R.R. Tolkien
photo: https://example.com/photos/tolkien.jpg
biography: John Ronald Reuel Tolkien was an English writer, poet, philologist, and academic...
cover_image: https://example.com/covers/thehobbit.jpg
/books/{bookId}/cover:
put:
summary: Update a book cover by ID
operationId: updateBookCoverById
description: Updates a single book cover
security:
- apiKey: []
tags:
- Books
parameters:
- name: bookId
description: ID of the book to update
in: path
required: true
schema:
type: integer
example: 1
requestBody:
description: Book cover
required: true
content:
multipart/form-data:
schema:
type: object
properties:
cover:
type: string
format: binary
responses:
"200":
description: Book cover updated successfully
/orders:
get:
summary: Get all orders
operationId: getAllOrders
description: Returns a list of orders
tags:
- Orders
security:
- clientCredentials:
- orders.read
responses:
"200":
description: A list of orders
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Order"
examples:
example:
summary: Example response
value:
- id: 1
date: 2023-05-17T09:24:00Z
status: pending
user:
id: 1
email: test@example.com
name: John Doe
products:
- id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
cover_image: https://example.com/covers/cleancode.jpg
post:
summary: Create a new order
operationId: createOrder
description: Creates a new order
tags:
- Orders
security:
- clientCredentials:
- orders.write
requestBody:
description: Order object to be created
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/NewOrder"
examples:
example:
summary: Example order
value:
user: 1
products:
- 1
- 3
status: pending
responses:
"201":
description: Order created successfully
content:
application/json:
schema:
$ref: "#/components/schemas/Order"
/orders/{orderId}:
get:
summary: Get an order by ID
operationId: getOrderById
description: Returns a single order
tags:
- Orders
security:
- clientCredentials:
- orders.read
parameters:
- name: orderId
description: ID of the order to return
in: path
required: true
schema:
type: integer
example: 1
responses:
"200":
description: A single order
content:
application/json:
schema:
$ref: "#/components/schemas/Order"
examples:
example:
summary: Example response
value:
id: 1
date: 2023-05-17T09:24:00Z
status: pending
user:
id: 1
email: user@example.com
products:
- id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
cover_image: https://example.com/covers/cleancode.jpg
/orderstream:
get:
summary: Get a stream of orders
operationId: getOrderStream
description: Returns a stream of orders
tags:
- Orders
security:
- apiKey: []
responses:
"200":
description: A stream of orders
content:
text/event-stream:
schema:
$ref: "#/components/schemas/OrderStreamMessage"
components:
schemas:
ProductId:
type: integer
example: 1
Book:
type: object
required:
- title
- description
- price
- category
- author
properties:
id:
$ref: "#/components/schemas/ProductId"
title:
type: string
example: Clean Code
description:
type: string
example: A Handbook of Agile Software Craftsmanship
price:
type: integer
description: Price in USD cents
example: 2999
category:
type: string
enum:
- Sci-fi
- Fantasy
- Programming
example: Programming
author:
$ref: "#/components/schemas/Author"
cover_image:
type: string
example: https://example.com/covers/cleancode.jpg
example:
id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
category: Programming
SciFiBook:
allOf:
- $ref: "#/components/schemas/Book"
- type: object
properties:
category:
type: string
const: Sci-fi
example: Sci-fi
example:
id: 3
title: New Sci-Fi Book
description: A new Sci-Fi book description
price: 1999
category: Sci-fi
cover_image: https://example.com/covers/newbook.jpg
author:
name: New Author
photo: https://example.com/photos/newauthor.jpg
biography: New Author is an upcoming writer in the Sci-Fi genre...
FantasyBook:
allOf:
- $ref: "#/components/schemas/Book"
- type: object
properties:
category:
type: string
const: Fantasy
example: Fantasy
example:
id: 2
title: The Hobbit
description: A fantasy novel by J.R.R. Tolkien
price: 1599
category: Fantasy
ProgrammingBook:
allOf:
- $ref: "#/components/schemas/Book"
- type: object
properties:
category:
type: string
const: Programming
example: Programming
example:
id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
AuthorId:
type: integer
example: 1
Author:
type: object
properties:
id:
$ref: "#/components/schemas/AuthorId"
name:
type: string
example: Robert C. Martin
photo:
type: string
example: https://example.com/photos/robert.jpg
biography:
type: string
example: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
anyOf:
- required:
- name
title: Author with name
- required:
- id
title: Author with ID
example:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
OrderId:
type: integer
example: 1
Order:
type: object
required:
- id
- date
- status
- user
- products
properties:
id:
$ref: "#/components/schemas/OrderId"
date:
type: string
format: date-time
example: 2023-05-17T09:24:00Z
status:
type: string
enum:
- pending
- shipped
- delivered
example: pending
user:
$ref: "#/components/schemas/User"
products:
type: array
items:
oneOf:
- $ref: "#/components/schemas/FantasyBook"
- $ref: "#/components/schemas/ProgrammingBook"
- $ref: "#/components/schemas/SciFiBook"
discriminator:
propertyName: category
mapping:
Programming: "#/components/schemas/ProgrammingBook"
Fantasy: "#/components/schemas/FantasyBook"
Sci-fi: "#/components/schemas/SciFiBook"
example:
id: 1
date: 2023-05-17T09:24:00Z
status: pending
user:
id: 1
email: user@example.com
name: John Doe
products:
- id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
- id: 2
title: The Hobbit
description: A fantasy novel by J.R.R. Tolkien
price: 1599
category: Fantasy
author:
id: 2
name: J.R.R. Tolkien
photo: https://example.com/photos/tolkien.jpg
biography: John Ronald Reuel Tolkien was an English writer, poet, philologist, and academic...
OrderStreamMessage:
type: object
description: A message in the order stream
required:
- id
- event
- data
properties:
id:
type: string
title: Message ID
format: UUID
example: 123e4567-e89b-12d3-a456-426614174000
event:
title: Event type
type: string
example: order_created
data:
$ref: "#/components/schemas/Order"
NewOrder:
type: object
required:
- user
- products
- status
properties:
user:
$ref: "#/components/schemas/UserId"
products:
type: array
items:
$ref: "#/components/schemas/ProductId"
example:
user: 1
products:
- 1
- 3
- 5
status: pending
UserId:
type: integer
example: 1
User:
type: object
properties:
id:
$ref: "#/components/schemas/UserId"
email:
type: string
example: user@example.com
name:
type: string
example: John Doe
example:
id: 1
email: user@example.com
name: John Doe
securitySchemes:
apiKey:
type: apiKey
in: header
name: X-API-Key
clientCredentials:
type: oauth2
flows:
clientCredentials:
tokenUrl: https://api.bookstore.com/oauth/token
refreshUrl: https://api.bookstore.com/oauth/refresh
scopes: {}

We define one global authentication method, apiKey.

openapi.yaml

openapi: 3.1.0
info:
title: Bookstore API
description: API for a bookstore with categories Programming, Fantasy, and Sci-fi
version: 1.0.0
contact:
name: John Doe
email: john@example.com
url: https://example.com
license:
name: MIT
url: https://opensource.org/licenses/MIT
servers:
- url: http://127.0.0.1:4010
description: Local Prism server
tags:
- name: Books
description: Operations related to books
- name: Orders
description: Operations related to orders
security:
- apiKey: []
paths:
/books:
get:
summary: Get all books
operationId: getAllBooks
description: Returns a list of books
security:
- clientCredentials:
- books.read
tags:
- Books
responses:
"200":
description: A list of books
content:
application/json:
schema:
type: array
items:
oneOf:
- $ref: "#/components/schemas/ProgrammingBook"
- $ref: "#/components/schemas/FantasyBook"
- $ref: "#/components/schemas/SciFiBook"
discriminator:
propertyName: category
mapping:
Programming: "#/components/schemas/ProgrammingBook"
Fantasy: "#/components/schemas/FantasyBook"
Sci-fi: "#/components/schemas/SciFiBook"
examples:
example1:
summary: Programming book example
value:
- id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
cover_image: https://example.com/covers/cleancode.jpg
- id: 2
title: The Hobbit
description: A fantasy novel by J.R.R. Tolkien
price: 1599
category: Fantasy
author:
id: 2
name: J.R.R. Tolkien
photo: https://example.com/photos/tolkien.jpg
biography: John Ronald Reuel Tolkien was an English writer, poet, philologist, and academic...
cover_image: https://example.com/covers/thehobbit.jpg
post:
summary: Add a new book
operationId: addBook
description: Adds a new book to the bookstore
security:
- apiKey: []
tags:
- Books
requestBody:
description: Book object to be added
required: true
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/ProgrammingBook"
- $ref: "#/components/schemas/FantasyBook"
- $ref: "#/components/schemas/SciFiBook"
discriminator:
propertyName: category
mapping:
Programming: "#/components/schemas/ProgrammingBook"
Fantasy: "#/components/schemas/FantasyBook"
Sci-fi: "#/components/schemas/SciFiBook"
examples:
example:
summary: Example book
value:
title: New Sci-Fi Book
description: A new Sci-Fi book description
price: 1999
category: Sci-fi
author:
name: New Author
photo: https://example.com/photos/newauthor.jpg
biography: New Author is an upcoming writer in the Sci-Fi genre...
cover_image: https://example.com/covers/newbook.jpg
responses:
"201":
description: Book created successfully
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/ProgrammingBook"
- $ref: "#/components/schemas/FantasyBook"
- $ref: "#/components/schemas/SciFiBook"
discriminator:
propertyName: category
mapping:
Programming: "#/components/schemas/ProgrammingBook"
Fantasy: "#/components/schemas/FantasyBook"
Sci-fi: "#/components/schemas/SciFiBook"
examples:
example:
summary: Example book
value:
id: 3
title: New Sci-Fi Book
description: A new Sci-Fi book description
price: 1999
category: Sci-fi
author:
name: New Author
photo: https://example.com/photos/newauthor.jpg
biography: New Author is an upcoming writer in the Sci-Fi genre...
cover_image: https://example.com/covers/newbook.jpg
/books/{bookId}:
get:
summary: Get a book by ID
operationId: getBookById
description: Returns a single book
security: []
tags:
- Books
parameters:
- name: bookId
description: ID of the book to return
in: path
required: true
schema:
type: integer
example: 1
responses:
"200":
description: A single book
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/ProgrammingBook"
- $ref: "#/components/schemas/FantasyBook"
- $ref: "#/components/schemas/SciFiBook"
discriminator:
propertyName: category
mapping:
Programming: "#/components/schemas/ProgrammingBook"
Fantasy: "#/components/schemas/FantasyBook"
Sci-fi: "#/components/schemas/SciFiBook"
examples:
example1:
summary: Programming book example
value:
id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
cover_image: https://example.com/covers/cleancode.jpg
example2:
summary: Fantasy book example
value:
id: 2
title: The Hobbit
description: A fantasy novel by J.R.R. Tolkien
price: 1599
category: Fantasy
author:
id: 2
name: J.R.R. Tolkien
photo: https://example.com/photos/tolkien.jpg
biography: John Ronald Reuel Tolkien was an English writer, poet, philologist, and academic...
cover_image: https://example.com/covers/thehobbit.jpg
/books/{bookId}/cover:
put:
summary: Update a book cover by ID
operationId: updateBookCoverById
description: Updates a single book cover
security:
- apiKey: []
tags:
- Books
parameters:
- name: bookId
description: ID of the book to update
in: path
required: true
schema:
type: integer
example: 1
requestBody:
description: Book cover
required: true
content:
multipart/form-data:
schema:
type: object
properties:
cover:
type: string
format: binary
responses:
"200":
description: Book cover updated successfully
/orders:
get:
summary: Get all orders
operationId: getAllOrders
description: Returns a list of orders
tags:
- Orders
security:
- clientCredentials:
- orders.read
responses:
"200":
description: A list of orders
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Order"
examples:
example:
summary: Example response
value:
- id: 1
date: 2023-05-17T09:24:00Z
status: pending
user:
id: 1
email: test@example.com
name: John Doe
products:
- id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
cover_image: https://example.com/covers/cleancode.jpg
post:
summary: Create a new order
operationId: createOrder
description: Creates a new order
tags:
- Orders
security:
- clientCredentials:
- orders.write
requestBody:
description: Order object to be created
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/NewOrder"
examples:
example:
summary: Example order
value:
user: 1
products:
- 1
- 3
status: pending
responses:
"201":
description: Order created successfully
content:
application/json:
schema:
$ref: "#/components/schemas/Order"
/orders/{orderId}:
get:
summary: Get an order by ID
operationId: getOrderById
description: Returns a single order
tags:
- Orders
security:
- clientCredentials:
- orders.read
parameters:
- name: orderId
description: ID of the order to return
in: path
required: true
schema:
type: integer
example: 1
responses:
"200":
description: A single order
content:
application/json:
schema:
$ref: "#/components/schemas/Order"
examples:
example:
summary: Example response
value:
id: 1
date: 2023-05-17T09:24:00Z
status: pending
user:
id: 1
email: user@example.com
products:
- id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
cover_image: https://example.com/covers/cleancode.jpg
/orderstream:
get:
summary: Get a stream of orders
operationId: getOrderStream
description: Returns a stream of orders
tags:
- Orders
security:
- apiKey: []
responses:
"200":
description: A stream of orders
content:
text/event-stream:
schema:
$ref: "#/components/schemas/OrderStreamMessage"
components:
schemas:
ProductId:
type: integer
example: 1
Book:
type: object
required:
- title
- description
- price
- category
- author
properties:
id:
$ref: "#/components/schemas/ProductId"
title:
type: string
example: Clean Code
description:
type: string
example: A Handbook of Agile Software Craftsmanship
price:
type: integer
description: Price in USD cents
example: 2999
category:
type: string
enum:
- Sci-fi
- Fantasy
- Programming
example: Programming
author:
$ref: "#/components/schemas/Author"
cover_image:
type: string
example: https://example.com/covers/cleancode.jpg
example:
id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
category: Programming
SciFiBook:
allOf:
- $ref: "#/components/schemas/Book"
- type: object
properties:
category:
type: string
const: Sci-fi
example: Sci-fi
example:
id: 3
title: New Sci-Fi Book
description: A new Sci-Fi book description
price: 1999
category: Sci-fi
cover_image: https://example.com/covers/newbook.jpg
author:
name: New Author
photo: https://example.com/photos/newauthor.jpg
biography: New Author is an upcoming writer in the Sci-Fi genre...
FantasyBook:
allOf:
- $ref: "#/components/schemas/Book"
- type: object
properties:
category:
type: string
const: Fantasy
example: Fantasy
example:
id: 2
title: The Hobbit
description: A fantasy novel by J.R.R. Tolkien
price: 1599
category: Fantasy
ProgrammingBook:
allOf:
- $ref: "#/components/schemas/Book"
- type: object
properties:
category:
type: string
const: Programming
example: Programming
example:
id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
AuthorId:
type: integer
example: 1
Author:
type: object
properties:
id:
$ref: "#/components/schemas/AuthorId"
name:
type: string
example: Robert C. Martin
photo:
type: string
example: https://example.com/photos/robert.jpg
biography:
type: string
example: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
anyOf:
- required:
- name
title: Author with name
- required:
- id
title: Author with ID
example:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
OrderId:
type: integer
example: 1
Order:
type: object
required:
- id
- date
- status
- user
- products
properties:
id:
$ref: "#/components/schemas/OrderId"
date:
type: string
format: date-time
example: 2023-05-17T09:24:00Z
status:
type: string
enum:
- pending
- shipped
- delivered
example: pending
user:
$ref: "#/components/schemas/User"
products:
type: array
items:
oneOf:
- $ref: "#/components/schemas/FantasyBook"
- $ref: "#/components/schemas/ProgrammingBook"
- $ref: "#/components/schemas/SciFiBook"
discriminator:
propertyName: category
mapping:
Programming: "#/components/schemas/ProgrammingBook"
Fantasy: "#/components/schemas/FantasyBook"
Sci-fi: "#/components/schemas/SciFiBook"
example:
id: 1
date: 2023-05-17T09:24:00Z
status: pending
user:
id: 1
email: user@example.com
name: John Doe
products:
- id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
- id: 2
title: The Hobbit
description: A fantasy novel by J.R.R. Tolkien
price: 1599
category: Fantasy
author:
id: 2
name: J.R.R. Tolkien
photo: https://example.com/photos/tolkien.jpg
biography: John Ronald Reuel Tolkien was an English writer, poet, philologist, and academic...
OrderStreamMessage:
type: object
description: A message in the order stream
required:
- id
- event
- data
properties:
id:
type: string
title: Message ID
format: UUID
example: 123e4567-e89b-12d3-a456-426614174000
event:
title: Event type
type: string
example: order_created
data:
$ref: "#/components/schemas/Order"
NewOrder:
type: object
required:
- user
- products
- status
properties:
user:
$ref: "#/components/schemas/UserId"
products:
type: array
items:
$ref: "#/components/schemas/ProductId"
example:
user: 1
products:
- 1
- 3
- 5
status: pending
UserId:
type: integer
example: 1
User:
type: object
properties:
id:
$ref: "#/components/schemas/UserId"
email:
type: string
example: user@example.com
name:
type: string
example: John Doe
example:
id: 1
email: user@example.com
name: John Doe
securitySchemes:
apiKey:
type: apiKey
in: header
name: X-API-Key
clientCredentials:
type: oauth2
flows:
clientCredentials:
tokenUrl: https://api.bookstore.com/oauth/token
refreshUrl: https://api.bookstore.com/oauth/refresh
scopes: {}

Let's examine the operations we'll need an SDK for, starting with getAllBooks.

This operation takes no input.

openapi.yaml

paths:
/books:
get:
summary: Get all books
operationId: getAllBooks
description: Returns a list of books
security:
- clientCredentials:
- books.read
tags:
- Books
responses:
"200":
description: A list of books
content:
application/json:
schema:
type: array
items:
oneOf:
- $ref: "#/components/schemas/ProgrammingBook"
- $ref: "#/components/schemas/FantasyBook"
- $ref: "#/components/schemas/SciFiBook"
discriminator:
propertyName: category
mapping:
Programming: "#/components/schemas/ProgrammingBook"
Fantasy: "#/components/schemas/FantasyBook"
Sci-fi: "#/components/schemas/SciFiBook"
examples:
example1:
summary: Programming book example
value:
- id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
cover_image: https://example.com/covers/cleancode.jpg
- id: 2
title: The Hobbit
description: A fantasy novel by J.R.R. Tolkien
price: 1599
category: Fantasy
author:
id: 2
name: J.R.R. Tolkien
photo: https://example.com/photos/tolkien.jpg
biography: John Ronald Reuel Tolkien was an English writer, poet, philologist, and academic...
cover_image: https://example.com/covers/thehobbit.jpg

What makes this operation interesting is that it returns an array of objects of three types: ProgrammingBook, FantasyBook, and SciFiBook. Each object's type is determined by the book's category.

This example allows us to test how our SDK generators handle discriminated unions in OpenAPI.

openapi.yaml

paths:
/books:
get:
summary: Get all books
operationId: getAllBooks
description: Returns a list of books
security:
- clientCredentials:
- books.read
tags:
- Books
responses:
"200":
description: A list of books
content:
application/json:
schema:
type: array
items:
oneOf:
- $ref: "#/components/schemas/ProgrammingBook"
- $ref: "#/components/schemas/FantasyBook"
- $ref: "#/components/schemas/SciFiBook"
discriminator:
propertyName: category
mapping:
Programming: "#/components/schemas/ProgrammingBook"
Fantasy: "#/components/schemas/FantasyBook"
Sci-fi: "#/components/schemas/SciFiBook"
examples:
example1:
summary: Programming book example
value:
- id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
cover_image: https://example.com/covers/cleancode.jpg
- id: 2
title: The Hobbit
description: A fantasy novel by J.R.R. Tolkien
price: 1599
category: Fantasy
author:
id: 2
name: J.R.R. Tolkien
photo: https://example.com/photos/tolkien.jpg
biography: John Ronald Reuel Tolkien was an English writer, poet, philologist, and academic...
cover_image: https://example.com/covers/thehobbit.jpg

Next up, we have an operation that adds a book to the database, called addBook.

This operation takes one object of type ProgrammingBook, FantasyBook, or SciFiBook as input.

openapi.yaml

post:
summary: Add a new book
operationId: addBook
description: Adds a new book to the bookstore
security:
- apiKey: []
tags:
- Books
requestBody:
description: Book object to be added
required: true
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/ProgrammingBook"
- $ref: "#/components/schemas/FantasyBook"
- $ref: "#/components/schemas/SciFiBook"
discriminator:
propertyName: category
mapping:
Programming: "#/components/schemas/ProgrammingBook"
Fantasy: "#/components/schemas/FantasyBook"
Sci-fi: "#/components/schemas/SciFiBook"
examples:
example:
summary: Example book
value:
title: New Sci-Fi Book
description: A new Sci-Fi book description
price: 1999
category: Sci-fi
author:
name: New Author
photo: https://example.com/photos/newauthor.jpg
biography: New Author is an upcoming writer in the Sci-Fi genre...
cover_image: https://example.com/covers/newbook.jpg
responses:
"201":
description: Book created successfully
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/ProgrammingBook"
- $ref: "#/components/schemas/FantasyBook"
- $ref: "#/components/schemas/SciFiBook"
discriminator:
propertyName: category
mapping:
Programming: "#/components/schemas/ProgrammingBook"
Fantasy: "#/components/schemas/FantasyBook"
Sci-fi: "#/components/schemas/SciFiBook"
examples:
example:
summary: Example book
value:
id: 3
title: New Sci-Fi Book
description: A new Sci-Fi book description
price: 1999
category: Sci-fi
author:
name: New Author
photo: https://example.com/photos/newauthor.jpg
biography: New Author is an upcoming writer in the Sci-Fi genre...
cover_image: https://example.com/covers/newbook.jpg

Our next book-related operation, updateBookCoverById, takes a book ID as a path variable, and an image as a binary payload.

We include this operation to test how our SDK generators handle binary payloads.

openapi.yaml

/books/{bookId}/cover:
put:
summary: Update a book cover by ID
operationId: updateBookCoverById
description: Updates a single book cover
security:
- apiKey: []
tags:
- Books
parameters:
- name: bookId
description: ID of the book to update
in: path
required: true
schema:
type: integer
example: 1
requestBody:
description: Book cover
required: true
content:
multipart/form-data:
schema:
type: object
properties:
cover:
type: string
format: binary

Our final book-related operation, getBookById, takes a book ID as a path variable, and returns one of our book objects.

openapi.yaml

/books/{bookId}:
get:
summary: Get a book by ID
operationId: getBookById
description: Returns a single book
security: []
tags:
- Books
parameters:
- name: bookId
description: ID of the book to return
in: path
required: true
schema:
type: integer
example: 1
responses:
"200":
description: A single book
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/ProgrammingBook"
- $ref: "#/components/schemas/FantasyBook"
- $ref: "#/components/schemas/SciFiBook"
discriminator:
propertyName: category
mapping:
Programming: "#/components/schemas/ProgrammingBook"
Fantasy: "#/components/schemas/FantasyBook"
Sci-fi: "#/components/schemas/SciFiBook"
examples:
example1:
summary: Programming book example
value:
id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
cover_image: https://example.com/covers/cleancode.jpg
example2:
summary: Fantasy book example
value:
id: 2
title: The Hobbit
description: A fantasy novel by J.R.R. Tolkien
price: 1599
category: Fantasy
author:
id: 2
name: J.R.R. Tolkien
photo: https://example.com/photos/tolkien.jpg
biography: John Ronald Reuel Tolkien was an English writer, poet, philologist, and academic...
cover_image: https://example.com/covers/thehobbit.jpg

Next up, we have an operation that returns a list of all orders in the database, called getAllOrders.

This operation returns an array of Order objects, so that we can test an array of nested objects.

openapi.yaml

/orders:
get:
summary: Get all orders
operationId: getAllOrders
description: Returns a list of orders
tags:
- Orders
security:
- clientCredentials:
- orders.read
responses:
"200":
description: A list of orders
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Order"
examples:
example:
summary: Example response
value:
- id: 1
date: 2023-05-17T09:24:00Z
status: pending
user:
id: 1
email: test@example.com
name: John Doe
products:
- id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
cover_image: https://example.com/covers/cleancode.jpg

Our next order-related operation, createOrder, takes an object of type NewOrder as input, and returns an object of type Order.

We include this one to test how our SDK generators help users avoid common mistakes, like passing the wrong type to an operation.

openapi.yaml

post:
summary: Create a new order
operationId: createOrder
description: Creates a new order
tags:
- Orders
security:
- clientCredentials:
- orders.write
requestBody:
description: Order object to be created
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/NewOrder"
examples:
example:
summary: Example order
value:
user: 1
products:
- 1
- 3
status: pending
responses:
"201":
description: Order created successfully
content:
application/json:
schema:
$ref: "#/components/schemas/Order"

Finally, we have an operation that returns a stream of order events, called getOrderStream.

We include this operation to test how our SDK generators handle server-sent events.

openapi.yaml

/orderstream:
get:
summary: Get a stream of orders
operationId: getOrderStream
description: Returns a stream of orders
tags:
- Orders
security:
- apiKey: []
responses:
"200":
description: A stream of orders
content:
text/event-stream:
schema:
$ref: "#/components/schemas/OrderStreamMessage"

The remainder of the OpenAPI document defines the components used in the operations above.

openapi.yaml

components:
schemas:
ProductId:
type: integer
example: 1
Book:
type: object
required:
- title
- description
- price
- category
- author
properties:
id:
$ref: "#/components/schemas/ProductId"
title:
type: string
example: Clean Code
description:
type: string
example: A Handbook of Agile Software Craftsmanship
price:
type: integer
description: Price in USD cents
example: 2999
category:
type: string
enum:
- Sci-fi
- Fantasy
- Programming
example: Programming
author:
$ref: "#/components/schemas/Author"
cover_image:
type: string
example: https://example.com/covers/cleancode.jpg
example:
id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
category: Programming
SciFiBook:
allOf:
- $ref: "#/components/schemas/Book"
- type: object
properties:
category:
type: string
const: Sci-fi
example: Sci-fi
example:
id: 3
title: New Sci-Fi Book
description: A new Sci-Fi book description
price: 1999
category: Sci-fi
cover_image: https://example.com/covers/newbook.jpg
author:
name: New Author
photo: https://example.com/photos/newauthor.jpg
biography: New Author is an upcoming writer in the Sci-Fi genre...
FantasyBook:
allOf:
- $ref: "#/components/schemas/Book"
- type: object
properties:
category:
type: string
const: Fantasy
example: Fantasy
example:
id: 2
title: The Hobbit
description: A fantasy novel by J.R.R. Tolkien
price: 1599
category: Fantasy
ProgrammingBook:
allOf:
- $ref: "#/components/schemas/Book"
- type: object
properties:
category:
type: string
const: Programming
example: Programming
example:
id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
AuthorId:
type: integer
example: 1
Author:
type: object
properties:
id:
$ref: "#/components/schemas/AuthorId"
name:
type: string
example: Robert C. Martin
photo:
type: string
example: https://example.com/photos/robert.jpg
biography:
type: string
example: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
anyOf:
- required:
- name
title: Author with name
- required:
- id
title: Author with ID
example:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
OrderId:
type: integer
example: 1
Order:
type: object
required:
- id
- date
- status
- user
- products
properties:
id:
$ref: "#/components/schemas/OrderId"
date:
type: string
format: date-time
example: 2023-05-17T09:24:00Z
status:
type: string
enum:
- pending
- shipped
- delivered
example: pending
user:
$ref: "#/components/schemas/User"
products:
type: array
items:
oneOf:
- $ref: "#/components/schemas/FantasyBook"
- $ref: "#/components/schemas/ProgrammingBook"
- $ref: "#/components/schemas/SciFiBook"
discriminator:
propertyName: category
mapping:
Programming: "#/components/schemas/ProgrammingBook"
Fantasy: "#/components/schemas/FantasyBook"
Sci-fi: "#/components/schemas/SciFiBook"
example:
id: 1
date: 2023-05-17T09:24:00Z
status: pending
user:
id: 1
email: user@example.com
name: John Doe
products:
- id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
- id: 2
title: The Hobbit
description: A fantasy novel by J.R.R. Tolkien
price: 1599
category: Fantasy
author:
id: 2
name: J.R.R. Tolkien
photo: https://example.com/photos/tolkien.jpg
biography: John Ronald Reuel Tolkien was an English writer, poet, philologist, and academic...
OrderStreamMessage:
type: object
description: A message in the order stream
required:
- id
- event
- data
properties:
id:
type: string
title: Message ID
format: UUID
example: 123e4567-e89b-12d3-a456-426614174000
event:
title: Event type
type: string
example: order_created
data:
$ref: "#/components/schemas/Order"
NewOrder:
type: object
required:
- user
- products
- status
properties:
user:
$ref: "#/components/schemas/UserId"
products:
type: array
items:
$ref: "#/components/schemas/ProductId"
example:
user: 1
products:
- 1
- 3
- 5
status: pending
UserId:
type: integer
example: 1
User:
type: object
properties:
id:
$ref: "#/components/schemas/UserId"
email:
type: string
example: user@example.com
name:
type: string
example: John Doe
example:
id: 1
email: user@example.com
name: John Doe
securitySchemes:
apiKey:
type: apiKey
in: header
name: X-API-Key
clientCredentials:
type: oauth2
flows:
clientCredentials:
tokenUrl: https://api.bookstore.com/oauth/token
refreshUrl: https://api.bookstore.com/oauth/refresh
scopes: {}

Our bookstore OpenAPI document is compliant with OpenAPI 3.1, which is supported by both Speakeasy and APIMatic. We define a basic info section and add a single development server.

Here we define two tags to organize our operations with: Books and Orders.

We define one global authentication method, apiKey.

Let's examine the operations we'll need an SDK for, starting with getAllBooks.

This operation takes no input.

What makes this operation interesting is that it returns an array of objects of three types: ProgrammingBook, FantasyBook, and SciFiBook. Each object's type is determined by the book's category.

This example allows us to test how our SDK generators handle discriminated unions in OpenAPI.

Next up, we have an operation that adds a book to the database, called addBook.

This operation takes one object of type ProgrammingBook, FantasyBook, or SciFiBook as input.

Our next book-related operation, updateBookCoverById, takes a book ID as a path variable, and an image as a binary payload.

We include this operation to test how our SDK generators handle binary payloads.

Our final book-related operation, getBookById, takes a book ID as a path variable, and returns one of our book objects.

Next up, we have an operation that returns a list of all orders in the database, called getAllOrders.

This operation returns an array of Order objects, so that we can test an array of nested objects.

Our next order-related operation, createOrder, takes an object of type NewOrder as input, and returns an object of type Order.

We include this one to test how our SDK generators help users avoid common mistakes, like passing the wrong type to an operation.

Finally, we have an operation that returns a stream of order events, called getOrderStream.

We include this operation to test how our SDK generators handle server-sent events.

The remainder of the OpenAPI document defines the components used in the operations above.

openapi.yaml

openapi: 3.1.0
info:
title: Bookstore API
description: API for a bookstore with categories Programming, Fantasy, and Sci-fi
version: 1.0.0
contact:
name: John Doe
email: john@example.com
url: https://example.com
license:
name: MIT
url: https://opensource.org/licenses/MIT
servers:
- url: http://127.0.0.1:4010
description: Local Prism server
tags:
- name: Books
description: Operations related to books
- name: Orders
description: Operations related to orders
security:
- apiKey: []
paths:
/books:
get:
summary: Get all books
operationId: getAllBooks
description: Returns a list of books
security:
- clientCredentials:
- books.read
tags:
- Books
responses:
"200":
description: A list of books
content:
application/json:
schema:
type: array
items:
oneOf:
- $ref: "#/components/schemas/ProgrammingBook"
- $ref: "#/components/schemas/FantasyBook"
- $ref: "#/components/schemas/SciFiBook"
discriminator:
propertyName: category
mapping:
Programming: "#/components/schemas/ProgrammingBook"
Fantasy: "#/components/schemas/FantasyBook"
Sci-fi: "#/components/schemas/SciFiBook"
examples:
example1:
summary: Programming book example
value:
- id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
cover_image: https://example.com/covers/cleancode.jpg
- id: 2
title: The Hobbit
description: A fantasy novel by J.R.R. Tolkien
price: 1599
category: Fantasy
author:
id: 2
name: J.R.R. Tolkien
photo: https://example.com/photos/tolkien.jpg
biography: John Ronald Reuel Tolkien was an English writer, poet, philologist, and academic...
cover_image: https://example.com/covers/thehobbit.jpg
post:
summary: Add a new book
operationId: addBook
description: Adds a new book to the bookstore
security:
- apiKey: []
tags:
- Books
requestBody:
description: Book object to be added
required: true
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/ProgrammingBook"
- $ref: "#/components/schemas/FantasyBook"
- $ref: "#/components/schemas/SciFiBook"
discriminator:
propertyName: category
mapping:
Programming: "#/components/schemas/ProgrammingBook"
Fantasy: "#/components/schemas/FantasyBook"
Sci-fi: "#/components/schemas/SciFiBook"
examples:
example:
summary: Example book
value:
title: New Sci-Fi Book
description: A new Sci-Fi book description
price: 1999
category: Sci-fi
author:
name: New Author
photo: https://example.com/photos/newauthor.jpg
biography: New Author is an upcoming writer in the Sci-Fi genre...
cover_image: https://example.com/covers/newbook.jpg
responses:
"201":
description: Book created successfully
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/ProgrammingBook"
- $ref: "#/components/schemas/FantasyBook"
- $ref: "#/components/schemas/SciFiBook"
discriminator:
propertyName: category
mapping:
Programming: "#/components/schemas/ProgrammingBook"
Fantasy: "#/components/schemas/FantasyBook"
Sci-fi: "#/components/schemas/SciFiBook"
examples:
example:
summary: Example book
value:
id: 3
title: New Sci-Fi Book
description: A new Sci-Fi book description
price: 1999
category: Sci-fi
author:
name: New Author
photo: https://example.com/photos/newauthor.jpg
biography: New Author is an upcoming writer in the Sci-Fi genre...
cover_image: https://example.com/covers/newbook.jpg
/books/{bookId}:
get:
summary: Get a book by ID
operationId: getBookById
description: Returns a single book
security: []
tags:
- Books
parameters:
- name: bookId
description: ID of the book to return
in: path
required: true
schema:
type: integer
example: 1
responses:
"200":
description: A single book
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/ProgrammingBook"
- $ref: "#/components/schemas/FantasyBook"
- $ref: "#/components/schemas/SciFiBook"
discriminator:
propertyName: category
mapping:
Programming: "#/components/schemas/ProgrammingBook"
Fantasy: "#/components/schemas/FantasyBook"
Sci-fi: "#/components/schemas/SciFiBook"
examples:
example1:
summary: Programming book example
value:
id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
cover_image: https://example.com/covers/cleancode.jpg
example2:
summary: Fantasy book example
value:
id: 2
title: The Hobbit
description: A fantasy novel by J.R.R. Tolkien
price: 1599
category: Fantasy
author:
id: 2
name: J.R.R. Tolkien
photo: https://example.com/photos/tolkien.jpg
biography: John Ronald Reuel Tolkien was an English writer, poet, philologist, and academic...
cover_image: https://example.com/covers/thehobbit.jpg
/books/{bookId}/cover:
put:
summary: Update a book cover by ID
operationId: updateBookCoverById
description: Updates a single book cover
security:
- apiKey: []
tags:
- Books
parameters:
- name: bookId
description: ID of the book to update
in: path
required: true
schema:
type: integer
example: 1
requestBody:
description: Book cover
required: true
content:
multipart/form-data:
schema:
type: object
properties:
cover:
type: string
format: binary
responses:
"200":
description: Book cover updated successfully
/orders:
get:
summary: Get all orders
operationId: getAllOrders
description: Returns a list of orders
tags:
- Orders
security:
- clientCredentials:
- orders.read
responses:
"200":
description: A list of orders
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Order"
examples:
example:
summary: Example response
value:
- id: 1
date: 2023-05-17T09:24:00Z
status: pending
user:
id: 1
email: test@example.com
name: John Doe
products:
- id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
cover_image: https://example.com/covers/cleancode.jpg
post:
summary: Create a new order
operationId: createOrder
description: Creates a new order
tags:
- Orders
security:
- clientCredentials:
- orders.write
requestBody:
description: Order object to be created
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/NewOrder"
examples:
example:
summary: Example order
value:
user: 1
products:
- 1
- 3
status: pending
responses:
"201":
description: Order created successfully
content:
application/json:
schema:
$ref: "#/components/schemas/Order"
/orders/{orderId}:
get:
summary: Get an order by ID
operationId: getOrderById
description: Returns a single order
tags:
- Orders
security:
- clientCredentials:
- orders.read
parameters:
- name: orderId
description: ID of the order to return
in: path
required: true
schema:
type: integer
example: 1
responses:
"200":
description: A single order
content:
application/json:
schema:
$ref: "#/components/schemas/Order"
examples:
example:
summary: Example response
value:
id: 1
date: 2023-05-17T09:24:00Z
status: pending
user:
id: 1
email: user@example.com
products:
- id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
cover_image: https://example.com/covers/cleancode.jpg
/orderstream:
get:
summary: Get a stream of orders
operationId: getOrderStream
description: Returns a stream of orders
tags:
- Orders
security:
- apiKey: []
responses:
"200":
description: A stream of orders
content:
text/event-stream:
schema:
$ref: "#/components/schemas/OrderStreamMessage"
components:
schemas:
ProductId:
type: integer
example: 1
Book:
type: object
required:
- title
- description
- price
- category
- author
properties:
id:
$ref: "#/components/schemas/ProductId"
title:
type: string
example: Clean Code
description:
type: string
example: A Handbook of Agile Software Craftsmanship
price:
type: integer
description: Price in USD cents
example: 2999
category:
type: string
enum:
- Sci-fi
- Fantasy
- Programming
example: Programming
author:
$ref: "#/components/schemas/Author"
cover_image:
type: string
example: https://example.com/covers/cleancode.jpg
example:
id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
category: Programming
SciFiBook:
allOf:
- $ref: "#/components/schemas/Book"
- type: object
properties:
category:
type: string
const: Sci-fi
example: Sci-fi
example:
id: 3
title: New Sci-Fi Book
description: A new Sci-Fi book description
price: 1999
category: Sci-fi
cover_image: https://example.com/covers/newbook.jpg
author:
name: New Author
photo: https://example.com/photos/newauthor.jpg
biography: New Author is an upcoming writer in the Sci-Fi genre...
FantasyBook:
allOf:
- $ref: "#/components/schemas/Book"
- type: object
properties:
category:
type: string
const: Fantasy
example: Fantasy
example:
id: 2
title: The Hobbit
description: A fantasy novel by J.R.R. Tolkien
price: 1599
category: Fantasy
ProgrammingBook:
allOf:
- $ref: "#/components/schemas/Book"
- type: object
properties:
category:
type: string
const: Programming
example: Programming
example:
id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
AuthorId:
type: integer
example: 1
Author:
type: object
properties:
id:
$ref: "#/components/schemas/AuthorId"
name:
type: string
example: Robert C. Martin
photo:
type: string
example: https://example.com/photos/robert.jpg
biography:
type: string
example: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
anyOf:
- required:
- name
title: Author with name
- required:
- id
title: Author with ID
example:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
OrderId:
type: integer
example: 1
Order:
type: object
required:
- id
- date
- status
- user
- products
properties:
id:
$ref: "#/components/schemas/OrderId"
date:
type: string
format: date-time
example: 2023-05-17T09:24:00Z
status:
type: string
enum:
- pending
- shipped
- delivered
example: pending
user:
$ref: "#/components/schemas/User"
products:
type: array
items:
oneOf:
- $ref: "#/components/schemas/FantasyBook"
- $ref: "#/components/schemas/ProgrammingBook"
- $ref: "#/components/schemas/SciFiBook"
discriminator:
propertyName: category
mapping:
Programming: "#/components/schemas/ProgrammingBook"
Fantasy: "#/components/schemas/FantasyBook"
Sci-fi: "#/components/schemas/SciFiBook"
example:
id: 1
date: 2023-05-17T09:24:00Z
status: pending
user:
id: 1
email: user@example.com
name: John Doe
products:
- id: 1
title: Clean Code
description: A Handbook of Agile Software Craftsmanship
price: 2999
category: Programming
author:
id: 1
name: Robert C. Martin
photo: https://example.com/photos/robert.jpg
biography: Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...
- id: 2
title: The Hobbit
description: A fantasy novel by J.R.R. Tolkien
price: 1599
category: Fantasy
author:
id: 2
name: J.R.R. Tolkien
photo: https://example.com/photos/tolkien.jpg
biography: John Ronald Reuel Tolkien was an English writer, poet, philologist, and academic...
OrderStreamMessage:
type: object
description: A message in the order stream
required:
- id
- event
- data
properties:
id:
type: string
title: Message ID
format: UUID
example: 123e4567-e89b-12d3-a456-426614174000
event:
title: Event type
type: string
example: order_created
data:
$ref: "#/components/schemas/Order"
NewOrder:
type: object
required:
- user
- products
- status
properties:
user:
$ref: "#/components/schemas/UserId"
products:
type: array
items:
$ref: "#/components/schemas/ProductId"
example:
user: 1
products:
- 1
- 3
- 5
status: pending
UserId:
type: integer
example: 1
User:
type: object
properties:
id:
$ref: "#/components/schemas/UserId"
email:
type: string
example: user@example.com
name:
type: string
example: John Doe
example:
id: 1
email: user@example.com
name: John Doe
securitySchemes:
apiKey:
type: apiKey
in: header
name: X-API-Key
clientCredentials:
type: oauth2
flows:
clientCredentials:
tokenUrl: https://api.bookstore.com/oauth/token
refreshUrl: https://api.bookstore.com/oauth/refresh
scopes: {}

We'll save this as openapi.yaml in the root of our test repository.

Installing the APIMatic CLI

The APIMatic CLI depends on Node.js, and we'll install it using npm. In the terminal, run:


npm install -g @apimatic/cli

This will install the APIMatic CLI in your global node_modules folder and create the apimatic command.

On our test environment, this installed 250 npm packages, 17 of which were deprecated.

Check your APIMatic CLI version:


apimatic --version
# @apimatic/cli/1.1.0-alpha.5 darwin-arm64 node-v20.17.0

Authenticate with APIMatic by running:


apimatic auth:login

Then enter your APIMatic email address and password.

The APIMatic CLI is also open source (opens in a new tab), with the latest update in September 2023.

Installing the Speakeasy CLI

To install the Speakeasy CLI, we'll follow the Speakeasy Getting Started (opens in a new tab) guide.

  1. Create an account on Speakeasy (opens in a new tab).

  2. Install the Speakeasy CLI using Homebrew or cURL:


    brew install speakeasy-api/tap/speakeasy

    or


    curl -fsSL https://raw.githubusercontent.com/speakeasy-api/speakeasy/main/install.sh | sh

  3. Authenticate the CLI with Speakeasy:


    speakeasy auth login

You can check the Speakeasy version:


speakeasy --version
# speakeasy version 1.390.5
# darwin_arm64

Linting OpenAPI Documents

Both Speakeasy and APIMatic can validate OpenAPI documents.

Validate openapi.yaml using APIMatic:


apimatic api:validate --file=openapi.yaml
# Validating specification file... done
# Info: One or more elements in the API specification has a missing description. (View Details)Source: API.
# ...
# Specification file provided is valid

Speakeasy goes beyond validation by linting an OpenAPI document, and then providing separate errors, warnings, and hints. This includes a link to a shareable lint report for easier collaboration.


speakeasy lint openapi -s openapi.yaml

Both platforms validated our OpenAPI document without errors, so let's move on to generating SDKs.

Generating an SDK Using the APIMatic CLI Tutorial

We'll follow the APIMatic tutorial (opens in a new tab) to generate a TypeScript SDK.

In the terminal, run:


apimatic sdk:generate --platform=typescript --file="openapi.yaml"

This should print the following to the terminal:


Generating SDK... done
Downloading SDK... done
Success! Your SDK is located at ~/speakeasy-apimatic-comparison/openapi_sdk_typescript

Inspecting the APIMatic-Generated SDK

To see what was generated, run the tree command from the new SDK directory:


tree openapi_sdk_typescript

  • LICENSE
  • README.md
  • jest.config.js
  • package.json
  • tsconfig.base.json
  • tsconfig.cjs.json
  • tsconfig.esm.json
  • tsconfig.json
  • Poking around the source, we found that the generated SDK did not contain any model code or types related to our bookstore example.

    Corrupted SDK Generation

    In a previous test from January 2024, we used the standard OpenAPI Petstore example API to generate an SDK using APIMatic. In that case, the SDK also did not contain any models, and we encountered a bizarre error - the src/clientInterface.ts file seems to have been generated incorrectly. Here's what we found:

    src/clientInterface.ts

    SdkRequestBuilderFactory = RequestBuilderFactory<
    Server,
    AuthParams
    >;
    export type SdkRequestBuilder = ReturnType<SdkRequestBuilderFactory>;
    export type Server = 'default';
    export type AuthParams = boolean;
    /**
    * Swagger PetstoreLib
    *
    * This file was automatically generated by APIMATIC v3.0 ( https://www.apimatic.io ).
    */
    import { RequestBuilderFactory } from './core';
    export interface ClientInterface {
    getRequestBuilderFactory(): SdkRequestBuilderFactory;
    }
    export type

    The file seems to start in the middle, then wraps around.

    This error gave us an opportunity to engage with the APIMatic support team, so we'll take a brief detour and share our experience: After seven days of filing the bug report, we received a curt, "We've tested out the SDKs via the CLI method and they're being generated as expected." To be fair, the support agent did offer to provide further assistance if we still had a problem.

    As any responsible tester would do, we decided to isolate the test environment from our system to be sure the issue didn't stem from an error on our side. We're using nvm on macOS after all, and issues can crop up when switching between Node versions.

    We created a Dockerfile to install the requirements and generate an SDK:


    FROM node:latest
    ARG apimatic_auth_key=APIMATIC_KEY
    RUN mkdir -p /usr/src/app
    WORKDIR /usr/src/app
    COPY petstore.yaml /usr/src/app
    RUN npm install -g @apimatic/cli
    RUN apimatic auth:login --auth-key=$apimatic_auth_key
    RUN apimatic sdk:generate --platform=typescript --file="petstore.yaml"
    RUN cat petstore_sdk_typescript/src/clientInterface.ts

    Replace $APIMATIC_KEY with your APIMatic API key, then run:


    docker build -t apimatic-petstore --build-arg apimatic_auth_key=$APIMATIC_KEY --progress plain .

    On a second run with Docker, the bug appeared to fix itself, only to fail again later.

    Sure enough, the result was the same - the src/clientInterface.ts file starts in the middle and wraps around. This might be caused by a race condition in the code that downloads and unzips the SDK from APIMatic. Tempting as it is to track this error down, we have SDKs to generate, so we'll move on.

    If you're following along, remember to delete your Docker image, as it contains your API key. The key also appears in your Docker history.

    In the terminal, run:


    docker image rm apimatic-petstore

    In both our Petstore and Bookstore examples, the APIMatic CLI failed to generate a usable SDK.

    We need to find another way to generate an SDK using APIMatic - let's try the web app.

    Generating an SDK Using the APIMatic UI

    Log in to the APIMatic web application and follow the prompts to import our openapi.yaml document as a new API.

    Click on Generate and select TypeScript. This generates a TypeScript SDK, which downloads to your computer as a zip file. We'll save this in our working directory as bookstore-sdk-apimatic.

    Here's what's inside:

  • LICENSE
  • README.md
  • jest.config.js
  • package.json
  • tsconfig.base.json
  • tsconfig.cjs.json
  • tsconfig.esm.json
  • tsconfig.json
  • This looks more complete, and none of the files are corrupted, so we'll move on to trying the Speakeasy generator.

    Create an SDK Using the Speakeasy CLI

    To generate an SDK using Speakeasy, run the following in the terminal:

    Terminal

    speakeasy generate sdk \
    --schema openapi.yaml \
    --lang typescript \
    --out ./bookstore-sdk-speakeasy

    Speakeasy lints the OpenAPI document, then creates a new folder, bookstore-sdk-speakeasy, with the generated SDK.

    Setting Up a Mock Server

    We used Stoplight Prism (opens in a new tab) to generate a mock server to test our SDKs:

    Terminal

    npm install -g @stoplight/prism-cli
    prism mock openapi.yaml

    This command starts a mock server at http://localhost:4010.

    SDK Code Comparison

    Now that we have two SDKs, let's compare the code generated by each platform.

    Runtime Type Checking

    Speakeasy creates SDKs that are type-safe from development to production. As our CEO recently wrote, Type Safe is better than Type Faith (opens in a new tab).

    Speakeasy uses Zod (opens in a new tab) to validate data at runtime. Data sent to the server and data received from the server are validated against Zod definitions in the client.

    This provides safer runtime code execution and helps developers who use your SDK to provide early feedback about data entered by their end users. Furthermore, trusting data validation on the client side allows developers more confidence to build optimistic UIs (opens in a new tab) that update as soon as an end user enters data, greatly improving end users' perception of your API's speed.

    Let's see how Speakeasy's runtime type checking works in an example.

    Consider the following Book component from our OpenAPI document:


    Book:
    type: object
    required:
    - title
    - description
    - price
    - category
    - author
    properties:
    id:
    $ref: "#/components/schemas/ProductId"
    title:
    type: string
    example: Clean Code
    description:
    type: string
    example: A Handbook of Agile Software Craftsmanship
    price:
    type: integer
    description: Price in USD cents
    example: 2999
    category:
    type: string
    enum:
    - Sci-fi
    - Fantasy
    - Programming
    example: Programming

    The highlighted price field above has the type integer.

    speakeasy-example.ts

    // techbooks-speakeasy SDK created by Speakeasy
    import { TechBooks } from "techbooks-speakeasy";
    const bookStore = new TechBooks({
    apiKey: "123",
    });
    async function run() {
    await bookStore.books.addBook({
    author: {
    name: "Robert C. Martin",
    photo: "https://example.com/photos/robert.jpg",
    biography: 'Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...',
    },
    category: "Programming",
    description: "A Handbook of Agile Software Craftsmanship",
    price: 29.99,
    title: "Clean Code",
    });
    }
    run();

    The price field in the Book object in our test code is set to 29.99, which is a floating-point number. This will cause a validation error before the data is sent to the server, as the price field is expected to be an integer.

    Handling Zod validation errors (opens in a new tab) is straightforward, and allows developers to provide meaningful feedback to their end users early in the process.

    The same book object in code using the SDK generated by APIMatic will only be validated on the server. This means that the error will only be caught from the client's perspective after the data is sent to the server, and the server responds with an error message.

    If the server is not set up to validate the price field, the error will not be caught at all, leading to unexpected behavior in your developer-users' applications.

    As a result, developers using the SDK generated by APIMatic may need to write additional client-side validation code to catch these errors before they are sent to the server.

    Dependency Injection: SDK Hooks

    Speakeasy generates a clean mechanism for safely injecting custom code.

    The abridged code below is from the SDK generated by Speakeasy:

    speakeasy/hooks/types.ts

    /*
    * Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT.
    */
    // ...
    export interface BeforeCreateRequestHook {
    /**
    * A hook that is called before the SDK creates a `Request` object. The hook
    * can modify how a request is constructed since certain modifications, like
    * changing the request URL, cannot be done on a request object directly.
    */
    beforeCreateRequest: (hookCtx: BeforeCreateRequestContext, input: RequestInput) => RequestInput;
    }
    export interface BeforeRequestHook {
    /**
    * A hook that is called before the SDK sends a request. The hook can
    * introduce instrumentation code such as logging, tracing and metrics or
    * replace the request before it is sent or throw an error to stop the
    * request from being sent.
    */
    beforeRequest: (hookCtx: BeforeRequestContext, request: Request) => Awaitable<Request>;
    }
    // ...

    The types above are well documented, but you can read more about Speakeasy SDK Hooks in Speakeasy's documentation.

    In short, hooks are typed and contain relevant context depending on when in the lifecycle they are applied. To add hooks, register hooks in the src/hooks/registration.ts file in your TypeScript SDK.

    Here's an example hook:

    src/hooks/registration.ts

    import { Hooks } from "./types";
    /*
    * This file is only ever generated once on the first generation and then is free to be modified.
    * Any hooks you wish to add should be registered in the initHooks function. Feel free to define them
    * in this file or in separate files in the hooks folder.
    */
    export function initHooks(hooks: Hooks) {
    // Add hooks by calling hooks.register{ClientInit/BeforeCreateRequest/BeforeRequest/AfterSuccess/AfterError}Hook
    // with an instance of a hook that implements that specific Hook interface
    // Hooks are registered per SDK instance, and are valid for the lifetime of the SDK instance
    hooks.registerBeforeCreateRequestHook(
    {
    beforeCreateRequest: (hookCtx, input) => {
    // Modify the request input here
    console.log("BeforeCreateRequestHook", input);
    console.log("HookContext", hookCtx);
    return input;
    },
    }
    );
    }

    Speakeasy also provides a clean abstraction to add dependencies to the SDK, by specifying dependencies in the SDK's gen.yaml file:

    gen.yaml

    typescript:
    additionalDependencies:
    dependencies:
    uuid: ^9.0.1
    devDependencies:
    "@types/uuid": "^9.0.8"
    peerDependencies: {}

    Dependency injection and SDK customization are not well documented for APIMatic. Of course, developers can patch the generated SDKs as much as they want, but mixing generated and custom code is often a recipe for disaster.

    OAuth Client Credentials Handling

    Both SDKs handle OAuth 2.0 with client credentials.

    Our bookstore API requires an OAuth 2.0 token with client credentials to access the API. Let's see how the SDKs handle this.

    Consider the following OAuth 2.0 configuration from our OpenAPI document:

    openapi.yaml

    clientCredentials:
    type: oauth2
    flows:
    clientCredentials:
    tokenUrl: https://api.bookstore.com/oauth/token
    refreshUrl: https://api.bookstore.com/oauth/refresh
    scopes: {}

    The SDK generated by Speakeasy takes a clientID and clientSecret when instantiating the SDK. The SDK also includes ClientCredentialsHook class that implements BeforeRequestHook to check whether the token is expired and refresh it if necessary. The hook also checks whether the client has the necessary scopes to access the endpoint, and handles authentication errors.

    speakeasy-example.ts

    // techbooks-speakeasy SDK created by Speakeasy
    import { TechBooks } from "techbooks-speakeasy";
    const bookStore = new TechBooks({
    security: {
    // OAuth 2.0 client credentials
    clientID: "<YOUR_CLIENT_ID_HERE>",
    clientSecret: "<YOUR_CLIENT_SECRET_HERE>",
    },
    });
    async function run() {
    // The SDK handles the token lifecycle, retries, and error handling for you
    await bookStore.books.addBook({
    // Book object
    });
    }
    run();

    The SDK generated by APIMatic has similar functionality.

    Server-Sent Events (SSE) and Streaming Responses

    Our bookstore API includes an operation that streams orders to the client using Server-Sent Events (SSE).


    paths:
    /orderstream:
    get:
    summary: Get a stream of orders
    operationId: getOrderStream
    description: Returns a stream of orders
    tags:
    - Orders
    security:
    - apiKey: []
    responses:
    '200':
    description: A stream of orders
    content:
    text/event-stream:
    schema:
    $ref: '#/components/schemas/OrderStreamMessage'

    Let's see how the SDKs handle this.

    Speakeasy generates types and methods for handling SSE without any customization. Here's an example of how to use the SDK to listen for new orders:

    speakeasy-example.ts

    import { TechBooks } from "techbooks-speakeasy";
    const bookStore = new TechBooks({
    apiKey: 'KEY123',
    });
    async function run() {
    const result = await bookStore.orders.getOrderStream();
    if (result.orderStreamMessage == null) {
    throw new Error('Failed to create stream: received null value');
    }
    const stream = result.orderStreamMessage.stream;
    if (!stream || typeof stream.getReader !== 'function') {
    throw new Error('Invalid stream: expected a ReadableStream');
    }
    const reader = stream.getReader();
    try {
    while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    console.log(new TextDecoder().decode(value));
    }
    } catch (error) {
    console.error('Error reading stream', error);
    } finally {
    reader.releaseLock();
    }
    }
    run();

    (The example above does not run against a local Prism server, but you can test it against Stoplight's hosted Prism (opens in a new tab) server.)

    APIMatic does not generate SSE-handling code.

    Discriminated Unions

    Our OpenAPI document includes a Book component with a category field that can be one of three values: Programming, Fantasy, or SciFi.

    This allows us to type the Book component in requests and responses as specific book types, such as ProgrammingBook, FantasyBook, and SciFiBook.

    OpenAPI supports discriminated unions using the discriminator field in the schema. Here's an example of a response that returns an array of books of different types:

    openapi.yaml

    schema:
    type: array
    items:
    oneOf:
    - $ref: '#/components/schemas/ProgrammingBook'
    - $ref: '#/components/schemas/FantasyBook'
    - $ref: '#/components/schemas/SciFiBook'
    discriminator:
    propertyName: category
    mapping:
    Programming: '#/components/schemas/ProgrammingBook'
    Fantasy: '#/components/schemas/FantasyBook'
    Sci-fi: '#/components/schemas/SciFiBook'

    Let's see how the SDKs handle this.

    Speakeasy generates TypeScript types for each book type, and uses a discriminated union to handle the different book types. This enables developers to use the correct type when working with books of different categories. This pattern could just as easily apply to payment methods or delivery options.

    The example below shows how Speakeasy defines the ProgrammingBook type. It also generates types for FantasyBook and SciFiBook.

    In this example, you'll notice that the category field is optional in the ProgrammingBook type, but is enforced by Zod validation in the SDK.

    speakeasy/books.ts

    /*
    * Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT.
    */
    import { Author, Author$ } from "./author";
    import * as z from "zod";
    export type ProgrammingBook = {
    id?: number | undefined;
    title: string;
    description: string;
    /**
    * Price in USD cents
    */
    price: number;
    category?: "Programming" | undefined;
    author: Author;
    coverImage?: string | undefined;
    };
    /** @internal */
    export namespace ProgrammingBook$ {
    export const inboundSchema: z.ZodType<ProgrammingBook, z.ZodTypeDef, unknown> = z
    .object({
    id: z.number().int().optional(),
    title: z.string(),
    description: z.string(),
    price: z.number().int(),
    category: z.literal("Programming").optional(),
    author: Author$.inboundSchema,
    cover_image: z.string().optional(),
    })
    .transform((v) => {
    return {
    ...(v.id === undefined ? null : { id: v.id }),
    title: v.title,
    description: v.description,
    price: v.price,
    ...(v.category === undefined ? null : { category: v.category }),
    author: v.author,
    ...(v.cover_image === undefined ? null : { coverImage: v.cover_image }),
    };
    });
    export type Outbound = {
    id?: number | undefined;
    title: string;
    description: string;
    price: number;
    category: "Programming";
    author: Author$.Outbound;
    cover_image?: string | undefined;
    };
    export const outboundSchema: z.ZodType<Outbound, z.ZodTypeDef, ProgrammingBook> = z
    .object({
    id: z.number().int().optional(),
    title: z.string(),
    description: z.string(),
    price: z.number().int(),
    category: z.literal("Programming").default("Programming" as const),
    author: Author$.outboundSchema,
    coverImage: z.string().optional(),
    })
    .transform((v) => {
    return {
    ...(v.id === undefined ? null : { id: v.id }),
    title: v.title,
    description: v.description,
    price: v.price,
    category: v.category,
    author: v.author,
    ...(v.coverImage === undefined ? null : { cover_image: v.coverImage }),
    };
    });
    }

    We can see how Speakeasy generates SDK code to handle the different book types in the response for the getgetAllBooks operation:

    speakeasy/getallbooks.ts

    /*
    * Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT.
    */
    import * as components from "../components";
    import * as z from "zod";
    export type ResponseBody =
    | (components.ProgrammingBook & { category: "Programming" })
    | (components.FantasyBook & { category: "Fantasy" })
    | (components.SciFiBook & { category: "Sci-fi" });
    export type GetAllBooksResponse = {
    httpMeta: components.HTTPMetadata;
    /**
    * A list of books
    */
    responseBodies?:
    | Array<
    | (components.ProgrammingBook & { category: "Programming" })
    | (components.FantasyBook & { category: "Fantasy" })
    | (components.SciFiBook & { category: "Sci-fi" })
    >
    | undefined;
    };
    // ...

    Note how the array elements in responseBodies are typed according to the book category.

    This may seem like a trivial example, but it illustrates how Speakeasy generates types that are more specific and easier to work with than the types generated by APIMatic. This could, for instance, help developers correctly handle different book types in their applications.

    APIMatic does not generate types for discriminated unions, and developers must manually handle the different book types in the response.

    Here is the equivalent type definition generated by APIMatic:

    apimatic/programmingBook.ts

    /**
    * Bookstore APILib
    *
    * This file was automatically generated by APIMATIC v3.0 ( https://www.apimatic.io ).
    */
    // ...
    import { CategoryEnum, categoryEnumSchema } from './categoryEnum';
    // ...
    export interface ProgrammingBook {
    id?: number;
    title: string;
    description: string;
    /** Price in USD cents */
    price: number;
    category: CategoryEnum;
    author: Author2;
    coverImage?: string;
    }
    // ...

    Following the CategoryEnum import:

    apimatic/categoryEnum.ts

    /**
    * Bookstore APILib
    *
    * This file was automatically generated by APIMATIC v3.0 ( https://www.apimatic.io ).
    */
    // ...
    /**
    * Enum for CategoryEnum
    */
    export enum CategoryEnum {
    Scifi = 'Sci-fi',
    Fantasy = 'Fantasy',
    Programming = 'Programming',
    }
    // ...

    Discriminating between different book types in the response is left to users.

    OpenAPI Overlays

    If editing your OpenAPI document is not an option, Speakeasy also supports the OpenAPI Overlays specification, which allows you to add or override parts of an OpenAPI document without modifying the original document.

    This step can form part of your CI/CD pipeline, ensuring that your SDKs are always up-to-date with your API, even if your OpenAPI document is generated from code.

    Speakeasy's CLI can also generate OpenAPI overlays for you, based on the differences between two OpenAPI documents.

    SDK and Bundle Size

    Let's compare the bundle sizes of the SDKs generated by Speakeasy and APIMatic.

    Start by adding a sdk-tests/speakeasy.ts file that imports the Speakeasy SDK:

    sdk-tests/speakeasy.ts

    import { SDKCore } from "openapi/core.js";
    import { booksAddBook } from "openapi/funcs/booksAddBook.js";
    // Use `SDKCore` for best tree-shaking performance.
    // You can create one instance of it to use across an application.
    const sdk = new SDKCore({
    apiKey: "<YOUR_API_KEY_HERE>",
    });
    async function run() {
    const res = await booksAddBook(sdk, {
    id: 1,
    title: "New Sci-Fi Book",
    description: "A new Sci-Fi book description",
    category: "Sci-fi",
    price: 1999,
    author: {
    id: 1,
    name: "New Author",
    photo: "https://example.com/photos/newauthor.jpg",
    biography: "New Author is an upcoming writer in the Sci-Fi genre...",
    },
    coverImage: "https://example.com/covers/newbook.jpg",
    });
    if (!res.ok) {
    throw res.error;
    }
    const { value: result } = res;
    // Handle the result
    console.log(result)
    }
    run();

    Next, add a sdk-tests/apimatic.ts file that imports the APIMatic SDK:

    sdk-tests/apimatic.ts

    import {
    Client,
    BooksController,
    AddBookBody,
    AddBookResponse,
    CategoryEnum,
    ApiError,
    } from "bookstore-apilib";
    const client = new Client({
    apiKeyCredentials: {
    "X-API-Key": "YOUR_API_KEY",
    },
    });
    const booksController = new BooksController(client);
    const body: AddBookBody = {
    title: "Clean Code",
    description: "A Handbook of Agile Software Craftsmanship",
    price: 2999,
    category: CategoryEnum.Programming,
    author: {
    name: "Robert C. Martin",
    photo: "https://example.com/photos/robert.jpg",
    biography:
    'Robert Cecil Martin, colloquially known as "Uncle Bob", is an American software engineer...',
    },
    id: 1,
    coverImage: "https://example.com/covers/cleancode.jpg",
    };
    async function addBook() {
    try {
    const { result, ...httpResponse } = await booksController.addBook(body);
    console.log(result);
    if (AddBookResponse.isProgrammingBook(result)) {
    // Use the result narrowed down to ProgrammingBook type.
    } else if (AddBookResponse.isFantasyBook(result)) {
    // Use the result narrowed down to FantasyBook type.
    } else if (AddBookResponse.isSciFiBook(result)) {
    // Use the result narrowed down to SciFiBook type.
    } else {
    // result is narrowed down to type 'never'.
    }
    // Get more response info...
    // const { statusCode, headers } = httpResponse;
    } catch (error) {
    console.error(error);
    if (error instanceof ApiError) {
    const errors = error.result;
    }
    }
    }
    addBook();

    Running the code above generates a validation error, due to the lack of discriminated unions in the SDK generated by APIMatic. This won't affect the bundle size, though.

    We'll use esbuild to bundle the SDKs. First, install esbuild:


    npm install esbuild

    Next, add a sdk-tests/build.js script that uses esbuild to bundle the SDKs:

    sdk-tests/build.js

    import * as esbuild from "esbuild";
    import * as fs from "fs";
    const speakeasyBuild = await esbuild.build({
    entryPoints: ["speakeasy.ts"],
    outfile: "dist/speakeasy.cjs",
    bundle: true,
    minify: true,
    treeShaking: true,
    metafile: true,
    target: "node18",
    platform: "node",
    });
    fs.writeFileSync(
    "dist/speakeasy.json",
    JSON.stringify(speakeasyBuild.metafile, null, 2)
    );
    const apimaticBuild = await esbuild.build({
    entryPoints: ["apimatic.ts"],
    outfile: "dist/apimatic.cjs",
    bundle: true,
    minify: true,
    treeShaking: true,
    metafile: true,
    target: "node18",
    platform: "node",
    });
    fs.writeFileSync(
    "dist/apimatic.json",
    JSON.stringify(apimaticBuild.metafile, null, 2)
    );

    Run the build.js script:


    node build.ts

    This generates two bundles, dist/speakeasy.cjs and dist/apimatic.cjs, along with their respective metafiles.

    Bundle Size Comparison

    Now that we have two bundles, let's compare their sizes.

    First, let's look at the size of the dist/speakeasy.cjs bundle:


    du -sh dist/speakeasy.cjs
    # Output
    # 128K dist/speakeasy.cjs

    Next, let's look at the size of the dist/apimatic.cjs bundle:


    du -sh dist/apimatic.cjs
    # Output
    # 360K dist/apimatic.cjs

    Despite lacking runtime data validation, the bundle built with the SDK generated by APIMatic is significantly larger than that built with the SDK generated by Speakeasy.

    We can use the metafiles generated by esbuild to analyze the bundle sizes in more detail.

    Analyzing Bundle Sizes

    The metafiles generated by esbuild contain detailed information about which source files contribute to each bundle's size, presented as a tree structure.

    We used esbuild's online bundle visualizer (opens in a new tab) to analyze the bundle sizes.

    Here's a summary of the bundle sizes:

    The dist/speakeasy.cjs bundle's largest contributor, at 43.4%, is the Zod library used for runtime data validation. The Zod library's tree-shaking capabilities are a work in progress, and future versions of SDKs are expected to have smaller bundle sizes.

    Speakeasy Bundle Size

    The dist/apimatic.cjs bundle's largest contributor, at 37.4%, is mime-db, a "large database of mime types and information about them" (mime-db on npm (opens in a new tab)).

    APIMatic Bundle Size

    Bundling for the Browser

    Speakeasy SDKs are designed to work in a range of environments, including the browser. To bundle an SDK for the browser, you can use a tool like esbuild or webpack.

    Here's an example of how to bundle the Speakeasy SDK for the browser using esbuild:


    npx esbuild speakeasy.ts --bundle --minify --target=es2020 --platform=browser --outfile=dist/speakeasy.js

    Doing the same for the APIMatic SDK generates an error, as the SDK is not designed to work in the browser out of the box.


    npx esbuild apimatic.ts --bundle --minify --target=es2020 --platform=browser --outfile=dist/apimatic.js
    # ✘ [ERROR] Could not resolve "stream"

    Linting and Change Detection

    Speakeasy keeps track of changes in your OpenAPI document, and versions the SDKs it creates based on changes.

    Speakeasy Compared to Open-Source Generators

    If you are interested in seeing how Speakeasy stacks up against other SDK generation tools, check out our post.

    CTA background illustrations

    Speakeasy Changelog

    Subscribe to stay up-to-date on Speakeasy news and feature releases.