An open API service indexing awesome lists of open source software.

https://github.com/s13g/docit

Simple, flexible, automatic API documentation for Ruby APIs.
https://github.com/s13g/docit

openapi-generator openapi-specification openapi3 restapi ruby ruby-on-rails swagger

Last synced: 2 months ago
JSON representation

Simple, flexible, automatic API documentation for Ruby APIs.

Awesome Lists containing this project

README

          

# Docit

[![Ruby](https://img.shields.io/badge/ruby-%3E%3D%203.2-red.svg)](https://www.ruby-lang.org)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

Decorator-style API documentation for Ruby on Rails. Write OpenAPI 3.0.3 docs with clean controller DSL macros, separate doc modules, or AI-assisted scaffolding for undocumented endpoints.

### Scalar (default)
![Scalar API Reference](docs/images/scalar_image.png)

### Swagger
![Swagger UI](docs/images/swagger_image.png)

## Table Of Contents

- Getting started
- [Installation](#installation)
- [Configuration](#configuration)
- [Usage](#usage)
- Documentation styles
- [Style 1: Inline (simple APIs)](#style-1-inline-simple-apis)
- [Style 2: Separate doc files (recommended for larger APIs)](#style-2-separate-doc-files-recommended-for-larger-apis)
- Endpoint DSL reference
- [Endpoint documentation DSL](#endpoint-documentation-dsl)
- [Request bodies](#request-bodies)
- [Path parameters](#path-parameters)
- [Enums](#enums)
- [Security](#security)
- [Deprecated endpoints](#deprecated-endpoints)
- [Nested objects and arrays](#nested-objects-and-arrays)
- [Response examples](#response-examples)
- [Shared schemas (`$ref`)](#shared-schemas-ref)
- [File uploads](#file-uploads)
- AI documentation
- [AI Automatic Documentation](#ai-automatic-documentation)
- [Quick start (included in install)](#quick-start-included-in-install)
- [Standalone commands](#standalone-commands)
- [Supported providers](#supported-providers)
- [What the AI generates](#what-the-ai-generates)
- Runtime and development
- [Documentation UIs](#documentation-uis)
- [How it works](#how-it-works)
- [Mounting at a different path](#mounting-at-a-different-path)
- [JSON spec only](#json-spec-only)
- [Development](#development)
- [Contributing](#contributing)
- [License](#license)
- Project docs
- [CHANGELOG](CHANGELOG.md)
- [CONTRIBUTING](CONTRIBUTING.md)
- [CODE OF CONDUCT](CODE_OF_CONDUCT.md)

## Installation

Add Docit to your Gemfile:

```ruby
gem "docit"
```

Then run:

```bash
bundle install
rails generate docit:install
```

The install generator does everything in one step:

1. Creates `config/initializers/docit.rb` with default settings
2. Mounts the documentation engine at `/api-docs` in your routes
3. Asks how you'd like to set up your docs:
- **AI automatic docs** — configure an AI provider, then Docit scans your routes and generates complete documentation for every endpoint
- **Manual docs** — Docit scans your routes and creates scaffolded doc files with TODO placeholders, injects `use_docs` into controllers, and lets you fill in the details
- **Skip** — just install the base config and set up docs later

Visit `/api-docs` to see your interactive API documentation (Scalar by default, Swagger UI also available at `/api-docs/swagger`).

If you choose AI setup, Docit stores your provider config in `.docit_ai.yml` with restricted file permissions and adds that file to `.gitignore` when possible.

## Configuration

Edit `config/initializers/docit.rb`:

```ruby
Docit.configure do |config|
config.title = "My API"
config.version = "1.0.0"
config.description = "Backend API documentation"

# Documentation UI: :scalar (default) or :swagger
config.default_ui = :scalar

# Authentication: pick one (or multiple):
config.auth :bearer # Bearer token (JWT by default)
config.auth :basic # HTTP Basic
config.auth :api_key, name: "X-API-Key", # API key in header
location: "header"

# Tag descriptions (shown in the documentation sidebar):
config.tag "Users", description: "User account management"
config.tag "Auth", description: "Authentication endpoints"

# Server URLs (shown in the server dropdown):
config.server "https://api.example.com", description: "Production"
config.server "https://staging.example.com", description: "Staging"
config.server "http://localhost:3000", description: "Development"
end
```

## Usage

Docit supports two styles for documenting endpoints. Choose whichever fits your project or mix both.

### Style 1: Inline (simple APIs)

Add `swagger_doc` blocks directly in your controller:

```ruby
class Api::V1::UsersController < ApplicationController
swagger_doc :index do
summary "List all users"
tags "Users"
response 200, "Users retrieved"
end
def index
# your code
end
end
```

### Style 2: Separate doc files (recommended for larger APIs)

Keep controllers clean by defining docs in dedicated files:

```ruby
# app/docs/api/v1/users_docs.rb
module Api::V1::UsersDocs
extend Docit::DocFile

doc :index do
summary "List all users"
description "Returns a paginated list of users"
tags "Users"

parameter :page, location: :query, type: :integer, description: "Page number"

response 200, "Users retrieved" do
property :users, type: :array, items: :object do
property :id, type: :integer, example: 1
property :email, type: :string, example: "user@example.com"
end
property :total, type: :integer, example: 42
end
end

doc :create do
summary "Create a user"
tags "Users"

request_body required: true do
property :email, type: :string, required: true
property :password, type: :string, required: true, format: :password
end

response 201, "User created" do
property :id, type: :integer
end

response 422, "Validation failed" do
property :errors, type: :object do
property :email, type: :array, items: :string
end
end
end
end

# app/controllers/api/v1/users_controller.rb — stays clean!
class Api::V1::UsersController < ApplicationController
use_docs Api::V1::UsersDocs

def index
# pure business logic
end

def create
# pure business logic
end
end
```

You can also mix both styles — use `use_docs` for most actions and add inline `swagger_doc` for one-offs:

```ruby
class Api::V1::UsersController < ApplicationController
use_docs Api::V1::UsersDocs # loads :index and :create from doc file

swagger_doc :destroy do # inline doc for this one action
summary "Delete user"
tags "Users"
response 204, "Deleted"
end

def index; end
def create; end
def destroy; end
end
```

### Endpoint documentation DSL

The following examples work in both `swagger_doc` blocks and `doc` blocks.

### Request bodies

```ruby
swagger_doc :create do
summary "Create a user"
tags "Users"

request_body required: true do
property :email, type: :string, required: true, example: "user@example.com"
property :password, type: :string, required: true, format: :password
property :name, type: :string, example: "Jane Doe"
property :profile, type: :object do
property :bio, type: :string
property :avatar_url, type: :string, format: :uri
end
end

response 201, "User created" do
property :id, type: :integer, example: 1
property :email, type: :string, example: "user@example.com"
end

response 422, "Validation failed" do
property :errors, type: :object do
property :email, type: :array, items: :string
end
end
end
def create
# your code
end
```

### Path parameters

```ruby
swagger_doc :show do
summary "Get a user"
tags "Users"

parameter :id, location: :path, type: :integer, required: true, description: "User ID"

response 200, "User found" do
property :id, type: :integer, example: 1
property :email, type: :string
property :name, type: :string
end

response 404, "User not found" do
property :error, type: :string, example: "Not found"
end
end
def show
# your code
end
```

### Enums

```ruby
swagger_doc :index do
summary "List orders"
tags "Orders"

parameter :status, location: :query, type: :string,
enum: %w[pending shipped delivered],
description: "Filter by status"

response 200, "Orders list" do
property :orders, type: :array do
property :id, type: :integer
property :status, type: :string, enum: %w[pending shipped delivered]
end
end
end
```

### Security

Mark endpoints as requiring authentication:

```ruby
swagger_doc :destroy do
summary "Delete a user"
tags "Users"
security :bearer_auth # references the scheme from your config

response 204, "User deleted"
response 401, "Unauthorized"
end
```

### Deprecated endpoints

```ruby
swagger_doc :legacy_search do
summary "Search (legacy)"
tags "Search"
deprecated

response 200, "Results"
end
```

### Nested objects and arrays

```ruby
response 200, "Success" do
property :user, type: :object do
property :id, type: :integer
property :name, type: :string
property :addresses, type: :array do
property :street, type: :string
property :city, type: :string
property :zip, type: :string
end
end
end
```

### Response examples

```ruby
response 200, "User found" do
property :id, type: :integer
property :email, type: :string

example "admin_user",
{ id: 1, email: "admin@example.com" },
description: "An admin user"

example "regular_user",
{ id: 2, email: "user@example.com" },
description: "A regular user"
end
```

### Shared schemas (`$ref`)

Define reusable schemas once and reference them across multiple endpoints:

```ruby
# In config/initializers/docit.rb or a dedicated file:
Docit.define_schema :User do
property :id, type: :integer, example: 1
property :email, type: :string, example: "user@example.com"
property :name, type: :string, example: "Jane Doe"
property :address, type: :object do
property :street, type: :string
property :city, type: :string
end
end

Docit.define_schema :Error do
property :error, type: :string, example: "Not found"
property :details, type: :array, items: :string
end
```

Reference them in any endpoint with `schema ref:`:

```ruby
swagger_doc :show do
summary "Get user"
tags "Users"

response 200, "User found" do
schema ref: :User
end

response 404, "Not found" do
schema ref: :Error
end
end

swagger_doc :create do
summary "Create user"
tags "Users"

request_body required: true do
schema ref: :User
end

response 201, "Created" do
schema ref: :User
end
end
```

This outputs `$ref: '#/components/schemas/User'` in the spec — Swagger UI resolves it automatically.

### File uploads

Use `type: :file` with `content_type: "multipart/form-data"` for file upload endpoints:

```ruby
swagger_doc :upload_avatar do
summary "Upload avatar"
tags "Users"

request_body required: true, content_type: "multipart/form-data" do
property :avatar, type: :file, required: true, description: "Avatar image"
property :caption, type: :string
end

response 201, "Avatar uploaded" do
property :url, type: :string, format: :uri
end
end
```

`type: :file` maps to `{ type: "string", format: "binary" }` in the OpenAPI spec.

## AI Automatic Documentation

Docit can generate complete API documentation using AI. This works with OpenAI, Anthropic, or Groq (free tier available).

### Quick start (included in install)

When you run `rails generate docit:install` and choose option 1 (AI automatic docs), everything is set up automatically — provider configuration, doc generation, controller wiring, and tag injection.

Before the first AI request, Docit warns that your controller source code will be sent to the selected provider and asks for confirmation in interactive terminals.

### Standalone commands

You can also set up AI docs separately:

```bash
# Configure your AI provider (one-time setup)
rails generate docit:ai_setup

# Generate docs for all undocumented endpoints
rails docit:autodoc

# Generate docs for a specific controller
rails docit:autodoc[Api::V1::UsersController]

# Preview what would be generated without writing files
DRY_RUN=1 rails docit:autodoc
```

### Supported providers

| Provider | Notes |
|------------|-------|
| OpenAI | Requires API key from platform.openai.com |
| Anthropic | Requires API key from console.anthropic.com |
| Groq | Free tier at console.groq.com |

All providers automatically retry on rate-limit (429) errors with exponential backoff, so free-tier usage works out of the box.

AI configuration is stored in `.docit_ai.yml`.
If your app does not have a `.gitignore`, add `.docit_ai.yml` manually.

### What the AI generates

For each undocumented endpoint, Docit:

1. Reads the controller source code
2. Inspects the route (HTTP method, path, parameters)
3. Writes the generated doc block to `app/docs/`
4. Injects `use_docs` into the controller
5. Adds tag descriptions to the initializer

Do not use AI autodoc on controllers that contain secrets, proprietary business rules, or internal comments you do not want sent to an external provider.

## Documentation UIs

Docit ships with two documentation UIs, both reading from the same OpenAPI spec:

| Path | UI | Notes |
|------|------|-------|
| `/api-docs` | Default (Scalar) | Configurable via `config.default_ui` |
| `/api-docs/scalar` | Scalar API Reference | Modern UI with built-in API client, dark mode, code samples |
| `/api-docs/swagger` | Swagger UI | Classic OpenAPI explorer |
| `/api-docs/spec` | Raw JSON | OpenAPI 3.0.3 spec |

Both UIs include a navigation bar to switch between them. Set `config.default_ui = :swagger` to make Swagger the default at `/api-docs`.

## How it works

1. `swagger_doc` registers an **Operation** for each controller action in a global **Registry**
2. When someone visits `/api-docs/spec`, Docit's **SchemaGenerator** combines all registered operations with your Rails routes (via **RouteInspector**) to produce an OpenAPI 3.0.3 JSON document
3. The **Engine** serves the configured documentation UI at `/api-docs`, pointing it at the generated spec

The DSL is included in all controllers automatically via a Rails Engine initializer — no manual `include` needed if you're using `ActionController::API` or `ActionController::Base`.

## Mounting at a different path

In `config/routes.rb`:

```ruby
mount Docit::Engine => "/docs" # now at /docs instead of /api-docs
```

## JSON spec only

If you just want the raw OpenAPI JSON (e.g., for code generation):

```
GET /api-docs/spec
```

## Development

```bash
git clone https://github.com/S13G/docit.git
cd docit
bundle install
bundle exec rspec # run all tests
```

## Contributing

Bug reports and pull requests are welcome on [GitHub](https://github.com/S13G/docit).

## License

[MIT](LICENSE.txt)