<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: euruuuu</title>
    <description>The latest articles on DEV Community by euruuuu (@euruuuudev).</description>
    <link>https://dev.to/euruuuudev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4175009%2F319291ac-85d6-4f6d-a581-48ff34c2255e.png</url>
      <title>DEV Community: euruuuu</title>
      <link>https://dev.to/euruuuudev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/euruuuudev"/>
    <language>en</language>
    <item>
      <title>I got tired of OpenAPI generators breaking my clients, so I built steadysdk</title>
      <dc:creator>euruuuu</dc:creator>
      <pubDate>Sat, 10 Oct 2026 10:01:38 +0000</pubDate>
      <link>https://dev.to/euruuuudev/i-got-tired-of-openapi-generators-breaking-my-clients-so-i-built-steadysdk-g9o</link>
      <guid>https://dev.to/euruuuudev/i-got-tired-of-openapi-generators-breaking-my-clients-so-i-built-steadysdk-g9o</guid>
      <description>&lt;p&gt;Most OpenAPI generators are "generate once and forget."&lt;/p&gt;

&lt;p&gt;You run them the first time and everything looks great. Then the backend changes an &lt;code&gt;operationId&lt;/code&gt;, adds an endpoint, or renames a field… and suddenly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your custom helpers get overwritten&lt;/li&gt;
&lt;li&gt;Method names silently change&lt;/li&gt;
&lt;li&gt;You have to re-check the entire generated client&lt;/li&gt;
&lt;li&gt;CI either breaks or you stop regenerating altogether&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I got tired of this cycle, so I built &lt;strong&gt;steadysdk&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What makes it different
&lt;/h2&gt;

&lt;p&gt;steadysdk is a TypeScript OpenAPI client generator designed for the &lt;em&gt;second&lt;/em&gt;, &lt;em&gt;tenth&lt;/em&gt;, and &lt;em&gt;hundredth&lt;/em&gt; generation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name locking&lt;/strong&gt; — every method name is recorded in a lock file. A renamed &lt;code&gt;operationId&lt;/code&gt; or a new colliding endpoint won’t break your existing code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safe regeneration&lt;/strong&gt; — &lt;code&gt;generated/&lt;/code&gt; is rewritten every run. Your own code lives in &lt;code&gt;client.ts&lt;/code&gt; (created once) or inside &lt;code&gt;// &amp;lt;steadysdk:custom&amp;gt;&lt;/code&gt; regions and is preserved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hand-edit detection&lt;/strong&gt; — if someone edits a generated file outside the custom regions, regeneration stops and tells you instead of silently overwriting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API changelog&lt;/strong&gt; — &lt;code&gt;steadysdk diff&lt;/code&gt; and &lt;code&gt;steadysdk changelog&lt;/code&gt; show exactly what changed between two versions of the spec and mark breaking changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero runtime dependencies&lt;/strong&gt; — the generated client only uses &lt;code&gt;fetch&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI-friendly&lt;/strong&gt; — &lt;code&gt;steadysdk generate --check&lt;/code&gt; fails when the committed SDK is out of date.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quick start
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
bash
npm install --save-dev @steadysdk/cli
npx steadysdk init
npx steadysdk generate

