<?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: Seyves</title>
    <description>The latest articles on DEV Community by Seyves (@seyves_02123877c1b6b0f3a7).</description>
    <link>https://dev.to/seyves_02123877c1b6b0f3a7</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%2F4046683%2Fc988e3c7-3229-4efa-853d-5dc565580df2.jpg</url>
      <title>DEV Community: Seyves</title>
      <link>https://dev.to/seyves_02123877c1b6b0f3a7</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/seyves_02123877c1b6b0f3a7"/>
    <language>en</language>
    <item>
      <title>Swagger docs from your existing TypeScript types — no framework required</title>
      <dc:creator>Seyves</dc:creator>
      <pubDate>Sat, 25 Jul 2026 12:45:24 +0000</pubDate>
      <link>https://dev.to/seyves_02123877c1b6b0f3a7/swagger-docs-from-your-existing-typescript-types-no-framework-required-1215</link>
      <guid>https://dev.to/seyves_02123877c1b6b0f3a7/swagger-docs-from-your-existing-typescript-types-no-framework-required-1215</guid>
      <description>&lt;h1&gt;
  
  
  The problem
&lt;/h1&gt;

&lt;p&gt;Recently I was looking for an npm package to generate &lt;strong&gt;OpenAPI&lt;/strong&gt; (Swagger) documentation for my existing TypeScript project. My biggest requirement was TypeScript type-to-schema conversion: I already have all my request and response types, so why should I maintain the same schemas again in OpenAPI? Zod support would be a nice bonus.&lt;/p&gt;

&lt;p&gt;After trying most of the existing solutions, I found they generally fall into two categories:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Runtime frameworks
&lt;/h2&gt;

&lt;p&gt;The most popular example is &lt;strong&gt;tsoa&lt;/strong&gt;. Honestly, tsoa is one of the best OpenAPI generators available today: it understands TypeScript well, generates schemas automatically and detects status codes.&lt;/p&gt;

&lt;p&gt;There are also contract-first libraries like &lt;strong&gt;ts-rest&lt;/strong&gt;, &lt;strong&gt;Zodios&lt;/strong&gt; and &lt;strong&gt;express-zod-api&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;However, none of these solutions are agnostic when it comes to how you write your code — they all dictate the shape of your routes.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Manual generators
&lt;/h2&gt;

&lt;p&gt;The best-known example is swagger-jsdoc. You write raw OpenAPI next to your code in JSDoc comments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * @openapi
 * /users:
 *   post:
 *     ...
 */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;swagger-jsdoc&lt;/strong&gt; is simple and framework-agnostic, but it's too verbose and knows nothing about your types.&lt;/p&gt;

&lt;p&gt;Then I found a similar tool that solved the verbosity problem: &lt;strong&gt;@visulima/jsdoc-open-api&lt;/strong&gt;. It parses much more readable JSDoc tags:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * POST /users
 * @summary Creates a new user.
 * @tags Users
 * @bodyContent {User} application/json - User object to create.
 * @bodyRequired
 * @response 201 - User created successfully.
 * @responseContent {User} 201.application/json - The created user object.
 */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But it still doesn't care about your types. User here is just the name of a component you have to define elsewhere in your document. Why not parse the actual types at generation time? That question inspired me to create &lt;strong&gt;Autoswag&lt;/strong&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  The solution - &lt;strong&gt;Autoswag&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;Describe your routes with readable JSDoc, and let the generator convert your TS types along the way. It doesn't affect your runtime code in any way, works with any framework, and even with vanilla JS.&lt;/p&gt;

&lt;p&gt;This is what Autoswag does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// api/users.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CreateUserRequest&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/types/user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * @autoswag POST /users
 * @summary Create a new user
 * @tag Users
 * @accept {CreateUserRequest}
 * @response {User} 201 User created successfully
 * @response 400 Invalid input
 * @response 401 Unauthorized
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Your implementation&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The types you specify in &lt;code&gt;@accept&lt;/code&gt; and &lt;code&gt;@response&lt;/code&gt; are parses and converted to OpenAPI schemas by &lt;strong&gt;Autoswag&lt;/strong&gt; at generation time. You can also attach OpenAPI metadata right on your types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/**
     * User ID
     * @example "5516e359-6c9c-4ebb-a409-52373d536d50"
     * @format uuid
     */&lt;/span&gt;
    &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * User's age
     * @example 32
     * @minimum 0
     * @maximum 200
     */&lt;/span&gt;
    &lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;

    &lt;span class="nx"&gt;preferences&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Preferences&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Already using Zod? Autoswag converts Zod schemas too, and picks up metadata straight from comments in the declaration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;UserSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="cm"&gt;/** @format uuid */&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="cm"&gt;/** User name */&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="cm"&gt;/** @format email */&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AddressSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="cm"&gt;/** @format datetime */&lt;/span&gt;
    &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;iso&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;UserSchema&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All of this happens at generation time — zero runtime dependencies, zero restrictions on how you structure your code. &lt;/p&gt;

&lt;p&gt;JSDoc &lt;a class="mentioned-user" href="https://dev.to/typedef"&gt;@typedef&lt;/a&gt; in vanilla JS is supported too, along with many more features.&lt;/p&gt;

&lt;h1&gt;
  
  
  Try it
&lt;/h1&gt;

&lt;p&gt;If you have an existing project and don't want to rewrite it around a framework just to get docs, give &lt;strong&gt;Autoswag&lt;/strong&gt; a try:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; autoswag
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check out the GitHub repository (&lt;a href="https://github.com/Seyves/autoswag" rel="noopener noreferrer"&gt;https://github.com/Seyves/autoswag&lt;/a&gt;) for the full feature list — issues, feedback and PRs are very welcome!&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>documentation</category>
      <category>api</category>
      <category>swagger</category>
    </item>
  </channel>
</rss>
