A comprehensive Ruby on Rails API template for building RESTful APIs with authentication, testing, and documentation. This template provides a clean foundation with user management, OAuth authentication, and comprehensive testing setup.
- RESTful API with JSON responses and proper HTTP status codes
- Comprehensive Testing with RSpec, FactoryBot, and Shoulda Matchers
- API Template Documentation with Swagger/OpenAPI via RSwag
- User Management with secure authentication
- OAuth 2.0 Provider via Doorkeeper for secure API template access
- Secure Password Handling with bcrypt encryption
- Modern Rails 7.1 architecture
- PostgreSQL database backend
- Production-Ready Security with proper authentication flows
- JSON API Serialization with ActiveModelSerializers
- Custom Renderer System for consistent API template responses
- Clean Foundation ready for your domain models
- Ruby: 3.2.2
- Rails: 7.1.5.2
- Database: PostgreSQL
- Testing: RSpec, FactoryBot, Shoulda Matchers
- API Docs: RSwag (Swagger/OpenAPI)
- Authentication: OAuth 2.0 via Doorkeeper
- Security: bcrypt for password encryption
- Serialization: ActiveModelSerializers with JSON API format
- Template Features: Ready-to-use API structure with comprehensive examples
- Ruby 3.2.2
- PostgreSQL
- Bundler
This API template is production-ready with:
- β All tests passing (25 examples, 0 failures)
- β OAuth 2.0 authentication fully implemented
- β bcrypt password security integrated
- β Comprehensive test coverage with RSpec
- β Swagger documentation ready for generation
- β Enterprise-grade security implemented
- β ActiveModelSerializers integrated with JSON API format
- β Custom renderer system for consistent API template responses
git clone https://github.com/fdecono/rails-api-template
cd rails-api-templatebundle install# Start PostgreSQL service (if not running)
brew services start postgresql@16
# Create and setup database
rails db:create
rails db:migrate
rails db:seedNote: The database includes migrations for the core functionality:
- User management with secure authentication
- OAuth 2.0 provider setup via Doorkeeper
- Clean foundation ready for your domain models
rails serverThe API template will be available at http://localhost:3000
# Run all tests
bundle exec rspec
# Run specific test file
bundle exec rspec spec/requests/api/v1/users_spec.rb
# Run serializer tests
bundle exec rspec spec/serializers/
# Run with documentation format
bundle exec rspec -fdspec/models/- Model tests with shared examplesspec/controllers/- Controller testsspec/requests/- API template endpoint tests with RSwag and OAuthspec/serializers/- Serializer tests for ActiveModelSerializersspec/factories/- FactoryBot factories for test dataspec/shared_examples/- Reusable test examplesspec/shared_contexts/- Reusable test contexts
The test suite includes OAuth authentication testing:
# Example from spec/requests/api/v1/users_spec.rb
let(:application) { Doorkeeper::Application.create!(name: 'Test App', redirect_uri: 'http://localhost:3000') }
let(:access_token) { Doorkeeper::AccessToken.create!(application: application, resource_owner_id: users.first.id, scopes: 'read') }
let(:Authorization) { "Bearer #{access_token.token}" }This ensures all protected endpoints are properly tested with valid OAuth tokens.
The test suite also includes comprehensive testing for OAuth application management:
# Example from spec/requests/api/v1/oauth_applications_spec.rb
let(:oauth_app) { create(:oauth_application) }
let(:access_token) { Doorkeeper::AccessToken.create!(application: application, resource_owner_id: user.id, scopes: 'write') }Tests cover CRUD operations for OAuth applications with proper authentication and validation.
# In your model specs
RSpec.describe User, type: :model do
include_examples "common validations", User, {
presence: [:email, :password],
uniqueness: [:email],
length: { password: { minimum: 6 } }
}
endAccess the interactive API template documentation at:
http://localhost:3000/api-docs
# Generate OpenAPI specs from RSpec tests
bundle exec rspec spec/requests/api/v1/users_spec.rb --format Rswag::Specs::SwaggerFormatterrails console# Create a new OAuth application
app = Doorkeeper::Application.create!(
name: 'My App',
redirect_uri: 'https://oauth.pstmn.io/v1/callback', # For Postman testing
scopes: 'read write'
)
puts "Client ID: #{app.uid}"
puts "Client Secret: #{app.secret}"curl -X POST http://localhost:3000/api/v1/oauth_applications \
-H "Content-Type: application/json" \
-d '{
"oauth_application": {
"name": "My App",
"redirect_uri": "https://oauth.pstmn.io/v1/callback",
"scopes": "read write"
}
}'curl -X POST http://localhost:3000/oauth/token \
--header 'Authorization: Basic [base64_encoded_client_credentials]' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'username=user@example.com' \
--data-urlencode 'password=password123'- Method:
POST - URL:
http://localhost:3000/oauth/token - Authorization Tab:
- Type:
Basic Auth - Username:
[Your Client ID] - Password:
[Your Client Secret]
- Type:
- Body (x-www-form-urlencoded):
grant_type: password username: user@example.com password: password123
{
"access_token": "abc123def456...",
"token_type": "Bearer",
"expires_in": 7200,
"refresh_token": "def456ghi789...",
"scope": "read write"
}curl -H "Authorization: Bearer abc123def456..." \
http://localhost:3000/api/v1/users- Authorization Tab:
Bearer Token - Token:
[Your Access Token]
When access tokens expire, use the refresh token:
curl -X POST http://localhost:3000/oauth/token \
--header 'Authorization: Basic [base64_encoded_client_credentials]' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'refresh_token=[your_refresh_token]'| Endpoint | Method | Description |
|---|---|---|
/oauth/token |
POST | Get access token (password grant) |
/oauth/token |
POST | Refresh access token |
/oauth/revoke |
POST | Revoke access token |
/oauth/introspect |
POST | Introspect token |
/oauth/applications |
GET/POST | Manage OAuth applications |
Common OAuth errors and solutions:
| Error | Description | Solution |
|---|---|---|
invalid_grant |
Invalid credentials or client | Check username/password and client credentials |
invalid_client |
Invalid client ID/secret | Verify OAuth application credentials |
invalid_scope |
Requested scope not allowed | Check application scopes |
unauthorized_client |
Client not authorized for grant type | Verify grant type configuration |
The API template uses ActiveModelSerializers to provide consistent, well-structured JSON responses following the JSON API specification.
- JSON API Format: Standardized JSON structure with
data,type, andattributes - Automatic Serialization: Models are automatically serialized using appropriate serializers
- Consistent Response Format: All API template responses follow the same structure
- Custom Attributes: Easy to add computed or conditional attributes
- Collection Support: Automatic handling of single objects and collections
All API template responses follow this structure:
{
"data": {
"id": "1",
"type": "users",
"attributes": {
"email": "user@example.com",
"firstName": "John",
"lastName": "Doe"
}
}
}{
"data": [
{
"id": "1",
"type": "users",
"attributes": { ... }
},
{
"id": "2",
"type": "users",
"attributes": { ... }
}
],
"meta": {
"totalCount": 2
}
}The API template includes a custom renderer system for consistent responses:
Renderer: Base renderer class with common functionalityObjectRenderer: Renders single objectsCollectionRenderer: Renders collections
Controllers can include the ResponseRenderer concern for easy rendering:
class UsersController < ApiController
include ResponseRenderer
def show
render_object @user
end
def index
render_collection @users
end
end- Inherit from ActiveModel::Serializer:
class UserSerializer < ActiveModel::Serializer
attributes :email, :first_name, :last_name
def full_name
"#{object.first_name} #{object.last_name}"
end
end- Add Custom Methods:
def custom_attribute
# Your custom logic here
object.some_computed_value
end- Conditional Attributes:
def attributes(*args)
hash = super
hash[:admin] = object.admin? if object.respond_to?(:admin?)
hash
endActiveModelSerializers is configured in config/initializers/active_model_serializers.rb:
- Adapter: JSON API format
- Key Transform: camelCase (e.g.,
created_atβcreatedAt) - Serializer Lookup: Automatic serializer discovery
The API template provides a clean foundation with core functionality:
- Authentication: Secure password handling with bcrypt
- Features: Email, first name, last name, admin status
- Methods:
full_name,admin?,confirmed? - OAuth Integration: Ready for Doorkeeper authentication
- User Management: Complete user CRUD operations
- Authentication: OAuth 2.0 provider setup
- Security: bcrypt password encryption
- Testing: Comprehensive test coverage with RSpec
- API Documentation: Swagger/OpenAPI ready
This template follows a clean foundation approach that provides:
- Essential Infrastructure: User management, authentication, testing
- Modern Rails: Rails 7.1 with latest best practices
- Production Ready: OAuth, security, and deployment ready
- Extensible: Clean slate for your domain models
- Well Tested: Comprehensive test suite as examples
GET /api/v1/users- List all users (requires authentication)POST /api/v1/users- Create a new userGET /api/v1/users/:id- Get user detailsPUT /api/v1/users/:id- Update userDELETE /api/v1/users/:id- Delete user
GET /api/v1/oauth_applications- List OAuth applicationsPOST /api/v1/oauth_applications- Create OAuth applicationGET /api/v1/oauth_applications/:id- Get OAuth application detailsPUT /api/v1/oauth_applications/:id- Update OAuth applicationDELETE /api/v1/oauth_applications/:id- Delete OAuth application
The API template uses OAuth 2.0 with Doorkeeper for secure, industry-standard authentication.
- Register OAuth Application β Get Client ID and Secret
- User Authentication β Exchange credentials for access token
- API Template Access β Use access token in Authorization header
- Token Refresh β Use refresh token when access token expires
- Password Grant: For trusted first-party applications (mobile apps, web apps)
- Client Credentials: For server-to-server communication
read: Read access to resources (default scope)write: Create, update, delete resourcesadmin: Administrative access
- Confidential Applications: Use HTTP Basic Authentication for client credentials
- Access Token Expiration: Configurable token lifetime
- Refresh Tokens: Automatic token renewal
- Scope-based Access Control: Granular permissions per application
rails-api-template/
βββ app/
β βββ controllers/
β β βββ api/v1/
β β β βββ users_controller.rb
β β β βββ oauth_applications_controller.rb
β β βββ concerns/
β β βββ response_renderer.rb
β β βββ object_renderer.rb
β β βββ collection_renderer.rb
β βββ models/
β β βββ user.rb
β β βββ concerns/
β β βββ serializable.rb
β βββ serializers/
β β βββ user_serializer.rb
β βββ views/
βββ config/
β βββ initializers/
β β βββ rswag_api.rb
β β βββ rswag_ui.rb
β β βββ doorkeeper.rb
β β βββ active_model_serializers.rb
β βββ routes.rb
βββ spec/
β βββ factories/
β β βββ users.rb
β βββ models/
β β βββ user_spec.rb
β βββ requests/api/v1/
β β βββ users_spec.rb
β βββ serializers/
β β βββ user_serializer_spec.rb
β βββ shared_examples/
β β βββ model_validations.rb
β βββ shared_contexts/
β β βββ common_setup.rb
β βββ rails_helper.rb
β βββ swagger_helper.rb
βββ swagger/
β βββ v1/
β βββ swagger.json
βββ Gemfile
ruby_rules.md- Ruby coding standards and best practicesproject_structure.md- Detailed project architectureSWAGGER_README.md- Comprehensive Swagger setup guidespec/README.md- Testing setup and usage guideconfig/initializers/doorkeeper.rb- OAuth 2.0 configuration
- API Docs: Mounted at
/api-docs - Swagger UI: Available at
/api-docs - OpenAPI Specs: Generated from RSpec tests
- Provider: Doorkeeper
- Grant Types: Password, Client Credentials
- Token Expiration: 2 hours (configurable)
- Refresh Tokens: Enabled
- Scopes: read, write, admin
- Applications: Confidential by default (HTTP Basic Auth)
- Adapter: JSON API format for standardized responses
- Key Transform: camelCase transformation (snake_case β camelCase)
- Serializer Lookup: Automatic discovery of serializer classes
- Response Format: Consistent JSON structure across all endpoints
- Encryption: bcrypt with automatic salting
- Model Integration:
has_secure_passwordin User model - Validation: Password confirmation and length requirements
- Authentication: Secure
User.authenticatemethod for OAuth - Storage: Only encrypted password digests in database
- Development:
rails_api_template_development - Test:
rails_api_template_test - Production:
rails_api_template_production
The database uses a streamlined approach:
- Unified Player References: All player-related models use consistent
player_idforeign keys - Essential Fields Only: Focus on core data without unnecessary complexity
- Optimized Indexes: Strategic indexing for common query patterns
- Clean Relationships: Simple, maintainable foreign key structure
- Create the model and controller
- Add routes to
config/routes.rb - Write RSpec tests with RSwag documentation
- Generate updated Swagger docs
When extending this template for your project:
- Start Clean: Begin with the provided user management foundation
- Follow Rails Conventions: Use standard Rails patterns and naming
- Test Everything: Follow the testing examples provided
- Document APIs: Use RSwag for automatic API documentation
- Keep it Simple: Focus on essential functionality first
# Generate from all tests
bundle exec rspec --format Rswag::Specs::SwaggerFormatter
# Generate from specific test files
bundle exec rspec spec/requests/api/v1/users_spec.rb --format Rswag::Specs::SwaggerFormatter
# Access documentation at
# http://localhost:3000/api-docs- Follow Ruby coding standards in
ruby_rules.md - Write comprehensive tests for all new features
- Update API template documentation when adding endpoints
# If you get connection errors
brew services start postgresql@16# Clear test database
rails db:test:prepare
# Run bundle install if gems are missing
bundle install# Regenerate Swagger docs
bundle exec rspec --format Rswag::Specs::SwaggerFormatter# Check OAuth applications
rails console
Doorkeeper::Application.all
# Verify user exists
User.find_by(email: 'user@example.com')
# Check Doorkeeper configuration
# See config/initializers/doorkeeper.rbCommon OAuth Errors:
invalid_grant: Check username/password and client credentialsinvalid_client: Verify OAuth application exists and credentials are correctunauthorized_client: Ensure grant type is enabled in Doorkeeper config
- Follow the Ruby coding standards in
ruby_rules.md - Write tests for all new features
- Update documentation as needed
- Ensure all tests pass before submitting
This project is licensed under the MIT License.
For questions or issues:
- Check the troubleshooting section above
- Review the
SWAGGER_README.mdfor Swagger-specific issues - Review the
spec/README.mdfor testing questions