Open-source project
rswag/rswag avatar
rswag/rswag

Rswag: OpenAPI documents generated from the Rails request specs you already have

Seamlessly adds a Swagger to Rails-based API's

2,203 stars453 forksRubyMIT

At a glance

What is it?
Rswag adds a Swagger-flavoured DSL to rspec-rails request specs so that writing an integration test also produces an OpenAPI file. The interesting question is which of the two release lines to install, and the README's own request for maintainers.
Who is it for?
Rswag is a good fit for a Rails API whose integration tests already exist, because the documentation then costs nothing extra to keep correct, and a poor fit for a team that would rather hand-write an OpenAPI file or has no rspec-rails suite at all. Two things to settle before installing.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 49 days ago.
What is it written in?
Mainly Ruby, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The document comes out of the test suite

Most Rails APIs document themselves twice: once in a spec that describes expected behaviour, and once in an OpenAPI file that drifts out of date within a month. Rswag's premise is that those are the same artefact. It extends rspec-rails request specs with a DSL, so describing an endpoint and testing it are one activity, and once the tests pass, a rake task turns them into an OpenAPI file served as YAML or JSON.

The DSL reads like documentation. A path block contains an operation, the operation declares tags, what it consumes and produces, and its parameters, then each response declares a status code, a description, an optional schema, the request parameters to send, and finally `run_test!` to execute the request and assert the response. The README's own example is a blogs endpoint with `POST /blogs` returning 201 and 422, and `GET /blogs/{id}` returning 200, 404 and 406. That 406 case exists because the spec sends an `Accept` header the controller does not support, which is the sort of detail that is easy to leave out of a hand-written document.

The practical consequence is that an endpoint cannot be documented without also being exercised. The failure mode is a stale document, which is the problem this approach removes.

One gem, or three you can separate

Installation starts with a Gemfile line. The whole toolchain is available as a single gem:

ruby
gem 'rswag'

The README then offers the split alternative, and the reason it gives is worth noting: if you would rather not load rspec in other bundler groups, you can install the components separately.

ruby
gem 'rswag-api'
gem 'rswag-ui'

group :development, :test do
  gem 'rspec-rails'
  gem 'rswag-specs'
end

The point of the split is that the specs gem stays inside the development and test group while the API and UI gems load in every environment, which keeps rspec out of your production bundle.

There is a bundler subtlety the README states in one sentence and that will otherwise cost you an afternoon. Adding the gem to the development group is not strictly necessary, but without it, the generators and rake tasks must be prefixed with `RAILS_ENV=test`. With the gems in place, install by running the generator:

ruby
rails g rswag:install

If you installed the components separately, run each one instead, and note that the specs generator still needs the environment prefix:

ruby
rails g rswag:api:install
rails g rswag:ui:install
RAILS_ENV=test rails g rswag:specs:install

For a first spec there is a generator too, `rails generate rspec:swagger API::MyController`, which scaffolds a file for a specific controller so you are not writing the DSL from memory.

The DSL covers more than paths and responses

The README's table of contents is the best available inventory of the feature set, and it is longer than a first-time user expects. Beyond the core, it documents null value handling, support for `oneOf`, `anyOf` and `allOf` schemas, global metadata, and a section on supporting multiple versions of an API through that metadata.

Request and response bodies are handled in two directions. `request_body_example` attaches a named example to a request, with a value, name and summary, which is how you get a populated example into the generated document rather than an empty schema. On the other side, the TOC lists response examples, response headers including nullable or optional ones, and a setting to enable automatic generation of examples from responses, with a dry run option so you can generate without running the assertions. There is also a documented mode for running the tests without documenting them, which matters when you want a spec that is purely a regression test.

Security is covered in its own section on specifying and testing API security, so authentication requirements can be declared in the DSL rather than described in prose. A helper methods section documents the rspec helpers available inside these specs.

The configuration surface is where most of the real work happens, and the TOC enumerates it: where generated OpenAPI files are written, where the specs are read from, how referenced parameters and schema definitions are resolved, the route prefix for the OpenAPI JSON endpoints, the root location for the OpenAPI files, dynamic values in the OpenAPI JSON, custom headers for those files, and how to enable, prefix, customise or protect the swagger-ui endpoints. One entry deserves attention in production: serving UI assets directly from your own web server, which avoids proxying every asset through Rails.

Two release lines, and a container that lags both

The release history has an odd shape that deserves explaining before you pick a version. The newest tag by date is 2.17.0 from 2025-11-05, a single change updating the gemspec to support Rails 8.1. But there is also 3.0.0.pre from 2025-08-01, three months older, and it is a prerelease of a major version that has not shipped as stable. Its changelog is a list of v3 branch merges: rebasing a long-running pull request, improving generation of OpenAPI 3.0 `requestBody` objects from `parameter` declarations, raising an error when an unsupported field is used in a Parameter object, merging Rails 8 support into v3, and removing the `.ruby-version` lock file.

So the practical read is that 3.0 is in progress on a branch while 2.x continues to receive maintenance fixes, and the newest stable is the one to install. The v3 work is worth knowing about because it changes how request bodies are generated, which means existing specs may need adjustment when you eventually move.

The other thing the release notes reveal is the Rails version support window. Version 2.16.0 added CI support for Rails 8, and 2.17.0 updated the gemspec for Rails 8.1. Against that, the Dockerfile at the repository root pins `ruby:3.0` with `RAILS_VERSION` set to 7.0.4.3. The container used for local development and the test suite is therefore running an older Rails than the gem claims to support. Both facts are in the repository; they are not contradictory in a way that breaks anything, but if you are testing against Rails 8.1 you are testing outside what this container exercises.

