NHacker Next
  • new
  • past
  • show
  • ask
  • show
  • jobs
  • submit
The Stainless SDK Generator (stainlessapi.com)
brandur 10 days ago [-]
I helped maintain the public API language bindings at Stripe for many years, and although I'd defend it to the death as the right DX (SDKs are so much easier to use than raw web APIs), it's hard to appreciate just how onerous of a process this was before Alex pioneered a codegen-based solution.

Some of the language SDKs were dynamic (e.g. Ruby, Python) and could adapt automatically to small API changes like if a new field was added in a response. Some were not (e.g. Java, Go), and for every API change, someone had to manually make the change to that SDK's codebase (add a field, add a struct, add a function for a new endpoint), get it reviewed and merged, and cut a release. As Stripe got bigger and there were API changes all the time, the only way this was even remotely functional was that we had a couple heroic workhorses that'd watch for changes and open hundreds of PRs a year for them. Honestly, in retrospect, it's amazing this even worked.

Getting everything switched over to a generated solution was an arduous process because the new generated code had to be API-compatible with the existing code so that the cutover to generated bindings didn't break every Stripe user under the sun. We eventually got there, but it took a long time.

I like Stainless' mission because after seeing the crazy maintenance hassle that all of this was at Stripe, I think it makes way more sense to save all that engineering time for your more concerns, and outsource this problem to someone else. A plug-and-play way of getting high quality SDKs in all common languages that get pushed to appropriate repositories for distribution and comes with quality companion documentation.

We've actually had pretty good luck at my current job with open source tools like openapi-generator [1], but this sort of codegen is so convoluted that the sustainability of a pure open source solution makes me a little afraid, and you still end up doing a lot of the last mile work yourself.

---

[1] https://github.com/OpenAPITools/openapi-generator

eskibars 9 days ago [-]
I've worked on 4 different (pretty heavily used) developer-focused products in my career in product management, and we hand-built SDKs for each of them. This experience is so common: in all cases, a lot of the leadership went into it thinking it would be an easy-to-maintain "background task" to maintain 4-6 SDKS because we generally hired pretty well seasoned and hard working developers

Inevitably in all cases language-specific problems exactly like this happened, but also just some frustrating drift. One of those heroic workhorses would go out on parental leave and maybe a backup would go on vacation or out for extended sick time and the SDKs would start drifting away from the API. Someone would be stuck maintaining code in their absence. At one point, I (a product manager at the time) got tasked with maintaining a Perl SDK while a developer was out on leave for several months because my background is in software development and I was the only person that could really maintain it in the company.

I'm happy to see tools like this come about because it seems everyone underestimates how much work SDKs take to really build functional SDKs. You can get bad ones cheaply by just putting a few people on and accepting bugs and drift. The counter-argument against these tools that I've generally heard is that they'll also produce worse ones cheaply or require effectively the same maintenance to figure out a machine-generated problem as a human-generated problem.

mythz 10 days ago [-]
Never been a fan of code-gen solutions that generates both DTOs and client proxies which IMO promotes a lot of fragile code-gen that's less resilient to changes. The solution we've adopted in ServiceStack to solve this with minimal code generation and better reusability is to only generate the message-based Typed DTOs for each language but have them able to be used by the same generic JSON ServiceClient.

This approach takes minimal effort since we only need to generate Typed DTOs in each language, which all works the same way, where you use the same generic `JsonServiceClient` (created once per language/platform) that use same methods to make API requests making it easy to for our built-in API Explorer [1] (Live Demo [2]) to auto generate API pages for all 11 supported languages which also supports dynamic languages like JS/TS, Python and PHP with additional type hints [3].

[1] https://docs.servicestack.net/api-explorer#code-tab

[2] https://vue-vite-api.jamstacks.net/ui/QueryBookings?tab=code

[3] https://docs.servicestack.net/add-servicestack-reference

brandur 10 days ago [-]
That's interesting — the typed request and response objects that you get with an API SDK are certainly one of the biggest upsides, and I certainly see the benefit of having full control of the transport layer yourself so that you can add any common telemetry / logging / statistics at your leisure.

