# 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 1. [Overview](#overview) 2. [Accessing the Documentation](#accessing-the-documentation) 3. [Adding New Endpoints](#adding-new-endpoints) 4. [Documentation Patterns](#documentation-patterns) 5. [Regenerating Documentation](#regenerating-documentation) 6. [Best Practices](#best-practices) 7. [Troubleshooting](#troubleshooting) 8. [Examples](#examples) ## Overview This project uses [rswag](https://github.com/rswag/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 1. **Start the Rails server:** ```bash rails server # or bin/dev ``` 2. **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 3. **Access Raw Swagger YAML:** - Direct URL: `http://localhost:3000/api-docs/v1/swagger.yaml` - Useful for importing into other tools (Postman, Insomnia, etc.) ### Production Environment The Swagger UI is typically only enabled in development. To enable in production: 1. Update `config/initializers/rswag_ui.rb` to conditionally mount 2. Consider adding authentication for production access 3. 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): ```ruby # 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: ```bash RAILS_ENV=test bundle exec rake rswag:specs:swaggerize ``` ### Step 3: Verify 1. Restart your Rails server 2. Visit `http://localhost:3000/api-docs` 3. Verify your new endpoint appears in the documentation ## Documentation Patterns ### GET Endpoint ```ruby 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 ```ruby 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 ```ruby 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 ```ruby 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 ```ruby 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 ```ruby 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: ```bash 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: ```ruby # 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: ```bash #!/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:** 1. Verify routes are mounted: `rails routes | grep api-docs` 2. Restart the Rails server 3. Check `config/initializers/rswag_ui.rb` configuration ### Issue: Endpoints not appearing in documentation **Solution:** 1. Verify spec file is in `spec/requests/swagger/` directory 2. Ensure spec uses `require 'swagger_helper'` 3. Regenerate documentation: `rake rswag:specs:swaggerize` 4. Restart the server ### Issue: Authentication not working in Swagger UI **Solution:** 1. Check security scheme in `swagger_helper.rb` 2. Verify `security [bearerAuth: []]` is included in endpoint specs 3. Check if authentication is properly configured in the application ### Issue: Generated YAML is empty or incomplete **Solution:** 1. Check that specs actually run: `rspec spec/requests/swagger/` 2. Verify `run_test!` is called in each response block 3. Check for errors in spec execution 4. Ensure factories and test data are properly set up ### Issue: Schema validation errors **Solution:** 1. Verify schema syntax matches OpenAPI 3.0.1 specification 2. Check that property types are valid (string, integer, object, array, etc.) 3. Ensure required fields are properly marked 4. 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 - [OpenAPI 3.0 Specification](https://swagger.io/specification/) - [rswag GitHub Repository](https://github.com/rswag/rswag) - [Swagger UI Documentation](https://swagger.io/tools/swagger-ui/) - [RSpec Request Specs](https://rspec.info/documentation/latest/rspec-rails/RSpec/Rails/ExampleGroup/RequestExampleGroup.html) ## Quick Reference ### Common Schema Types ```ruby # 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 resource - `put` - Update resource (full) - `patch` - Update resource (partial) - `delete` - Delete resource ### Common Tags - `OTP` - One-time password operations - `Health` - Health check endpoints - `Jobs Monitor` - Job queue monitoring - `Prompts` - Autocomplete/prompt endpoints - `SES Webhooks` - AWS SES webhook handling - `Unfurl` - URL metadata extraction --- **Last Updated:** December 2024 **Maintained By:** Development Team