rapitapir 0.1.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +7 -7
  3. data/.rubocop_todo.yml +83 -0
  4. data/README.md +1319 -235
  5. data/RUBY_WEEKLY_LAUNCH_POST.md +219 -0
  6. data/docs/RAILS_INTEGRATION_IMPLEMENTATION.md +209 -0
  7. data/docs/SINATRA_EXTENSION.md +399 -348
  8. data/docs/STRICT_VALIDATION.md +229 -0
  9. data/docs/VALIDATION_IMPROVEMENTS.md +218 -0
  10. data/docs/ai-integration-plan.md +112 -0
  11. data/docs/auto-derivation.md +505 -92
  12. data/docs/endpoint-definition.md +536 -129
  13. data/docs/n8n-integration.md +212 -0
  14. data/docs/observability.md +810 -500
  15. data/docs/using-mcp.md +93 -0
  16. data/examples/ai/knowledge_base_rag.rb +83 -0
  17. data/examples/ai/user_management_mcp.rb +92 -0
  18. data/examples/ai/user_validation_llm.rb +187 -0
  19. data/examples/rails/RAILS_8_GUIDE.md +165 -0
  20. data/examples/rails/RAILS_LOADING_FIX.rb +35 -0
  21. data/examples/rails/README.md +497 -0
  22. data/examples/rails/comprehensive_test.rb +91 -0
  23. data/examples/rails/config/routes.rb +48 -0
  24. data/examples/rails/debug_controller.rb +63 -0
  25. data/examples/rails/detailed_test.rb +46 -0
  26. data/examples/rails/enhanced_users_controller.rb +278 -0
  27. data/examples/rails/final_server_test.rb +50 -0
  28. data/examples/rails/hello_world_app.rb +116 -0
  29. data/examples/rails/hello_world_controller.rb +186 -0
  30. data/examples/rails/hello_world_routes.rb +28 -0
  31. data/examples/rails/rails8_minimal_demo.rb +132 -0
  32. data/examples/rails/rails8_simple_demo.rb +140 -0
  33. data/examples/rails/rails8_working_demo.rb +255 -0
  34. data/examples/rails/real_world_blog_api.rb +510 -0
  35. data/examples/rails/server_test.rb +46 -0
  36. data/examples/rails/test_direct_processing.rb +41 -0
  37. data/examples/rails/test_hello_world.rb +80 -0
  38. data/examples/rails/test_rails_integration.rb +54 -0
  39. data/examples/rails/traditional_app/Gemfile +37 -0
  40. data/examples/rails/traditional_app/README.md +265 -0
  41. data/examples/rails/traditional_app/app/controllers/api/v1/posts_controller.rb +254 -0
  42. data/examples/rails/traditional_app/app/controllers/api/v1/users_controller.rb +220 -0
  43. data/examples/rails/traditional_app/app/controllers/application_controller.rb +86 -0
  44. data/examples/rails/traditional_app/app/controllers/application_controller_simplified.rb +87 -0
  45. data/examples/rails/traditional_app/app/controllers/documentation_controller.rb +149 -0
  46. data/examples/rails/traditional_app/app/controllers/health_controller.rb +42 -0
  47. data/examples/rails/traditional_app/config/routes.rb +25 -0
  48. data/examples/rails/traditional_app/config/routes_best_practice.rb +25 -0
  49. data/examples/rails/traditional_app/config/routes_simplified.rb +36 -0
  50. data/examples/rails/traditional_app_runnable.rb +406 -0
  51. data/examples/rails/users_controller.rb +4 -1
  52. data/examples/serverless/Gemfile +43 -0
  53. data/examples/serverless/QUICKSTART.md +331 -0
  54. data/examples/serverless/README.md +520 -0
  55. data/examples/serverless/aws_lambda_example.rb +307 -0
  56. data/examples/serverless/aws_sam_template.yaml +215 -0
  57. data/examples/serverless/azure_functions_example.rb +407 -0
  58. data/examples/serverless/deploy.rb +204 -0
  59. data/examples/serverless/gcp_cloud_functions_example.rb +367 -0
  60. data/examples/serverless/gcp_function.yaml +23 -0
  61. data/examples/serverless/host.json +24 -0
  62. data/examples/serverless/package.json +32 -0
  63. data/examples/serverless/spec/aws_lambda_spec.rb +196 -0
  64. data/examples/serverless/spec/spec_helper.rb +89 -0
  65. data/examples/serverless/vercel.json +31 -0
  66. data/examples/serverless/vercel_example.rb +404 -0
  67. data/examples/strict_validation_examples.rb +104 -0
  68. data/examples/validation_error_examples.rb +173 -0
  69. data/lib/rapitapir/ai/llm_instruction.rb +456 -0
  70. data/lib/rapitapir/ai/mcp.rb +134 -0
  71. data/lib/rapitapir/ai/rag.rb +287 -0
  72. data/lib/rapitapir/ai/rag_middleware.rb +147 -0
  73. data/lib/rapitapir/auth/oauth2.rb +43 -57
  74. data/lib/rapitapir/cli/command.rb +362 -2
  75. data/lib/rapitapir/cli/mcp_export.rb +18 -0
  76. data/lib/rapitapir/cli/validator.rb +2 -6
  77. data/lib/rapitapir/core/endpoint.rb +59 -6
  78. data/lib/rapitapir/core/enhanced_endpoint.rb +2 -6
  79. data/lib/rapitapir/dsl/fluent_endpoint_builder.rb +53 -0
  80. data/lib/rapitapir/endpoint_registry.rb +47 -0
  81. data/lib/rapitapir/observability/health_check.rb +4 -4
  82. data/lib/rapitapir/observability/logging.rb +10 -10
  83. data/lib/rapitapir/schema.rb +2 -2
  84. data/lib/rapitapir/server/rack_adapter.rb +1 -3
  85. data/lib/rapitapir/server/rails/configuration.rb +77 -0
  86. data/lib/rapitapir/server/rails/controller_base.rb +185 -0
  87. data/lib/rapitapir/server/rails/documentation_helpers.rb +76 -0
  88. data/lib/rapitapir/server/rails/resource_builder.rb +181 -0
  89. data/lib/rapitapir/server/rails/routes.rb +114 -0
  90. data/lib/rapitapir/server/rails_adapter.rb +10 -3
  91. data/lib/rapitapir/server/rails_adapter_class.rb +1 -3
  92. data/lib/rapitapir/server/rails_controller.rb +1 -3
  93. data/lib/rapitapir/server/rails_integration.rb +67 -0
  94. data/lib/rapitapir/server/rails_response_handler.rb +16 -3
  95. data/lib/rapitapir/server/sinatra_adapter.rb +29 -5
  96. data/lib/rapitapir/server/sinatra_integration.rb +4 -4
  97. data/lib/rapitapir/sinatra/extension.rb +2 -2
  98. data/lib/rapitapir/sinatra/oauth2_helpers.rb +34 -40
  99. data/lib/rapitapir/types/array.rb +4 -0
  100. data/lib/rapitapir/types/auto_derivation.rb +4 -18
  101. data/lib/rapitapir/types/datetime.rb +1 -3
  102. data/lib/rapitapir/types/float.rb +2 -6
  103. data/lib/rapitapir/types/hash.rb +40 -2
  104. data/lib/rapitapir/types/integer.rb +4 -12
  105. data/lib/rapitapir/types/object.rb +6 -2
  106. data/lib/rapitapir/types.rb +6 -2
  107. data/lib/rapitapir/version.rb +1 -1
  108. data/lib/rapitapir.rb +5 -3
  109. metadata +74 -2