On the other hand, a benefit of a more complete API is that in typed languages you can tab-complete your way to success. With each endpoint a function and all the requisite configuration (API URL, etc.) bundled in, for basic integrations you may never even have to reference documentation, or if you do, very little of it, as your IDE finds functions/properties for you and you can read documentation right out their docstrings.

mythz 10 days ago [-]
Sending Messages also have many benefits over RPC methods [1] that's especially important for evolvable APIs across process boundaries.

The DTOs are still typed so you still get AutoComplete that also include API Docs and Type hints in the generated DTOs since we full control how DTOs are generated and we're able to capture richer type information in the server C# DTOs (used as blueprints to generate DTOs in different languages).

All the information about how to call the API and what it returns is captured in the Request DTOs, and the only thing the Service Clients need is the BaseUrl for where the APIs are hosted. So you could create a higher level SDK client that just needs to inherit the Service Client and hard code its URL, e.g:

class MyClient : JsonServiceClient(BaseUrl) {}

Where they'll also be able to add any helper methods specific to their APIs (e.g. Custom Auth). For the trade-off of not being able to reuse that client to call different APIs and endpoints, but will still share the same base class so you could still create reusable functionality that can be shared across all Service Clients.

[1] https://docs.servicestack.net/advantages-of-message-based-we...

GeneticGenesis 10 days ago [-]
Disclaimer: We're an early adopter of Stainless at Mux.

I've spent more of my time than I'd like to admit managing both OpenAPi spec files [1] and fighting with openapi-generator [2] than any sane person should have to. While it's great having the freedom to change the templates an thus generated SDKs you get with using that sort of approach, it's also super time consuming, and when you have a lot of SDKs (we have 6 generated SDKs), in my experience it needs someone devoted to managing the process, staying up with template changes etc.

Excited to see more SDK languages come to Stainless!

[1] https://www.mux.com/blog/an-adventure-in-openapi-v3-api-code...

[2] https://github.com/OpenAPITools/openapi-generator

bterlson 10 days ago [-]
Grats on the release! It will be awesome to see how far y'all can push codegen quality from an OpenAPI source of truth.

I worked on this extensively inside Azure and I know it is not an easy problem (and with more JSON Schema coming in 3.1/4.0, it is only getting harder). There are a lot of API patterns that you want to expose purpose built client abstractions for. Pagination is a big example. If you stick to OpenAPI, you have to ensure your specs use the patterns your client generator recognizes, and it's not always trivial for authors to know how to express that pattern and for your codegen to infer that pattern. In Azure we tended to rely more on custom extensions to OpenAPI because it made the contract a lot clearer and less error prone, but then you lose interoperability.

One thing to consider - I work on TypeSpec[1], and one of the main reasons we built it is to allow encapsulation and reuse of patterns in a first-class way. So, rather than the contract being "endpoints which declare parameters and/or return types with these shapes are inferred to be paginated endpoints", the contract can be "use Page<T>" and the emitted OpenAPI conforms to the contract without effort. It would be fun to see a Stainless TypeSpec library for all the patterns your codegen supports!

1: https://typespec.io

mmcclure 10 days ago [-]
We're in the process of (slowly) switching our SDKs over to Stainless. I'll be transparent that it hasn't been the easiest/quickest process ever, but the biggest pain point so far is that we were forced to clean up some rough spots around our OpenAPI spec. It was something we needed to do anyway, so definitely not a knock on Stainless, but it did take the first SDK rollout from being a month or two to many months.

The biggest reason we started working with Stainless was to migrate from the open source generator process we were struggling with, but actually ended up transitioning our hand-rolled Node/JS SDK first because it's both our most popular SDK and because the fragmentation of the JS ecosystem has been a surprisingly big headache[1]. Went into it for reduced maintenance, came out pleasantly surprised at how useful I was finding my own SDK's features around `for...of` on paginated routes, etc.

Congrats on the launch, Stainless crew!

[1] https://www.mux.com/blog/keeping-up-with-the-node-ish-ecosys...