Developing rswag itself, and who is asking for help

The repository is a Ruby monorepo of four gems plus a test application. The tree shows `rswag/` as the meta-gem that bundles the others, `rswag-api/` for the OpenAPI generation and JSON endpoints, `rswag-specs/` for the rspec DSL, and `rswag-ui/` for the embedded swagger-ui. A `test-app/` directory is the Rails application those specs run against, and `ci/` holds the build and test scripts the container calls.

Local development runs in Docker, and the Makefile is a single build target that passes your host user id so files written inside the container belong to you rather than root:

bash
docker compose build --build-arg=USER_ID=$(shell id -u)

The compose file defines two services from a shared anchor. `test-app` builds from the repository root, maps port 3000 to the host and bind-mounts the working directory into `/rswag`. The `test` service reuses that configuration and overrides the command to run `./ci/test.sh`. The Dockerfile is careful about two things worth copying into your own images: it updates RubyGems to 3.4.22 with an inline comment linking a mini_racer issue, and it creates a non-root `appuser` with a configurable `USER_ID` so generated files do not need chowning afterwards. It runs `ci/build.sh` during the build and starts with `rails server -b 0.0.0.0` in the test-app directory.

The README opens by saying the project is seeking maintainers, and invites anyone who wants to fix a bug to say so in the issue or pull request. Given that this gem sits between your API and every consumer's generated client, that is a fair signal to weigh when deciding how much to depend on it. The repository does carry the usual governance files, with `CONTRIBUTING.md`, `SECURITY.md`, `CHANGELOG.md` and a `MIT-LICENCE` file matching the MIT license in the project metadata, plus rubocop configuration split between `.rubocop.yml` and a `.rubocop_todo.yml` of deferred offences.

Where rswag is the wrong choice

The obvious wrong fit is a project without rspec-rails request specs. Rswag's entire mechanism is that the document is a side effect of a test, so if your suite does not exist, the first thing you build is a test suite, and the documentation only comes afterwards. Hand-writing an OpenAPI file and validating it in CI is less work in that situation, even though it drifts.

The second wrong fit is a non-Rails or non-ruby backend. There is no other language binding here, and the DSL is rspec syntax, so a Node or Go service gets nothing from this gem.

The third is a team that treats the generated document as a contract to be reviewed rather than a by-product to be trusted. Rswag produces what your specs describe, which means a missing spec is a missing endpoint rather than an empty one, and a wrong assertion is a wrong document that still validates. That is a fair trade for internal APIs where the tests are the source of truth, and a poor one if the OpenAPI file is consumed by third parties who need it to be independently authored and reviewed.

The alternative worth naming is hand-maintained OpenAPI validated with a linter, or a separate documentation site. The difference in approach is direct: rswag derives the document from executable behaviour, so it cannot drift, but it cannot describe anything you have not also written a test for. Hand-written documentation can describe intended behaviour, planned endpoints and semantics that no test covers, at the cost of never being guaranteed accurate.

Editorial conclusion

Rswag is a good fit for a Rails API whose integration tests already exist, because the documentation then costs nothing extra to keep correct, and a poor fit for a team that would rather hand-write an OpenAPI file or has no rspec-rails suite at all. Two things to settle before installing. Choose the 2.x line, since 2.17.0 from 2025-11-05 is newer than the 3.0.0.pre prerelease from 2025-08-01 and carries the Rails 8.1 gemspec support, and be aware the README is openly asking for maintainers, which tells you how to weigh long-term dependency risk on a library your whole API contract generation depends on. Start with `rails g rswag:install`, write one spec for one endpoint with `run_test!`, and only then decide whether to split into the rswag-api, rswag-ui and rswag-specs gems.

Frequently asked questions

What is the rswag gem used for?

It adds a Swagger-based DSL to rspec-rails request specs so you describe and test an API operation in one place. Once the tests pass, a rake task generates the corresponding OpenAPI files and exposes them as YAML or JSON, and the rswag-ui component serves them in an embedded swagger-ui.

How do I install rswag in a Rails application?

Add `gem 'rswag'` to the Gemfile, or install rswag-api, rswag-ui and rswag-specs separately to keep rspec out of other bundler groups, then run `rails g rswag:install`. The split version needs `RAILS_ENV=test` in front of the specs generator.

Which rswag version should I use, 2.x or 3.0?

Version 2.17.0 from 2025-11-05 is the newest stable tag and includes gemspec support for Rails 8.1. The 3.0.0.pre prerelease dates from 2025-08-01 and its changelog consists of merges into the v3 branch, so the 2.x line is the one to install for now.

Does rswag let you add request and response examples?

Yes. The DSL includes request_body_example for attaching a named request example with a value and summary, and the README documents response examples, response headers including nullable ones, and an option to generate examples automatically from responses with a dry run variant.

How is the generated rswag OpenAPI file served?

The rswag-api gem exposes the generated OpenAPI as JSON endpoints, and the configuration sections cover the route prefix for those endpoints, the root location for the files, dynamic values and custom headers. The rswag-ui gem serves the swagger-ui interface, with separate settings for its route prefix, customisation, optional basic auth, and serving its assets straight from your web server.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. rswag/rswag on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/rswag-rswag.svg)](https://hysenlabs.com/projects/rswag-rswag)