@@ -1,467 +1,518 @@
1
- # RapiTapir Sinatra Extension
1
+ # RapiTapir Sinatra Integration
2
2
 
3
3
  **Zero-boilerplate, enterprise-grade API development with RapiTapir and Sinatra**
4
4
 
5
- The RapiTapir Sinatra Extension provides a seamless, ergonomic experience for building production-ready APIs by eliminating repetitive configuration and following SOLID principles.
5
+ RapiTapir provides two elegant ways to integrate with Sinatra: the recommended **SinatraRapiTapir base class** for maximum simplicity, and the **manual extension registration** for advanced customization.
6
6
 
7
- ## 🚀 Quick Start
7
+ ## 🚀 Quick Start (Recommended)
8
+
9
+ ### Option 1: SinatraRapiTapir Base Class
10
+
11
+ The simplest and cleanest way to create APIs:
8
12
 
9
13
  ```ruby
10
- require 'sinatra/base'
11
14
  require 'rapitapir'
12
- require 'rapitapir/sinatra/extension'
13
-
14
- class MyAPI < Sinatra::Base
15
- register RapiTapir::Sinatra::Extension
16
15
 
16
+ class MyAPI < SinatraRapiTapir
17
17
  rapitapir do
18
- info title: 'My API', version: '1.0.0'
19
- development_defaults!
18
+ info(title: 'My API', version: '2.0.0')
19
+ development_defaults! # Auto CORS, docs, health checks
20
20
  end
21
21
 
22
- # Define a simple endpoint
22
+ # T shortcut and HTTP verbs automatically available
23
23
  endpoint(
24
- RapiTapir.get('/hello')
25
- .ok(RapiTapir::Types.hash({ "message" => RapiTapir::Types.string }))
24
+ GET('/hello')
25
+ .query(:name, T.string, description: 'Your name')
26
+ .ok(T.hash({ "message" => T.string }))
26
27
  .build
27
- ) { { message: 'Hello, World!' } }
28
+ ) { |inputs| { message: "Hello, #{inputs[:name]}!" } }
29
+
30
+ run! if __FILE__ == $0
28
31
  end
29
32
  ```
30
33
 
31
- ## 📋 Table of Contents
34
+ ### Option 2: Manual Extension Registration
32
35
 