pksunkara 10 days ago [-]
lapusta 10 days ago [-]
We've recently evaluated all four platforms—Stainless, Fern, Speakeasy, and Liblab—and here are our key takeaways:

Stainless: The standout for maturity and idiomatic code generation. While method signatures across products may look the same, Stainless shines during developing & debugging - making their codebase easier to navigate. They have a practical separation of SDK configuration from OpenAPI specification, setting it apart from others reliant on OpenAPI overlays. The Stainless Studio also proved invaluable for refining our OpenAPI specs during our exploration phase.

Fern: Notable for being open-source, though not free. It provides a robust end-to-end Developer Experience, covering everything from SDKs and documentation to Postman collections. Fern uses an internal "Fern Definition" language (~ think Smithy), it's optional and enables capabilities like merging multiple specs, but is adding another layer to navigate in our view.

Speakeasy: Moves at a fast pace, which could be a double-edged sword. Rapid iterations may lead to frequent, potentially disruptive updates for customers. A minor gripe was the inclusion of "Speakeasy" in class names, which felt overly branded.

Liblab: Initially limited in language support, they've expanded but still lag behind in establishing a strong customer base, which might be a red flag for some adopters.

BTW all folks are very approachable and collaborative!

simplesager 9 days ago [-]
Thanks for the mention! We do move fast and to help manage the changes better we've introduced a number of change management concepts recently like breaking change detection and more controls around semver. The updates can also be stacked by the user into PRs and versions updated and published in one go. Definitely choices that we can guide you through

On the Speakeasy branded classes. We got rid of that some time ago based on customer feedback. Check out our new TS generator https://www.speakeasyapi.dev/post/how-we-built-universal-ts

alalani1 10 days ago [-]
When you say it shines during development and debugging - can you provide examples of what types of problems it solved?
jenanwise 10 days ago [-]
I’ve been working with the Stainless team over the last year, and I rapidly went from being highly skeptical of outsourcing SDKs to being one of the team’s biggest fans.

Separately, as a consumer of many afterthought-ish SDKs for popular services, I’m extremely excited to see their work opening up to more developers. I hope it raises the bar for SDKs everywhere!

ucarion 10 days ago [-]
Lots of these have been popping up lately, they all seem really good. This is the first one I've seen with an answer for "I want to customize the output by hand", which is neat.

https://buildwithfern.com/ https://liblab.com/ https://www.speakeasyapi.dev/

Between this and fancy API doc generators (Mintlify, etc.), I feel like we're finally getting close to being able to just go from OpenAPI to Stripe-level "polish".

dgellow 9 days ago [-]
We have to improve our docs a lot, but yes, that’s the goal! You as a stainless user should feel empowered and in control :)
androa 10 days ago [-]
I've been using openapi-generator for this for quite some time. I love the principle, but find that their generators are not all top notch, and the template-based approach is very hard to contribute to.

One thing I do not see with Stainless is also using it to generate the server side of the API.

We use OpenAPI as a design doc, and generate client SDKs and Datos for the server from the same spec. This gives us pretty solid control over interoperability.

itslennysfault 10 days ago [-]
Thats what I used at my previous startup and had a lot of luck with it. Our backend used NestJS which allowed us to define APIs and DTOs using decorators which automatically generated OpenAPI json files. Then our clients would load the json and generate their own SDK as a library. It worked extremely well. It worked so well that I don't really understand what problem Stainless is solving. We didn't ship public SDKs. So, maybe that's what I'm missing, but for our internal clients OpenAPI generators worked great. We generated SDKs on the fly for TypeScript (Node services and Angular clients), Dart (flutter), Kotlin (android), and Swift (iOS). For the most part it all "just worked" for our use case.
rattray 10 days ago [-]
> We didn't ship public SDKs. So, maybe that's what I'm missing

Yeah, Stainless is more focused on public SDKs, which can be a lot trickier and more demanding.

Some more details replying to your other comment here: https://news.ycombinator.com/item?id=40148629

Bjartr 10 days ago [-]
> their generators are not all top notch, and the template-based approach is very hard to contribute to.

I agree, I love them in theory, but wrangling mustache templates is a hassle. They end up super coupled to the code that populates them which makes them a bear to modify.

