API Docs with Swagger
Swagger API Documentation Guide
This guide explains how to use, maintain, and extend the Swagger/OpenAPI documentation for the ecommerce application.
Table of Contents
- Overview
- Accessing the Documentation
- Adding New Endpoints
- Documentation Patterns
- Regenerating Documentation
- Best Practices
- Troubleshooting
- Examples
Overview
This project uses rswag to generate Swagger/OpenAPI 3.0.1 documentation from RSpec tests. The documentation is automatically generated from test specs, ensuring it stays in sync with the actual API implementation.
Key Components
- rswag: Main gem for Swagger generation
- rswag-api: Serves the Swagger JSON/YAML files
- rswag-ui: Provides the interactive Swagger UI
- rswag-specs: Generates Swagger from RSpec tests
File Structure
spec/
swagger_helper.rb # Main Swagger configuration
requests/
swagger/
v1/
otp_spec.rb # OTP API documentation
unfurl_spec.rb # Unfurl API documentation
ses_webhooks_spec.rb # SES Webhooks documentation
health_spec.rb # Health check endpoints
jobs_monitor_spec.rb # Jobs monitoring endpoints
prompts_spec.rb # Prompts endpoints
rails_health_spec.rb # Rails health check
swagger/
v1/
swagger.yaml # Generated Swagger specification
config/
initializers/
rswag_api.rb # API configuration
rswag_ui.rb # UI configuration
Accessing the Documentation
Development Environment
- Start the Rails server:
rails server # or bin/dev - Access Swagger UI:
- Open your browser to:
http://localhost:3000/api-docs - You’ll see an interactive interface to explore and test all API endpoints
- Open your browser to:
- Access Raw Swagger YAML:
- Direct URL:
http://localhost:3000/api-docs/v1/swagger.yaml - Useful for importing into other tools (Postman, Insomnia, etc.)
- Direct URL:
Production Environment
The Swagger UI is typically only enabled in development. To enable in production:
- Update
config/initializers/rswag_ui.rbto conditionally mount - Consider adding authentication for production access
- Ensure the
swagger/directory is deployed with your application
Adding New Endpoints
Step 1: Create a Swagger Spec File
Create a new spec file in spec/requests/swagger/ (or appropriate subdirectory):
# spec/requests/swagger/v1/new_endpoint_spec.rb
# frozen_string_literal: true
require 'swagger_helper'
RSpec.describe 'New Endpoint API', type: :request do
path '/api/v1/new_endpoint' do
post 'Create new resource' do
tags 'New Resource'
description 'Creates a new resource with the provided parameters'
consumes 'application/json'
produces 'application/json'
security [bearerAuth: []]
parameter name: :new_resource, in: :body, schema: {
type: :object,
properties: {
name: { type: :string, example: 'Example Name' },
description: { type: :string, example: 'Example description' }
},
required: ['name']
}
response '201', 'Resource created successfully' do
schema type: :object,
properties: {
id: { type: :integer, example: 1 },
name: { type: :string, example: 'Example Name' },
created_at: { type: :string, format: :date_time }
},
required: ['id', 'name']
let(:user) { create(:user) }
let(:Authorization) { "Bearer #{user.id}" }
let(:new_resource) { { name: 'Example Name' } }
before do
sign_in(user, scope: :user)
end
run_test!
end
response '422', 'Validation error' do
schema type: :object,
properties: {
errors: {
type: :array,
items: { type: :string },
example: ['Name can\'t be blank']
}
}
let(:user) { create(:user) }
let(:Authorization) { "Bearer #{user.id}" }
let(:new_resource) { { name: '' } }
before do
sign_in(user, scope: :user)
end
run_test!
end
response '401', 'Unauthorized' do
schema type: :object
let(:new_resource) { { name: 'Example Name' } }
run_test!
end
end
end
end
Step 2: Regenerate Swagger Documentation
After creating your spec, regenerate the Swagger file:
RAILS_ENV=test bundle exec rake rswag:specs:swaggerize
Step 3: Verify
- Restart your Rails server
- Visit
http://localhost:3000/api-docs - Verify your new endpoint appears in the documentation
Documentation Patterns
GET Endpoint
path '/api/v1/resources' do
get 'List resources' do
tags 'Resources'
description 'Returns a paginated list of resources'
produces 'application/json'
security [bearerAuth: []]
parameter name: :page, in: :query, type: :integer, required: false, example: 1
parameter name: :per_page, in: :query, type: :integer, required: false, example: 25
response '200', 'Resources retrieved successfully' do
schema type: :object,
properties: {
resources: {
type: :array,
items: {
type: :object,
properties: {
id: { type: :integer },
name: { type: :string }
}
}
},
pagination: {
type: :object,
properties: {
current_page: { type: :integer },
total_pages: { type: :integer }
}
}
}
let(:user) { create(:user) }
let(:Authorization) { "Bearer #{user.id}" }
before do
sign_in(user, scope: :user)
end
run_test!
end
end
end
POST Endpoint with Body Parameters
path '/api/v1/resources' do
post 'Create resource' do
tags 'Resources'
consumes 'application/json'
produces 'application/json'
security [bearerAuth: []]
parameter name: :resource, in: :body, schema: {
type: :object,
properties: {
name: { type: :string, example: 'Resource Name' },
description: { type: :string, example: 'Resource description' }
},
required: ['name']
}
response '201', 'Resource created' do
# ... response schema
end
end
end
Endpoint with Path Parameters
path '/api/v1/resources/{id}' do
get 'Get resource' do
tags 'Resources'
produces 'application/json'
security [bearerAuth: []]
parameter name: :id, in: :path, type: :integer, required: true, example: 1
response '200', 'Resource found' do
schema type: :object,
properties: {
id: { type: :integer, example: 1 },
name: { type: :string, example: 'Resource Name' }
}
let(:user) { create(:user) }
let(:Authorization) { "Bearer #{user.id}" }
let(:id) { create(:resource).id }
before do
sign_in(user, scope: :user)
end
run_test!
end
response '404', 'Resource not found' do
let(:user) { create(:user) }
let(:Authorization) { "Bearer #{user.id}" }
let(:id) { 99999 }
before do
sign_in(user, scope: :user)
end
run_test!
end
end
end
Endpoint with Query Parameters
path '/api/v1/resources/search' do
get 'Search resources' do
tags 'Resources'
produces 'application/json'
parameter name: :q, in: :query, type: :string, description: 'Search query', required: false
parameter name: :category, in: :query, type: :string, required: false
parameter name: :limit, in: :query, type: :integer, required: false, example: 10
response '200', 'Search results' do
# ... response schema
end
end
end
Endpoint Without Authentication
path '/health' do
get 'Health check' do
tags 'Health'
produces 'application/json'
# Note: No security parameter means no authentication required
response '200', 'Service is healthy' do
# ... response schema
end
end
end
Multiple Response Codes
response '200', 'Success' do
# ... success schema
end
response '400', 'Bad Request' do
schema type: :object,
properties: {
error: { type: :string, example: 'Invalid parameters' }
}
end
response '401', 'Unauthorized' do
schema type: :object
end
response '403', 'Forbidden' do
schema type: :object,
properties: {
error: { type: :string, example: 'Access denied' }
}
end
response '404', 'Not Found' do
schema type: :object,
properties: {
error: { type: :string, example: 'Resource not found' }
}
end
response '422', 'Unprocessable Entity' do
schema type: :object,
properties: {
errors: {
type: :array,
items: { type: :string },
example: ['Validation error message']
}
}
end
response '429', 'Too Many Requests' do
schema type: :object,
properties: {
error: { type: :string, example: 'Rate limit exceeded' }
}
end
response '500', 'Internal Server Error' do
schema type: :object,
properties: {
error: { type: :string, example: 'Internal server error' }
}
end
Regenerating Documentation
After Adding/Modifying Specs
Whenever you add or modify Swagger spec files, regenerate the documentation:
RAILS_ENV=test bundle exec rake rswag:specs:swaggerize
Automated Regeneration
You can add this to your CI/CD pipeline or create a rake task:
# lib/tasks/swagger.rake
namespace :swagger do
desc 'Generate Swagger documentation'
task generate: :environment do
require 'rspec/core'
require 'rswag/specs/swagger_formatter'
RSpec::Core::Runner.run(
['spec/requests/swagger'],
$stderr,
$stdout
)
end
end
Git Hook (Optional)
Add a pre-commit hook to ensure documentation is up-to-date:
#!/bin/sh
# .git/hooks/pre-commit
RAILS_ENV=test bundle exec rake rswag:specs:swaggerize
git add swagger/v1/swagger.yaml
Best Practices
1. Keep Documentation in Sync
- Always update Swagger specs when modifying API endpoints
- Regenerate documentation after changes
- Review generated YAML to ensure accuracy
2. Comprehensive Documentation
- Document all possible response codes
- Include request/response examples
- Add clear descriptions for each endpoint
- Document all parameters (required vs optional)
3. Consistent Naming
- Use consistent tag names for grouping endpoints
- Follow RESTful naming conventions
- Use clear, descriptive endpoint names
4. Security Documentation
- Always document authentication requirements
- Include security schemes in swagger_helper.rb
- Document authorization levels if applicable
5. Error Handling
- Document all possible error responses
- Include error message formats
- Provide examples of error responses
6. Versioning
- Use API versioning in paths (
/api/v1/,/api/v2/) - Create separate Swagger specs for different versions
- Document deprecation notices for old versions
7. Testing
- Ensure Swagger specs actually run and pass
- Use realistic test data in examples
- Test error cases, not just success cases
Troubleshooting
Issue: Swagger UI shows 404
Solution:
- Verify routes are mounted:
rails routes | grep api-docs - Restart the Rails server
- Check
config/initializers/rswag_ui.rbconfiguration
Issue: Endpoints not appearing in documentation
Solution:
- Verify spec file is in
spec/requests/swagger/directory - Ensure spec uses
require 'swagger_helper' - Regenerate documentation:
rake rswag:specs:swaggerize - Restart the server
Issue: Authentication not working in Swagger UI
Solution:
- Check security scheme in
swagger_helper.rb - Verify
security [bearerAuth: []]is included in endpoint specs - Check if authentication is properly configured in the application
Issue: Generated YAML is empty or incomplete
Solution:
- Check that specs actually run:
rspec spec/requests/swagger/ - Verify
run_test!is called in each response block - Check for errors in spec execution
- Ensure factories and test data are properly set up
Issue: Schema validation errors
Solution:
- Verify schema syntax matches OpenAPI 3.0.1 specification
- Check that property types are valid (string, integer, object, array, etc.)
- Ensure required fields are properly marked
- Validate examples match the schema
Examples
Complete Example: CRUD Operations
See spec/requests/swagger/v1/otp_spec.rb for a complete example of:
- Multiple endpoints (send, verify, resend)
- Request/response schemas
- Error handling
- Authentication
- Rate limiting documentation
Example: Health Check Endpoint
See spec/requests/swagger/health_spec.rb for:
- Simple GET endpoints
- Multiple response codes
- No authentication required
Example: Jobs Monitoring
See spec/requests/swagger/jobs_monitor_spec.rb for:
- Pagination parameters
- Path parameters
- Complex response schemas
- Array responses
Additional Resources
Quick Reference
Common Schema Types
# String
{ type: :string, example: 'example' }
# Integer
{ type: :integer, example: 123 }
# Boolean
{ type: :boolean, example: true }
# Date/DateTime
{ type: :string, format: :date_time, example: '2025-01-01T00:00:00Z' }
# Array
{ type: :array, items: { type: :string } }
# Object
{ type: :object, properties: { ... } }
# Nullable
{ type: :string, nullable: true }
Common HTTP Methods
get- Retrieve resource(s)post- Create resourceput- Update resource (full)patch- Update resource (partial)delete- Delete resource
Common Tags
OTP- One-time password operationsHealth- Health check endpointsJobs Monitor- Job queue monitoringPrompts- Autocomplete/prompt endpointsSES Webhooks- AWS SES webhook handlingUnfurl- URL metadata extraction
Last Updated: December 2024
Maintained By: Development Team