33
- - [Features](#features)
34
- - [Installation](#installation)
35
- - [Configuration](#configuration)
36
- - [Endpoint Definition](#endpoint-definition)
37
- - [RESTful Resources](#restful-resources)
38
- - [Authentication](#authentication)
39
- - [Middleware](#middleware)
40
- - [Documentation](#documentation)
41
- - [Examples](#examples)
42
- - [Architecture](#architecture)
43
-
44
- ## ✨ Features
45
-
46
- ### Zero Boilerplate
47
- - **One-line configuration**: Set up authentication, middleware, and documentation with minimal code
48
- - **Smart defaults**: Production and development presets that just work
49
- - **Automatic registration**: Endpoints are automatically registered with proper validation
50
-
51
- ### Enterprise-Ready
52
- - **Built-in authentication**: Bearer token, API key, and custom schemes
53
- - **Production middleware**: CORS, rate limiting, security headers
54
- - **Auto-generated OpenAPI**: Beautiful Swagger UI documentation
55
- - **Type-safe validation**: Automatic request/response validation
56
-
57
- ### Developer Experience
58
- - **RESTful resource builder**: Create full CRUD APIs with a single block
59
- - **Helpful error messages**: Clear validation and authentication errors
60
- - **Hot reloading**: Development-friendly configuration
61
- - **Extensive examples**: Get started quickly with working code
62
-
63
- ## 🔧 Installation
64
-
65
- Add to your Gemfile:
36
+ For advanced customization and existing Sinatra applications:
66
37
 
67
38
  ```ruby
68
- gem 'rapitapir'
69
- ```
70
-
71
- Or install directly:
72
-
73
- ```bash
74
- gem install rapitapir
75
- ```
76
-
77
- ## ⚙️ Configuration
78
-
79
- ### Basic Configuration
39
+ require 'sinatra/base'
40
+ require 'rapitapir/sinatra/extension'
80
41
 
81
- ```ruby
82
42
  class MyAPI < Sinatra::Base
83
43
  register RapiTapir::Sinatra::Extension
84
44
 
85
45
  rapitapir do
86
- # API metadata
87
- info(
88
- title: 'My API',
89
- description: 'A fantastic API',
90
- version: '1.0.0',
91
- contact: { email: 'support@example.com' }
92
- )
93
-
94
- # Server configuration
95
- server url: 'http://localhost:4567', description: 'Development'
96
- server url: 'https://api.example.com', description: 'Production'
97
-
98
- # Enable documentation
99
- enable_docs path: '/docs', openapi_path: '/openapi.json'
46
+ info(title: 'My API', version: '2.0.0')
47
+ development_defaults!
100
48
  end
49
+
50
+ endpoint(
51
+ GET('/hello')
52
+ .query(:name, T.string)
53
+ .ok(T.hash({ "message" => T.string }))
54
+ .build
55
+ ) { |inputs| { message: "Hello, #{inputs[:name]}!" } }
101
56
  end
102
57
  ```
103
58
 
104
- ### Environment-Specific Defaults
59
+ ## 📋 Key Features
105
60
 
106
- ```ruby
107
- rapitapir do
108
- if development?
109
- development_defaults! # Permissive CORS, high rate limits
110
- else
111
- production_defaults! # Secure defaults for production
112
- end
113
- end
114
- ```
61
+ ### ✨ Zero Configuration
62
+ - **SinatraRapiTapir base class**: Inherit and start building APIs immediately
63
+ - **T shortcut**: Use `T.string` instead of `RapiTapir::Types.string` everywhere
64
+ - **HTTP verbs**: `GET()`, `POST()`, `PUT()`, `DELETE()` methods built-in
65
+ - **Smart defaults**: Production and development presets that just work
115
66
 
116
- ## 🛡️ Authentication
67
+ ### 🏭 Enterprise Ready
68
+ - **Built-in authentication**: OAuth2, Bearer token, API key, and custom schemes
69
+ - **Production middleware**: CORS, rate limiting, security headers, request validation
70
+ - **Auto-generated documentation**: Interactive Swagger UI with try-it-out functionality
71
+ - **Type-safe validation**: Automatic request/response validation with helpful errors
117
72
 
118
- ### Bearer Token Authentication
73
+ ### 🤖 AI Integration
74
+ - **LLM instruction generation**: Auto-generate AI prompts for your endpoints
75
+ - **RAG pipelines**: Retrieval-augmented generation for enhanced responses
76
+ - **MCP export**: Model Context Protocol for AI agent consumption
77
+ - **CLI toolkit**: Complete command-line development workflow
78
+
79
+ ## 📖 Configuration Options
119
80
 
81
+ ### API Information
120
82
  ```ruby
121
83
  rapitapir do
122
- bearer_auth :bearer, {
123
- realm: 'My API',
124
- token_validator: proc do |token|
125
- user = User.find_by_token(token)
126
- next nil unless user
127
-
128
- {
129
- user: user,
130
- scopes: user.scopes
131
- }
132
- end
133
- }
84
+ info(
85
+ title: 'My API',
86
+ description: 'A comprehensive API for my application',
87
+ version: '2.0.0',
88
+ contact: {
89
+ name: 'API Team',
90
+ email: 'api@example.com',
91
+ url: 'https://example.com/support'
92
+ },
93
+ license: {
94
+ name: 'MIT',
95
+ url: 'https://opensource.org/licenses/MIT'
96
+ }
97
+ )
98
+
99
+ # Server environments
100
+ server(url: 'http://localhost:4567', description: 'Development')
101
+ server(url: 'https://staging-api.example.com', description: 'Staging')
102
+ server(url: 'https://api.example.com', description: 'Production')
134
103
  end
135
104
  ```
136
105
 
137
- ### API Key Authentication
106
+ ### Development vs Production Defaults
138
107
 
139
108
  ```ruby
109
+ # Development mode (automatic CORS, docs, health checks)
140
110
  rapitapir do
141
- api_key_auth :api_key, {
142
- location: 'header',
143
- name: 'X-API-Key',
144
- key_validator: proc do |key|
145
- # Validate API key
146
- end
147
- }
111
+ development_defaults!
148
112
  end
149
- ```
150
113
 
151
- ### Public Endpoints
114
+ # Production mode (security headers, rate limiting, metrics)
115
+ rapitapir do
116
+ production_defaults!
117
+ end
152
118
 
153
- ```ruby
119
+ # Custom configuration
154
120
  rapitapir do
155
- public_paths '/health', '/docs', '/openapi.json'
121
+ enable_docs(path: '/documentation', openapi_path: '/spec.json')
122
+ enable_cors(origins: ['https://myapp.com'], methods: %w[GET POST])
123
+ enable_health_checks(path: '/health')
124
+ enable_metrics(path: '/metrics')
156
125
  end
157
126
  ```
158
127
 
159
- ## 📡 Endpoint Definition
128
+ ## 🏗️ Endpoint Definition
160
129
 
161
- ### Simple Endpoints
130
+ ### Modern HTTP Verb DSL
162
131
 
163
132
  ```ruby
164
- endpoint(
165
- RapiTapir.get('/users')
166
- .summary('List users')
167
- .ok(RapiTapir::Types.array(USER_SCHEMA))
168
- .build
169
- ) do |inputs|
170
- User.all
171
- end
172
- ```
173
-
174
- ### With Authentication Requirements
133
+ class BookAPI < SinatraRapiTapir
134
+ # Basic endpoint
135
+ endpoint(
136
+ GET('/books')
137
+ .summary('List all books')
138
+ .query(:limit, T.optional(T.integer(minimum: 1, maximum: 100)), description: 'Number of books to return')
139
+ .query(:genre, T.optional(T.string), description: 'Filter by genre')
140
+ .ok(T.array(T.hash({
141
+ "id" => T.integer,
142
+ "title" => T.string,
143
+ "author" => T.string,
144
+ "genre" => T.string
145
+ })))
146
+ .build
147
+ ) do |inputs|
148
+ books = Book.all
149
+ books = books.where(genre: inputs[:genre]) if inputs[:genre]
150
+ books = books.limit(inputs[:limit] || 50)
151
+ books.map(&:to_h)
152
+ end
175
153
 
176
- ```ruby
177
- endpoint(
178
- RapiTapir.post('/admin/users')
179
- .summary('Create user (admin only)')
180
- .json_body(USER_CREATE_SCHEMA)
181
- .created(USER_SCHEMA)
182
- .build
183
- ) do |inputs|
184
- require_scope!('admin')
185
- User.create(inputs[:body])
154
+ # POST with body validation
155
+ endpoint(
156
+ POST('/books')
157
+ .summary('Create a new book')
158
+ .body(T.hash({
159
+ "title" => T.string(min_length: 1, max_length: 500),
160
+ "author" => T.string(min_length: 1),
161
+ "genre" => T.string,
162
+ "isbn" => T.optional(T.string(pattern: /^\d{13}$/)),
163
+ "pages" => T.optional(T.integer(minimum: 1))
164
+ }), description: 'Book data')
165
+ .ok(T.hash({
166
+ "id" => T.integer,
167
+ "title" => T.string,
168
+ "author" => T.string,
169
+ "created_at" => T.datetime
170
+ }), description: 'Created book')
171
+ .error_response(400, T.hash({ "error" => T.string, "details" => T.array(T.string) }))
172
+ .build
173
+ ) do |inputs|
174
+ begin
175
+ book = Book.create!(inputs[:body])
176
+ status 201
177
+ {
178
+ id: book.id,
179
+ title: book.title,
180
+ author: book.author,
181
+ created_at: book.created_at
182
+ }
183
+ rescue ValidationError => e
184
+ halt 400, {
185
+ error: 'Validation failed',
186
+ details: e.messages
187
+ }.to_json
188
+ end
189
+ end
186
190
  end
187
191
  ```
188
192
 
189
- ## 🏗️ RESTful Resources
193
+ ### RESTful Resource Builder
190
194
 
191
- The resource builder creates full CRUD APIs with minimal code:
195
+ Create complete CRUD APIs with minimal code:
192
196
 
193
197
  ```ruby
194
- api_resource '/api/v1/tasks', schema: TASK_SCHEMA do
195
- # Enable all CRUD operations
196
- crud do
197
- index { Task.all }
198
- show { |inputs| Task.find(inputs[:id]) }
199
- create { |inputs| Task.create(inputs[:body]) }
200
- update { |inputs| Task.update(inputs[:id], inputs[:body]) }
201
- destroy { |inputs| Task.delete(inputs[:id]) }
198
+ class UserAPI < SinatraRapiTapir
199
+ USER_SCHEMA = T.hash({
200
+ "id" => T.integer,
201
+ "name" => T.string(min_length: 1, max_length: 100),
202
+ "email" => T.email,
203
+ "active" => T.boolean,
204
+ "created_at" => T.datetime,
205
+ "updated_at" => T.datetime
206
+ })
207
+
208
+ api_resource '/users', schema: USER_SCHEMA do
209
+ crud do
210
+ index do |inputs|
211
+ users = User.all
212
+ users = users.where(active: true) if inputs[:active] == 'true'
213
+ users.limit(inputs[:limit] || 50).map(&:to_h)
214
+ end
215
+
216
+ show { |inputs| User.find(inputs[:id])&.to_h || halt(404) }
217
+
218
+ create do |inputs|
219
+ user = User.create!(inputs[:body])
220
+ status 201
221
+ user.to_h
222
+ end
223
+
224
+ update do |inputs|
225
+ user = User.find(inputs[:id]) || halt(404)
226
+ user.update!(inputs[:body])
227
+ user.to_h
228
+ end
229
+
230
+ destroy do |inputs|
231
+ user = User.find(inputs[:id]) || halt(404)
232
+ user.destroy!
233
+ status 204
234
+ end
235
+ end
236
+
237
+ # Custom endpoints
238
+ custom :post, ':id/activate' do |inputs|
239
+ user = User.find(inputs[:id]) || halt(404)
240
+ user.update!(active: true)
241
+ { message: 'User activated', user: user.to_h }
242
+ end
202
243
  end
203
244
  end
204
245
  ```
205
246
 
206
- ### Selective Operations
247
+ ## 🔐 Authentication
248
+
249
+ ### OAuth2 + Auth0 Integration
207
250
 
208
251
  ```ruby
209
- api_resource '/books', schema: BOOK_SCHEMA do
210
- crud(except: [:destroy]) do # All operations except delete
211
- index { Book.published }
212
- show { |inputs| Book.find(inputs[:id]) }
213
- create { |inputs| Book.create(inputs[:body]) }
214
- update { |inputs| Book.update(inputs[:id], inputs[:body]) }
252
+ class SecureAPI < SinatraRapiTapir
253
+ rapitapir do
254
+ info(title: 'Secure API', version: '2.0.0')
255
+
256
+ # Configure Auth0 OAuth2
257
+ oauth2_auth0 :auth0,
258
+ domain: ENV['AUTH0_DOMAIN'],
259
+ audience: ENV['AUTH0_AUDIENCE'],
260
+ realm: 'API Access'
261
+
262
+ production_defaults!
215
263
  end
216
- end
217
- ```
218
-
219
- ### Custom Endpoints
220
264
 
221
- ```ruby
222
- api_resource '/tasks', schema: TASK_SCHEMA do
223
- crud { /* ... */ }
224
-
225
- # Custom endpoint: /tasks/by-status/:status
226
- custom(:get, 'by-status/:status',
227
- summary: 'Get tasks by status',
228
- configure: ->(endpoint) {
229
- endpoint
230
- .path_param(:status, RapiTapir::Types.string)
231
- .ok(RapiTapir::Types.array(TASK_SCHEMA))
265
+ # Protected endpoint with scopes
266
+ endpoint(
267
+ GET('/profile')
268
+ .summary('Get user profile')
269
+ .bearer_auth(scopes: ['read:profile'])
270
+ .ok(T.hash({
271
+ "user" => T.hash({
272
+ "id" => T.string,
273
+ "email" => T.string,
274
+ "name" => T.string
275
+ })
276
+ }))
277
+ .build
278
+ ) do
279
+ user_info = current_auth_context[:user_info]
280
+ {
281
+ user: {
282
+ id: user_info['sub'],
283
+ email: user_info['email'],
284
+ name: user_info['name']
285
+ }
232
286
  }
233
- ) do |inputs|
234
- Task.where(status: inputs[:status])
235
287
  end
236
- end
237
- ```
238
-
239
- ### Different Scopes for Operations
240
288
 
241
- ```ruby
242
- api_resource '/users', schema: USER_SCHEMA do
243
- crud do
244
- index(scopes: ['read']) { User.all }
245
- show(scopes: ['read']) { |inputs| User.find(inputs[:id]) }
246
- create(scopes: ['write']) { |inputs| User.create(inputs[:body]) }
247
- update(scopes: ['write']) { |inputs| User.update(inputs[:id], inputs[:body]) }
248
- destroy(scopes: ['admin']) { |inputs| User.delete(inputs[:id]) }
289
+ # Scope-based protection for multiple endpoints
290
+ protect_with_oauth2 scopes: ['api:read'] do
291
+ endpoint(GET('/protected').ok(T.hash({})).build) { { data: 'protected' } }
292
+ endpoint(GET('/also-protected').ok(T.hash({})).build) { { data: 'also protected' } }
249
293
  end
250
294
  end
251
295
  ```
252
296
 
253
- ## 🔐 Authentication Helpers
297
+ ### Bearer Token Authentication
254
298
 
255
299
  ```ruby
256
- endpoint(...) do |inputs|
257
- # Check if user is authenticated
258
- require_authentication!
259
-
260
- # Check specific scope
261
- require_scope!('admin')
262
-
263
- # Get current user
264
- user = current_user
265
-
266
- # Check if authenticated (non-throwing)
267
- if authenticated?
268
- # User is logged in
300
+ class TokenAPI < SinatraRapiTapir
301
+ rapitapir do
302
+ bearer_auth :api_key, realm: 'API'
303
+ production_defaults!
269
304
  end
270
305
 
271
- # Check scope (non-throwing)
272
- if has_scope?('write')
273
- # User has write permission
306
+ endpoint(
307
+ GET('/secure-data')
308
+ .bearer_auth
309
+ .ok(T.array(T.hash({})))
310
+ .build
311
+ ) do
312
+ # current_auth_context available here
313
+ SecureData.for_user(current_auth_context[:user_id])
274
314
  end
275
315
  end
276
316
  ```
277
317
 
278
- ## 🛠️ Middleware Configuration
318
+ ## 🤖 AI Integration
279
319
 
280
- ### CORS
320
+ ### LLM Instructions and RAG
281
321
 
282
322
  ```ruby
283
- rapitapir do
284
- cors(
285
- allowed_origins: ['https://myapp.com'],
286
- allowed_methods: ['GET', 'POST', 'PUT', 'DELETE'],
287
- allowed_headers: ['Authorization', 'Content-Type'],
288
- allow_credentials: true
289
- )
290
- end
291
- ```
292
-
293
- ### Rate Limiting
294
-
295
- ```ruby
296
- rapitapir do
297
- rate_limiting(
298
- requests_per_minute: 60,
299
- requests_per_hour: 1000
300
- )
301
- end
302
- ```
323
+ class AIEnhancedAPI < SinatraRapiTapir
324
+ rapitapir do
325
+ info(title: 'AI-Enhanced API', version: '2.0.0')
326
+ development_defaults!
327
+ end
303
328
 
304
- ### Security Headers
329
+ # AI-powered search with RAG
330
+ endpoint(
331
+ GET('/books/ai-search')
332
+ .query(:query, T.string, description: 'Natural language search query')
333
+ .summary('AI-powered semantic book search')
334
+ .tags('AI', 'Search')
335
+ .ok(T.hash({
336
+ "books" => T.array(BOOK_SCHEMA),
337
+ "reasoning" => T.string,
338
+ "confidence" => T.float
339
+ }))
340
+ .enable_rag(
341
+ retrieval_backend: :memory,
342
+ llm_provider: :openai
343
+ )
344
+ .enable_mcp # Export for AI agents
345
+ .build
346
+ ) do |inputs|
347
+ # RAG context automatically injected
348
+ results = AIBookSearch.semantic_search(
349
+ query: inputs[:query],
350
+ context: rag_context
351
+ )
352
+
353
+ {
354
+ books: results[:books],
355
+ reasoning: results[:explanation],
356
+ confidence: results[:confidence_score]
357
+ }
358
+ end
305
359
 
306
- ```ruby
307
- rapitapir do
308
- security_headers(
309
- csp: "default-src 'self'",
310
- hsts: true
311
- )
360
+ # Generate LLM instructions for endpoints
361
+ endpoint(
362
+ GET('/ai/instructions/:endpoint_id')
363
+ .path_param(:endpoint_id, T.string)
364
+ .query(:purpose, T.string(enum: %w[validation transformation analysis documentation testing completion]))
365
+ .summary('Generate LLM instructions for an endpoint')
366
+ .ok(T.hash({
367
+ "instructions" => T.string,
368
+ "metadata" => T.hash({})
369
+ }))
370
+ .build
371
+ ) do |inputs|
372
+ generator = RapiTapir::AI::LLMInstruction::Generator.new
373
+ endpoint = find_endpoint(inputs[:endpoint_id])
374
+
375
+ instructions = generator.generate_instructions(
376
+ endpoint: endpoint,
377
+ purpose: inputs[:purpose].to_sym
378
+ )
379
+
380
+ {
381
+ instructions: instructions,
382
+ metadata: {
383
+ endpoint_id: inputs[:endpoint_id],
384
+ purpose: inputs[:purpose],
385
+ generated_at: Time.now
386
+ }
387
+ }
388
+ end
312
389
  end
313
390
  ```
314
391
 
315
- ## 📚 Documentation
316
-
317
- ### Automatic OpenAPI Generation
318
-
319
- The extension automatically generates OpenAPI 3.0 specifications from your endpoint definitions:
320
-
321
- - **Swagger UI**: Beautiful, interactive documentation
322
- - **Type validation**: Schemas automatically derived from RapiTapir types
323
- - **Authentication schemes**: Security requirements automatically added
324
- - **Request/response examples**: Generated from your schemas
392
+ ## 📊 Observability
325
393
 
326
- ### Custom Documentation
394
+ ### Comprehensive Monitoring
327
395
 
328
396
  ```ruby
329
- rapitapir do
330
- info(
331
- title: 'My API',
332
- description: 'Comprehensive API documentation',
333
- version: '1.0.0',
334
- contact: {
335
- name: 'Support Team',
336
- email: 'support@example.com',
337
- url: 'https://example.com/support'
338
- },
339
- license: {
340
- name: 'MIT',
341
- url: 'https://opensource.org/licenses/MIT'
342
- }
343
- )
397
+ class MonitoredAPI < SinatraRapiTapir
398
+ rapitapir do
399
+ info(title: 'Monitored API', version: '2.0.0')
400
+
401
+ enable_observability do |config|
402
+ # Health checks
403
+ config.health_checks.enable(path: '/health')
404
+ config.health_checks.add_check('database') { Database.healthy? }
405
+ config.health_checks.add_check('redis') { Redis.current.ping == 'PONG' }
406
+
407
+ # OpenTelemetry tracing
408
+ config.tracing.enable_opentelemetry(
409
+ service_name: 'my-api',
410
+ exporters: [:honeycomb, :jaeger]
411
+ )
412
+
413
+ # Prometheus metrics
414
+ config.metrics.enable_prometheus(namespace: 'my_api')
415
+
416
+ # Structured logging
417
+ config.logging.enable_structured(format: :json)
418
+ end
419
+
420
+ production_defaults!
421
+ end
422
+
423
+ # Endpoint with observability features
424
+ endpoint(
425
+ GET('/monitored-endpoint')
426
+ .with_metrics('endpoint_requests', labels: { operation: 'get' })
427
+ .with_tracing('fetch_data')
428
+ .ok(T.hash({ "data" => T.string }))
429
+ .build
430
+ ) do
431
+ # Automatic metrics and tracing
432
+ { data: 'monitored response' }
433
+ end
344
434
  end
345
435
  ```
346
436
 
347
- ## 🎯 Complete Examples
437
+ ## 🧪 Testing
348
438
 
349
- ### Minimal Bookstore API
439
+ ### Test Your Endpoints
350
440
 
351
441
  ```ruby
352
- require 'sinatra/base'
353
- require 'rapitapir/sinatra/extension'
442
+ require 'rack/test'
354
443
 
355
- class BookstoreAPI < Sinatra::Base
356
- register RapiTapir::Sinatra::Extension
444
+ RSpec.describe MyAPI do
445
+ include Rack::Test::Methods
357
446
 
358
- rapitapir do
359
- info title: 'Bookstore API', version: '1.0.0'
360
- development_defaults!
361
- public_paths '/books'
447
+ def app
448
+ MyAPI
362
449
  end
363
450
 
364
- BOOK_SCHEMA = RapiTapir::Types.hash({
365
- "id" => RapiTapir::Types.integer,
366
- "title" => RapiTapir::Types.string,
367
- "author" => RapiTapir::Types.string
368
- })
451
+ it 'returns hello message' do
452
+ get '/hello?name=World'
453
+
454
+ expect(last_response).to be_ok
455
+ expect(JSON.parse(last_response.body)).to eq({
456
+ 'message' => 'Hello, World!'
457
+ })
458
+ end
369
459
 
370
- api_resource '/books', schema: BOOK_SCHEMA do
371
- crud do
372
- index { Book.all }
373
- show { |inputs| Book.find(inputs[:id]) }
374
- create { |inputs| Book.create(inputs[:body]) }
375
- update { |inputs| Book.update(inputs[:id], inputs[:body]) }
376
- end
460
+ it 'validates required parameters' do
461
+ get '/hello'
462
+
463
+ expect(last_response.status).to eq(400)
464
+ expect(JSON.parse(last_response.body)).to include('error')
377
465
  end
378
466
  end
379
467
  ```
380
468
 
381
- ### Enterprise Task Management API
382
-
383
- See `examples/enterprise_extension_demo.rb` for a comprehensive example with:
384
- - Bearer token authentication
385
- - Multiple resources
386
- - Custom endpoints
387
- - Admin-only operations
388
- - Full middleware stack
389
-
390
- ## 🏛️ Architecture
469
+ ### Validate Endpoint Definitions
391
470
 
392
- The RapiTapir Sinatra Extension follows SOLID principles:
393
-
394
- ### Single Responsibility Principle
395
- - **Extension**: Manages Sinatra integration only
396
- - **Configuration**: Handles API configuration only
397
- - **ResourceBuilder**: Creates RESTful endpoints only
398
- - **SwaggerUIGenerator**: Generates documentation UI only
471
+ ```ruby
472
+ # Validate all endpoints
473
+ validator = RapiTapir::CLI::Validator.new(MyAPI.rapitapir_endpoints)
474
+ result = validator.validate
399
475
 
400
- ### Open/Closed Principle
401
- - **Extensible**: Add new authentication schemes without modifying core
402
- - **Pluggable**: Custom middleware can be added
403
- - **Customizable**: Resource builder supports custom endpoints
476
+ puts "Validation passed: #{result}"
477
+ puts "Errors: #{validator.errors}" unless result
478
+ ```
404
479
 
405
- ### Liskov Substitution Principle
406
- - **Endpoint compatibility**: Works with any RapiTapir endpoint
407
- - **Authentication schemes**: All auth schemes follow same interface
480
+ ## 🔗 Comparison: Base Class vs Extension
408
481
 
409
- ### Interface Segregation Principle
410
- - **Focused interfaces**: Each component has a specific, minimal interface
411
- - **Optional features**: Documentation, authentication, middleware are optional
482
+ | Feature | SinatraRapiTapir | Manual Extension |
483
+ |---------|------------------|------------------|
484
+ | Setup complexity | Single inheritance | Manual registration |
485
+ | HTTP verb methods | ✅ Built-in | ✅ Available |
486
+ | T shortcut | ✅ Automatic | ✅ Available |
487
+ | Configuration | ✅ Simple | ✅ Full control |
488
+ | Sinatra features | ✅ All available | ✅ All available |
489
+ | Customization | Good | Excellent |
490
+ | Use case | New APIs, rapid prototyping | Existing apps, advanced needs |
412
491
 
413
- ### Dependency Inversion Principle
414
- - **Abstractions**: Depends on RapiTapir abstractions, not concrete implementations
415
- - **Injection**: Authentication and validation logic is injected via procs
492
+ ## 📚 Examples
416
493
 
417
- ## 🔄 Migration from Manual Implementation
494
+ ### Complete RESTful API
495
+ See [examples/working_simple_example.rb](../examples/working_simple_example.rb)
418
496
 
419
- Migrating from manual Sinatra routes to the extension:
497
+ ### Authentication with Auth0
498
+ See [examples/oauth2/](../examples/oauth2/)
420
499
 
421
- ### Before (Manual)
422
- ```ruby
423
- class MyAPI < Sinatra::Base
424
- use SomeMiddleware
425
- use SomeOtherMiddleware
426
-
427
- get '/tasks' do
428
- # Manual authentication
429
- # Manual validation
430
- # Manual response formatting
431
- end
432
-
433
- post '/tasks' do
434
- # Repeat authentication, validation, etc.
435
- end
436
-
437
- # Repeat for every endpoint...
438
- end
439
- ```
500
+ ### AI-Powered Features
501
+ See [examples/auto_derivation_ruby_friendly.rb](../examples/auto_derivation_ruby_friendly.rb)
440
502
 
441
- ### After (Extension)
442
- ```ruby
443
- class MyAPI < Sinatra::Base
444
- register RapiTapir::Sinatra::Extension
445
-
446
- rapitapir do
447
- bearer_auth { /* config */ }
448
- development_defaults!
449
- end
450
-
451
- api_resource '/tasks', schema: TASK_SCHEMA do
452
- crud { /* handlers */ }
453
- end
454
- end
455
- ```
503
+ ### Production Observability
504
+ See [examples/observability/](../examples/observability/)
456
505
 
457
- ## 🤝 Contributing
506
+ ## 🎯 Best Practices
458
507
 
459
- 1. Fork the repository
460
- 2. Create a feature branch
461
- 3. Add tests for your changes
462
- 4. Ensure all tests pass
463
- 5. Submit a pull request
508
+ 1. **Use SinatraRapiTapir** for new projects - it's the simplest approach
509
+ 2. **Leverage T shortcuts** everywhere for cleaner type definitions
510
+ 3. **Use resource builders** for RESTful APIs to reduce boilerplate
511
+ 4. **Enable observability** for production deployments
512
+ 5. **Implement proper error handling** with meaningful error responses
513
+ 6. **Document with examples** using the built-in OpenAPI generation
514
+ 7. **Test endpoint validation** to ensure type safety works correctly
464
515
 
465
- ## 📄 License
516
+ ---
466
517
 
467
- MIT License - see LICENSE file for details.
518
+ RapiTapir + Sinatra = **APIs so fast and clean, they practically run wild!** 🦙⚡