&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/euruuuuu" rel="noopener noreferrer"&gt;
        euruuuuu
      &lt;/a&gt; / &lt;a href="https://github.com/euruuuuu/steadysdk" rel="noopener noreferrer"&gt;
        steadysdk
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;steadysdk&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Generate typed API clients from OpenAPI, and keep them maintainable as the API evolves.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/@steadysdk/cli" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/3a4566a19cb9780bc3b50f30dab63d1f425bcdcf03f7f4da9a808419747ec784/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f4073746561647973646b2f636c693f6c6162656c3d6e706d25323025343073746561647973646b253246636c69" alt="npm"&gt;&lt;/a&gt;
&lt;a href="https://github.com/euruuuuu/steadysdk/actions/workflows/ci.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/euruuuuu/steadysdk/actions/workflows/ci.yml/badge.svg" alt="CI"&gt;&lt;/a&gt;
&lt;a href="https://github.com/euruuuuu/steadysdk/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/7013272bd27ece47364536a221edb554cd69683b68a46fc0ee96881174c4214c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e737667" alt="License: MIT"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/568133950e2872ee792ec28f0b6589cd20ad37b09bce7afdc5263d3095fd2108/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6e6f64652d32322e31392532422d3333393933333f6c6f676f3d6e6f64652e6a73266c6f676f436f6c6f723d7768697465"&gt;&lt;img src="https://camo.githubusercontent.com/568133950e2872ee792ec28f0b6589cd20ad37b09bce7afdc5263d3095fd2108/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6e6f64652d32322e31392532422d3333393933333f6c6f676f3d6e6f64652e6a73266c6f676f436f6c6f723d7768697465" alt="Node.js 22.19+"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/f853349a425a8c69ab7f2372f72a5cddd6b8b73fc51ad5670cc2809269ee8ad0/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f547970655363726970742d372d3331373843363f6c6f676f3d74797065736372697074266c6f676f436f6c6f723d7768697465"&gt;&lt;img src="https://camo.githubusercontent.com/f853349a425a8c69ab7f2372f72a5cddd6b8b73fc51ad5670cc2809269ee8ad0/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f547970655363726970742d372d3331373843363f6c6f676f3d74797065736372697074266c6f676f436f6c6f723d7768697465" alt="TypeScript 7"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/euruuuuu/steadysdk/docs/images/hero.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Feuruuuuu%2Fsteadysdk%2FHEAD%2Fdocs%2Fimages%2Fhero.png" alt="steadysdk: typed TypeScript clients from OpenAPI that survive the 50th regeneration, not just the first"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most OpenAPI generators are "generate once and forget": re-running them overwrites everything, wipes out
hand-written helpers, and silently renames methods when the spec shifts. steadysdk is built for the second
tenth and hundredth generation.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;div class="table-wrapper-paragraph"&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;table&gt;

&lt;thead&gt;

&lt;tr&gt;

&lt;th&gt;Situation&lt;/th&gt;

&lt;th&gt;Typical generator&lt;/th&gt;

&lt;th&gt;steadysdk&lt;/th&gt;

&lt;/tr&gt;

&lt;/thead&gt;

&lt;tbody&gt;

&lt;tr&gt;

&lt;td&gt;First generation&lt;/td&gt;

&lt;td&gt;Works&lt;/td&gt;

&lt;td&gt;Works&lt;/td&gt;

&lt;/tr&gt;

&lt;tr&gt;

&lt;td&gt;API adds an endpoint&lt;/td&gt;

&lt;td&gt;Rewrites everything&lt;/td&gt;

&lt;td&gt;Adds the new method; every other file is untouched&lt;/td&gt;

&lt;/tr&gt;

&lt;tr&gt;

&lt;td&gt;You wrote a custom helper&lt;/td&gt;

&lt;td&gt;Gets deleted&lt;/td&gt;

&lt;td&gt;Lives in your own file or a custom region; kept on every run&lt;/td&gt;

&lt;/tr&gt;

&lt;tr&gt;

&lt;td&gt;Someone hand-edits generated code&lt;/td&gt;

&lt;td&gt;Silently overwritten&lt;/td&gt;

&lt;td&gt;Detected; regeneration stops and tells you&lt;/td&gt;

&lt;/tr&gt;

&lt;tr&gt;

&lt;td&gt;An &lt;code&gt;operationId&lt;/code&gt; is renamed upstream&lt;/td&gt;

&lt;td&gt;Your method is renamed (breaking)&lt;/td&gt;

&lt;td&gt;Name pinned by the lock file until you opt in&lt;/td&gt;

&lt;/tr&gt;

&lt;tr&gt;

&lt;td&gt;The API changes&lt;/td&gt;

&lt;td&gt;You diff the YAML by hand&lt;/td&gt;

&lt;td&gt;
&lt;br&gt;
&lt;code&gt;steadysdk diff&lt;/code&gt; lists every change and marks the breaking ones&lt;/td&gt;

&lt;/tr&gt;

&lt;tr&gt;

&lt;td&gt;Release notes&lt;/td&gt;

&lt;td&gt;Written by hand&lt;/td&gt;

&lt;td&gt;
&lt;br&gt;
&lt;code&gt;steadysdk changelog&lt;/code&gt;&lt;br&gt;
&lt;/td&gt;

&lt;/tr&gt;

&lt;/tbody&gt;

&lt;/table&gt;&lt;/div&gt;…&lt;p&gt;&lt;/p&gt;&lt;/div&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/euruuuuu/steadysdk" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>ai</category>
      <category>typescript</category>
      <category>node</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
