Changelog: Customisable Imports, OpenAPI Overlays, and Terraform generation improvements!
New features to the Speakeasy Platform - November 15th, 2023.
Welcome to another edition of the Speakeasy Changelog. In this issue, we'll give you a sneak peek into our support for OpenAPI Overlays (opens in a new tab) and how we're leveraging them to help customers customize their SDKs and other generated artifacts without changing the underlying specification.
We'll also be diving into new DevEx improvements that let you customize SDK imports, as well as exciting Terraform releases, including Pulumi support!
Sound good?
Ok, letβs go! π
OpenAPI Overlays
What is an Overlay, you ask? You can think of them as miniature OpenAPI documents that can be used to customize certain details of your API without altering the source document. Why would you want to maintain one?
- You might want to customize your OpenAPI spec for SDK creation, but changing your spec is hard because it's generated from an API framework like FastAPI, tsoa, JOI, etc.
- You have a lot of teams at your company creating OpenAPI specs, and asking one of them to make a change is a tough process.
- You are setting up a Terraform provider for your OSS product and need different server URLs configured so users only hit a hosted API.
Let's look at an example. Here's a simple spec for the Speakeasy bar with only two tags
defined, drinks
and orders
.
openapi: 3.1.0info: title: The Speakeasy Bar version: 1.0.0 summary: A bar that serves drinks.servers: - url: https://speakeasy.bar description: The production server.security: - apiKey: []tags: - name: drinks description: The drinks endpoints. - name: orders description: The orders endpoints.paths: /dinner/: post: ... get: ... /drinks/: post: ...
To make an easy-to-use SDK, we've decided that a public interface should use snacks
, i.e., sdk.snacks.get_orders()
. As the owner of the company's SDK, you want to make this change, but that would mean making an actual
code change with the team that owns the drinks and orders service. Worse yet, it's all microservices, and there is no one team that owns all the services. You can get around this sustainably with
an overlay.
This overlay includes a target
that you want to modify in your source document and an action
to modify the target.
overlay: 1.0.0info: title: Overlay to fix the Speakeasy bar version: 0.0.1actions: - target: "$.tags" description: Add a Snacks tag to the global tags list update: - name: Snacks description: All methods related to serving snacks - target: "$.paths['/dinner']" description: Remove all paths related to serving dinner remove: true
Specify that we want to add a new tag to the global tags list $.tags
and add a description of the edit you're making. Under the update label, add the name and description of the tag you want to add.
Now you can use the Speakeasy CLI to merge these two documents right before generating the SDK:
speakeasy overlay apply -s openapi.yaml -o overlay.yaml >> combined.yaml
Time to celebrate π You've just created a new OpenAPI document that you can use to generate an SDK with the snacks
tag. More on how to use Overlays here.
π¦ Customizable Imports
At Speakeasy, we believe that automated doesn't mean no input. Certain aspects of SDK design need to be in the hands of the API builders. That's why we've built a platform which is flexible enough to let developers craft the devex for their users. To that end, we've released customizable imports. You can now configure the structure of the import
paths in your SDK, and how they are referenced by your users. As an example, for Typescript:
By default, our SDKs have created models in directories dictated by the OpenAPI spec, i.e. models/components
, models/operations
and models/errors
. This is great for keeping your SDK organized, but it could be a bit verbose for your users, especially
for less structured languages like Typescript and Python where global imports are the norm.
sdk/ββ models/β ββ components/β β ββ user.tsβ β ββ drink.tsβ β ββ ...β ββ operations/β β ββ getuser.tsβ β ββ updateuser.tsβ β ββ getdrink.tsβ β ββ updatedrink.tsβ β ββ ...β ββ errors/β ββ sdkerror.tsβ ββ responseerror.tsβ ββ ...ββ ...
The import structure in this case would look like:
import { SDK } from '@speakeasy/bar'import { User } from '@speakeasy/bar/dist/models/components/user'import { GetDrinkRequest } from '@speakeasy/bar/dist/models/operations/user'
By default, our SDKs have created models in directories dictated by the OpenAPI spec, i.e. models/components
, models/operations
and models/errors
. This is great for keeping your SDK organized, but it could be a bit verbose for your users, especially
for less structured languages like Typescript and Python where global imports are the norm.
The import structure in this case would look like:
import { SDK } from '@speakeasy/bar'import { User } from '@speakeasy/bar/dist/models/components/user'import { GetDrinkRequest } from '@speakeasy/bar/dist/models/operations/user'
sdk/ββ models/β ββ components/β β ββ user.tsβ β ββ drink.tsβ β ββ ...β ββ operations/β β ββ getuser.tsβ β ββ updateuser.tsβ β ββ getdrink.tsβ β ββ updatedrink.tsβ β ββ ...β ββ errors/β ββ sdkerror.tsβ ββ responseerror.tsβ ββ ...ββ ...
As an API producer, you can now configure your SDK to generate models into a single directory and import them from there. For Typescript, this would result in:
/ββ srcβ ββ models/β β ββ user.tsβ β ββ drink.tsβ β ββ getuser.tsβ β ββ updateuser.tsβ β ββ getdrink.tsβ β ββ updatedrink.tsβ β ββ sdkerror.tsβ β ββ responseerror.tsβ β ββ index.tsβ β ββ ...β ββ ...ββ ...
With an import structure that is flat and supports a global path as follows:
import { User, GetDrinkRequest, SDK } from '@speakeasy/bar'
As an API producer, you can now configure your SDK to generate models into a single directory and import them from there. For Typescript, this would result in:
With an import structure that is flat and supports a global path as follows:
import { User, GetDrinkRequest, SDK } from '@speakeasy/bar'
/ββ srcβ ββ models/β β ββ user.tsβ β ββ drink.tsβ β ββ getuser.tsβ β ββ updateuser.tsβ β ββ getdrink.tsβ β ββ updatedrink.tsβ β ββ sdkerror.tsβ β ββ responseerror.tsβ β ββ index.tsβ β ββ ...β ββ ...ββ ...
More documentation on how to configure this in your SDK's gen.yaml
file can be found here.
π’ Improvements and Bug Fixes π
Speakeasy v119.1 (opens in a new tab)
Terraform
π’ Pulumi support for generated Terraform providers
π’ Importing resources with Integer IDs
π’ DataSources with no required arguments (Empty Datasources)
π’ x-speakeasy-conflicts-with
extension
Python
π’ Oauth support for Python SDKs
Php
π’ Support for customizable imports
Typescript
π Ensure contentType
variable doesn't conflict with common parameter names
π Correctly handle x-www-form-urlencode
in Typescript
π’ unset baseURL on default axios client
Golang
π BigInt
& Decimal
type support within a Union
type
Other:
π’ Allow optional properties in usage snippets to be conditionally rendered
π’ Support for customizing input/output model suffixes
π’ Maintain OpenAPI Order in generated models
π’ Automatic Authentication documentation generated in READMEs
π’ Automatic Retry documentation generated in READMEs when retry extensions are used