morgante 10 days ago [-]
Stainless and Alex have been great to work with! We've partnered with them to incorporate automated migrations for several APIs (ex. https://docs.grit.io/patterns/library/cloudflare_go_v2) and everyone at Stainless is consistently technically brilliant and kind.
nknj 10 days ago [-]
Proud to have Stainless as a partner at OpenAI. All our SDKs are generated by them. Alex, Robert, Philip and team are extremely thoughtful about SDK design and push us to improve our API + other products while they're at it. Stainless is bringing a new standard of SDKs to our industry and this is a great thing for all developers.
twitchard 9 days ago [-]
Congratulations to Alex and team! Had the pleasure of working with Alex on SDK generators back in 2019 and 2020 and it's very exciting to see these ideas and the fruits of this become available to the entire world. Stainless would definitely be where I looked first if I were starting a developer-facing SaaS today.
sloanesturz 10 days ago [-]
We've been happy customers of Stainless since the beginning. Alex and the team are thoughtful and care a lot about API design. Recommended!

https://github.com/increase

bgentry 10 days ago [-]
I was fortunate to be able to watch Stainless evolve while at Mux when we were evaluating it to replace open source & homegrown generators. Alex & team were incredibly responsive to feedback throughout, and the product improved quickly.

Generated code has a bad rap for results that are not idiomatic or user-friendly, but I think it's clear that it doesn't have to be that way—getting it right just takes a lot of care and effort that is typically overlooked.

Stainless is definitely best-in-class here as far as I've seen.

yencabulator 2 days ago [-]
Frankly, this looks horrible. Generics and weird calling conventions instead of setting struct fields, no thanks.

    zone, err: = client.Zones.New(context.Background(), zones.ZoneNewParams {
        Account: cloudflare.F(zones.ZoneNewParamsAccount {
            ID: cloudflare.F("023e105f4ecef8ad9ca31a8372d0c353"),
        }),
        Name: cloudflare.F("example.com"),
        Type: cloudflare.F(zones.ZoneNewParamsTypeFull),
    })
pm 10 days ago [-]
A couple of months ago I tried to generate a Swift SDK using Cloudflare's OpenAPI spec with Apple's Swift OpenAPI generator, to no avail. Apple's generator is quite strict from what I can tell, so I'm curious about the differences between Stainless' and Apple's work.

