[HN Gopher] The Stainless SDK Generator
       ___________________________________________________________________
        
       The Stainless SDK Generator
        
       Author : brandur
       Score  : 158 points
       Date   : 2024-04-24 16:34 UTC (6 hours ago)
        
 (HTM) web link (www.stainlessapi.com)
 (TXT) w3m dump (www.stainlessapi.com)
        
       | yreznikova wrote:
       | Great solution!
        
       | Harriet_ wrote:
       | Been waiting for this! Nice!!
        
       | mmcclure wrote:
       | 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...
        
       | morgante wrote:
       | 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.
        
       | kmlx wrote:
       | what's the difference between this and
       | https://github.com/deepmap/oapi-codegen
        
         | rattray wrote:
         | 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 wrote:
           | 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 wrote:
             | 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 wrote:
               | Very helpful. Thanks for the details.
        
         | mmcclure wrote:
         | 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...
        
         | jamietanna wrote:
         | 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
        
       | Max_Horstmann wrote:
       | Congrats!
       | 
       | Any C# support in the works? :)
        
         | segphault wrote:
         | 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.
        
       | gp wrote:
       | This is a very exciting product - I'm surprised this didn't exist
       | until now. Would love to see C++ support in the future.
        
       | geekodour wrote:
       | is this an alternative to buf.build?
        
         | rattray wrote:
         | buf.build : protobuf :: stainless : REST
         | 
         | (roughly)
        
         | frankfrank13 wrote:
         | buf would require to switch to protobufs, which for most
         | companies is huge switch
        
       | pksunkara wrote:
       | How do you guys differ against https://www.speakeasyapi.dev and
       | https://www.buildwithfern.com and https://liblab.com?
        
         | lapusta wrote:
         | 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!
        
           | alalani1 wrote:
           | When you say it shines during development and debugging - can
           | you provide examples of what types of problems it solved?
        
       | TehShrike wrote:
       | 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 wrote:
         | 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
        
       | jacoblee9315 wrote:
       | Congrats on the launch! Incredible customer list!
        
       | sloanesturz wrote:
       | 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
        
       | GeneticGenesis wrote:
       | 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
        
       | pm wrote:
       | 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).
        
       | ramijames wrote:
       | That is a very nice landing page.
        
       | addcn wrote:
       | 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 wrote:
       | We use Stainless. Great team and product.
        
       | androa wrote:
       | 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 wrote:
         | 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 wrote:
           | > 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 wrote:
         | > 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.
        
       | brandur wrote:
       | 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
        
         | mythz wrote:
         | 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 wrote:
           | 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 wrote:
             | 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...
        
       | jenanwise wrote:
       | 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!
        
       | ezekg wrote:
       | 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?
        
         | amne wrote:
         | 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.
        
         | rattray wrote:
         | > 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.
        
       | shidoshi wrote:
       | Congrats, folks.
        
       | htunnicliff wrote:
       | 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 wrote:
         | 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...
        
       | ucarion wrote:
       | 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 wrote:
         | 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 :)
        
       | bterlson wrote:
       | 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
        
       | ashryan wrote:
       | 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.
        
       | bgentry wrote:
       | 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.
        
       | nknj wrote:
       | 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.
        
       | vvoyer wrote:
       | What about jsii? The technology behind AWS cdk sdks:
       | https://aws.github.io/jsii/
       | 
       | Is Stainless similar, different?
        
       | enepture wrote:
       | 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!
        
       | serjester wrote:
       | 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.
        
       | beastman82 wrote:
       | Is gRPC too easy or ?
        
       ___________________________________________________________________
       (page generated 2024-04-24 23:01 UTC)