Frameworks

Generate an SDK from Rails with rswag

Rails has no built-in OpenAPI output — unlike FastAPI or Spring Boot, nothing ships a spec for free. The standard fix is rswag: you describe and test your API with an RSpec DSL, and a rake task turns those request specs into a real OpenAPI 3.0 document. From there, a Rails OpenAPI SDK is the same two-step problem as any other framework: get a clean spec out, then run it through a generator or a hosted pipeline like Sourced.

Getting an OpenAPI spec out of Rails with rswag

rswag doesn't read your controllers directly — it generates the spec from RSpec request specs that both document and test each operation. That's the trade: you write (or already have) integration tests, and the spec comes out synchronized with what actually passes.

Step Command / file What it does
1. Add the gems gem 'rswag-api', gem 'rswag-ui', plus gem 'rspec-rails' and gem 'rswag-specs' in :development, :test (or just gem 'rswag' for all three) Installs spec generation, JSON/YAML serving, and the Swagger UI
2. Install rails g rswag:install Scaffolds spec/openapi_helper.rb and mounts the rswag-api/rswag-ui engines
3. Write specs spec/requests/*_spec.rb, using path, get/post/etc., parameter, response, run_test! Describes and tests each operation in one file
4. Generate rake rswag:specs:swaggerize (aliased rake rswag) Runs the specs and writes the OpenAPI file(s) declared in config.openapi_specs
5. Serve mount Rswag::Api::Engine => 'api-docs' and mount Rswag::Ui::Engine => 'api-docs' in config/routes.rb Spec JSON and an embedded Swagger UI both live at /api-docs

A minimal spec looks like this:

# spec/requests/blogs_spec.rb
require 'openapi_helper'

describe 'Blogs API' do
  path '/blogs' do
    post 'Creates a blog' do
      tags 'Blogs'
      operationId 'createBlog'
      consumes 'application/json'
      parameter name: 'blog', in: :body, schema: {
        type: :object,
        properties: { title: { type: :string }, content: { type: :string } },
        required: %w[title content]
      }

      response '201', 'blog created' do
        let(:request_params) { { 'blog' => { title: 'foo', content: 'bar' } } }
        run_test!
      end
    end
  end
end

The output location is configurable in spec/openapi_helper.rb:

RSpec.configure do |config|
  config.openapi_root = Rails.root.to_s + '/openapi'
  config.openapi_specs = {
    'v1/openapi.json' => { openapi: '3.0.1', info: { title: 'API V1', version: 'v1' } }
  }
end

If you're on Grape instead of Rails controllers

Grape APIs don't use rswag's request-spec DSL. The equivalent there is grape-swagger: add gem 'grape-swagger', call add_swagger_documentation in your root API class, and it introspects your Grape routes directly — no separate spec files needed:

module API
  class Root < Grape::API
    format :json
    mount API::Cats
    mount API::Dogs
    add_swagger_documentation # default mount_path: /swagger_doc
  end
end

The gotcha: as of this writing, grape-swagger generates Swagger / OpenAPI Spec 2.0, not OpenAPI 3.x. Most modern codegen and hosted-docs tooling — Sourced included — expects OpenAPI 3.0 or 3.1 input, so a Grape spec needs an upgrade pass first. See OpenAPI 3.1 vs Swagger 2.0: upgrade first before feeding a grape-swagger output into any 3.x-only pipeline.

The gotcha: operationId is opt-in, not automatic

rswag's DSL supports an explicit operationId 'createBlog' call inside an operation block, but nothing forces you to set one. Skip it and the field is simply absent from the generated spec — which is spec-legal but bad for codegen: generators fall back to building a method name from the HTTP verb and path, so api.createBlog(...) becomes something like api.blogsPost(...). See how to fix missing operationId errors for the naming pattern that survives regeneration, and add operationId to every operation block before you generate a public SDK from it.

From a Rails spec to hosted docs and typed SDKs

Once rake rswag produces a clean file — real operationIds, referenced (not inline) schemas — turning it into an installable SDK is a separate step. With Sourced:

  1. Point Sourced at your repo or the generated file. Connect from GitHub, or upload the openapi.json that rake rswag wrote directly.
  2. Preview the TypeScript and Python SDKs. Sourced renders both before anything publishes, so a verb-and-path fallback method name shows up in the preview, not a customer's autocomplete.
  3. Get hosted docs and an llms.txt from the same spec. No separate docs deploy — the hosted docs preview and llms.txt come from the same pass as the SDKs.
  4. Publish on your schedule. Free unlimited previews and up to 2 hosted noindex docs review sites and one hosted MCP server, no credit card, cover steps 1-3.

Comparison: rswag vs. a full SDK pipeline

Need rswag gives you What it doesn't give you
A generated OpenAPI spec Yes, from RSpec request specs Nothing — this part is real and free
Tests that stay synced with docs Yes — the DSL runs your specs to produce the spec Nothing to add here
Interactive API browsing Embedded Swagger UI at /api-docs A hosted docs site for customers
A typed client SDK Nothing built in Generated TypeScript or Python SDK
A diff before a breaking release Nothing built in A compatibility report
Agent-readable docs Nothing built in llms.txt derived from the spec

OSS generators for other languages

Sourced's generated SDKs focus on TypeScript and Python today — the two ecosystems where teams publish first, not Ruby. For the SDK itself, a clean rswag or grape-swagger (upgraded to 3.x) spec works with the standard per-language OpenAPI Generator ecosystem — Ruby, Go, Java, PHP, C#, Kotlin, Rust, and Swift all have their own walkthroughs. The five-minute generate-an-SDK guide covers running a local generator against any spec, rswag's included. Sourced still has a role on that same spec: hosted docs, llms.txt, and validation, independent of which language generates the client.

Other backend frameworks with their own spec-extraction paths: FastAPI, NestJS, Express, Django, Spring Boot, Laravel, and .NET.

Honest scope: when rswag alone is enough

If your Rails API only has internal consumers who already read Ruby, the embedded Swagger UI at /api-docs plus a thin Faraday wrapper is a reasonable stopping point. rswag is also only as good as your spec coverage: an endpoint with no request spec produces no documentation, since generation is driven by the tests you've written, not by scanning controllers. Reach for full SDK generation once you have external customers, a second language to support, or a docs site that needs to outlive your test suite's Swagger UI.

FAQ

Does Rails generate OpenAPI automatically?

No. Rails has no built-in OpenAPI output. rswag is the standard way to get one: it generates the spec from RSpec request specs via a rake task, rather than introspecting your routes or controllers directly.

What's the rake task to generate the OpenAPI file?

rake rswag:specs:swaggerize, aliased as rake rswag. It runs your RSpec request specs and writes the file(s) configured in config.openapi_specs inside spec/openapi_helper.rb.

Does rswag support OpenAPI 3.1?

rswag documents itself as "OpenAPI 3.0 compatible." If your downstream generator or docs tool needs 3.1 specifically, validate the emitted document rather than assuming — see OpenAPI 3.1 vs 3.0 for what actually changes between versions.

I'm on Grape, not Rails controllers — does rswag still work?

No, rswag's DSL is built for rspec-rails request specs. Grape APIs use grape-swagger instead, which introspects your Grape routes directly. Its output is Swagger 2.0, so plan an upgrade step before feeding it to OpenAPI 3.x-only tooling.

Why does my generated SDK have method names like blogsPost instead of createBlog?

That's the generator's fallback when operationId is missing from the spec. rswag supports an explicit operationId 'createBlog' call inside each operation block — it's just not automatic. Add it to every operation before generating a public SDK.

Can rswag document an endpoint without a request spec?

No. Generation is driven entirely by the RSpec request specs you write, with no separate controller-scanning step. An endpoint without a spec simply doesn't appear in the generated file.

Ship it

Once rake rswag produces a spec with real operationIds and referenced schemas, you have a real Rails OpenAPI SDK source to work with. Start free on Sourced to preview a TypeScript and Python SDK plus hosted docs from that spec in one pass, or run it through the OpenAPI validator first to catch gaps before you generate anything.