(I haven't tried it since then, so it may work now).

serjester 10 days ago [-]
Out of curiosity how difficult has it been hosting OpenAI's SDK? Is there one off stuff that goes into that? Their API seems really challenging - SSE's, tons of complex generics, async, docs - it's endless.

Really impressive stuff, I can't imagine doing all this with a team as lean as yours.

rattray 9 days ago [-]
It definitely isn't easy, but it's the kind of challenges we love! You mentioned many of the big ones, but there's also just the sheer scale of the userbase, which means the libraries will be run under every permutation of runtime and configuration under the sun. It's a lot of edge cases to get right!

Their team is great to work with, which helps make it worth it.

(Note that a lot of the SSE type stuff we do generally so we're able to support a variety of AI companies and other streaming use cases, though disclaimer it is a premium feature)

ezekg 10 days ago [-]
I run an API SaaS and this looks great. I've wanted to add more SDKs to my lineup, but it's a big commitment for a single person to maintain. Right now, I have a minimal Go SDK to test the waters and definitively see how much effort it takes. One hurdle I have with services like Stainless (which looks great!) is that I don't know where to start to actually codegen an OpenAPI spec from a Ruby/Rails API. Writing and maintaining the spec by hand sounds like a nightmare for any decent sized API, but I guess it's not worse than maintaining multiple SDKs (or even one). How did Stripe handle this?
rattray 10 days ago [-]
> I don't know where to start to actually codegen an OpenAPI spec from a Ruby/Rails API… How did Stripe handle this?

Stripe built their own Ruby DSL for exactly this, and it worked great. It looked something like this:

    class CreateCustomerMethod < AbstractAPIMethod
      endpoint "post /v1/customers"

      returns CustomerAPIResource
      
      required :name, String, description: "The name of the customer."
      optional :description, String, description: "Additional information about the customer."

      def execute
        # …
      end
    end
Unfortunately, I don't know of an open-source equivalent for Ruby; FastAPI in Python is the closest I know of in any language. At Stainless, we're building a TypeScript version of this, but it's still deep in dogfooding.

Most people just end up maintaining by hand and using a tool like Optic or Akita to check that it's not wildly out-of-date.

amne 10 days ago [-]
From experience I can tell you that you just need to do it manually. And keep maintaing the spec manually. That keeps you aware of it. It is your contract with the outside world. You don't want tools to automatically update it everytime you push. On the contrary, you want tools to slap you hard every time you push and you forgot to update the spec. Now you're going to think hard why did you change the spec and if you maybe need to bump to /api/v2/*

As far as generating the first spec from an existing API I think there was shown here on HN a while ago a tool that can generate the spec from you just "browsing" the API with developer tools or something. Maybe even from a HAR file IIRC.

TehShrike 10 days ago [-]
I'm curious to see the output. Most of the time I specifically avoid using the SDK published by the API provider because it's thousands of lines of code when all I need is ten lines wrapping fetch.
rattray 10 days ago [-]
We try to keep it to a minimum, especially in JS (though we have some nice improvements coming soon when we deprecate node-fetch in favor of built-in fetch). The package sizes aren't tiny because we include thorough types and sourcemaps, but the bundle sizes are fairly tidy.

Here's an example of a typical RESTful endpoint (Lithic's `client.cards.create()`:

https://github.com/lithic-com/lithic-node/blob/36d4a6a70597e...

Here are some example repos produced by Stainless:

1. https://github.com/openai/openai-node 2. https://github.com/openai/openai-python 3. https://github.com/cloudflare/cloudflare-go 4. https://github.com/Modern-Treasury/modern-treasury-java

vvoyer 10 days ago [-]
What about jsii? The technology behind AWS cdk sdks: https://aws.github.io/jsii/

Is Stainless similar, different?

rattray 9 days ago [-]
Very different! Stainless crafts each language's SDK generator around its specific idioms.
ashryan 10 days ago [-]
I’ve been keeping a simple list of SDK generators here: https://gist.github.com/ashryanbeats/7806079820e0c83b35b94ae...

Nothing fancy but maybe more helpful than picking through SEO articles in you’re looking for options.

Max_Horstmann 10 days ago [-]
Congrats!

Any C# support in the works? :)

segphault 10 days ago [-]
Yep! C# is on our roadmap, coming up next after we ship support for Ruby. I recently joined the engineering team at Stainless and this was one of the first questions that I asked. I'm a C# enthusiast and worked at Xamarin back in the day, so I'm pretty excited for this feature.
enepture 10 days ago [-]
Congratulations to Alex and the team. After chatting with them recently at a NYC meet-up, you can tell they live and breathe client SDK generation. They are thoughtful about creating an excellent experience for those generating SDKs (Stainless Studio) and those consuming them. Good luck!
10 days ago [-]
htunnicliff 10 days ago [-]
Question for Stainless folks: For Node.js/JavaScript SDKs, did you consider using Proxy instances in lieu of classes for each resource, to reduce the amount of actual code generated?
rattray 10 days ago [-]
Yeah, we opted for classes like this to ensure simple, reliable static typing.

The amount of runtime code generated per endpoint is typically around 3 short lines – here's an example, also posted elsewhere on this thread: https://github.com/lithic-com/lithic-node/blob/36d4a6a70597e...

kmlx 10 days ago [-]
what’s the difference between this and https://github.com/deepmap/oapi-codegen
jamietanna 10 days ago [-]
Thanks for thinking of oapi-codegen (I'm one of the maintainers) but agreed, we only target Go - maybe that'll change in the future, but there are a lot of great companies out there working on this as a problem that I'm happy not trying to compete against
kmlx 7 days ago [-]
thanks for being a maintainer!
mmcclure 10 days ago [-]
There are open source generators for a ton of languages[1]! We've been reasonably happy with their output for ~5 SDKs over the last few years. We did a massive writeup on our experience at the time[2].

In the end...ongoing maintenance there was still enough of a pain that we made the decision to outsource it. For just one or two SDKs I think we probably would have kept going with it, though.

[1] https://github.com/OpenAPITools/openapi-generator

[2] https://www.mux.com/blog/an-adventure-in-openapi-v3-api-code...

rattray 10 days ago [-]
oapi-codegen looks pretty cool (it seems I starred it some time ago) but for starters, it's Go only. Stainless does a bunch of languages with more on the way.

You can see an example of the Go code Stainless produces here: https://github.com/cloudflare/cloudflare-go

(I'm the Stainless founder)

itslennysfault 10 days ago [-]
That one only does Go, but it is just one of many OpenAPI generators. I used openapi-generator and was able to produce SDKs for TypeScript (angular and node), Dart (Flutter), Kotlin (andoird), and Swift (iOS). They worked great for our purposes and I really had no complaints or issues.

I guess my question is, what is the key differentiator that Stainless offers above just using OpenAPI and its huge library of existing generators?

https://github.com/OpenAPITools/openapi-generator

rattray 10 days ago [-]
If it works great for you, that's great – stick with it!

From what I've seen, a lot of API developers find that openapi-generator flat-out breaks or produces broken code for their OpenAPI spec (varies language-to-language of course).

Beyond hitting the "it just works" mark more often, some other key differentiators:

1. much easier to configure & customize

2. auto-retry with exponential backoff (this is huge for our customers)

3. auto-pagination

4. overall ergonomics of using the SDK

5. internals that look handwritten (some people care, others don't)

6. one-click releases to github & package managers w/ semver, changelogs, etc

7. careful handling of edge cases (eg, will adding a new enum variant cause your Java client to crash?)

8. a long tail of more advanced features, like webhook signature verification, streaming, etc

Thanks for the question – hope this helps :)

itslennysfault 10 days ago [-]
Very helpful. Thanks for the details.
jacoblee9315 10 days ago [-]
Congrats on the launch! Incredible customer list!
gp 10 days ago [-]
This is a very exciting product - I'm surprised this didn't exist until now. Would love to see C++ support in the future.
tango12 9 days ago [-]
Love this!

Curious, wouldn’t this be great for folks with internal APIs as well?

Team 1 can consume team 2’s API with an SDK instead of a raw REST client.

addcn 10 days ago [-]
Alex and team are top notch. Met some of their customers who also use Optic and hear great things.

Definitely the place to go these days if you have a public API and want it to be as developer friendly as they come.

aazo11 10 days ago [-]
We use Stainless. Great team and product.
beastman82 10 days ago [-]
Is gRPC too easy or ?
shidoshi 10 days ago [-]
Congrats, folks.
ramijames 10 days ago [-]
That is a very nice landing page.
Harriet_ 10 days ago [-]
Been waiting for this! Nice!!
geekodour 10 days ago [-]
is this an alternative to buf.build?
frankfrank13 10 days ago [-]
buf would require to switch to protobufs, which for most companies is huge switch
rattray 10 days ago [-]
buf.build : protobuf :: stainless : REST

(roughly)

cyabuggy 10 days ago [-]
[flagged]
goofybozo111 10 days ago [-]
[flagged]
yreznikova 10 days ago [-]
Great solution!
simplesager 9 days ago [-]
Congratulations to the Stainless team on a fantastic launch! I'm one of the co founders of Speakeasy (https://www.speakeasyapi.dev/). We do similar things in the SDK space and some other things too.

I resonate with a lot of what is said in the comments. Last mile API tooling is underrated and most companies don't have the deep API platform expertise to build from scratch. The OSS options in the space proved SDK generation could be done but the actual product that you got in hand still needed a lot of work to be enterprise ready. The code was poor quality, wasn't customisable and there was no change management story. We're seeing a new wave of work in this space that's making api platform engineering much more accessible.

The API devex story starts with SDK creation and maintenance but there is a lot more to come!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact
Rendered at 10:32:46 GMT+0000 (Coordinated Universal Time) with Vercel.