grape 0.1.1 → 2.4.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 (197) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1167 -0
  3. data/CONTRIBUTING.md +156 -0
  4. data/LICENSE +1 -1
  5. data/README.md +4192 -0
  6. data/UPGRADING.md +1697 -0
  7. data/grape.gemspec +28 -105
  8. data/grape.png +0 -0
  9. data/lib/grape/api/helpers.rb +9 -0
  10. data/lib/grape/api/instance.rb +247 -0
  11. data/lib/grape/api.rb +158 -194
  12. data/lib/grape/content_types.rb +31 -0
  13. data/lib/grape/cookies.rb +50 -0
  14. data/lib/grape/dry_types.rb +10 -0
  15. data/lib/grape/dsl/api.rb +17 -0
  16. data/lib/grape/dsl/callbacks.rb +69 -0
  17. data/lib/grape/dsl/configuration.rb +15 -0
  18. data/lib/grape/dsl/desc.rb +124 -0
  19. data/lib/grape/dsl/headers.rb +21 -0
  20. data/lib/grape/dsl/helpers.rb +108 -0
  21. data/lib/grape/dsl/inside_route.rb +454 -0
  22. data/lib/grape/dsl/logger.rb +22 -0
  23. data/lib/grape/dsl/middleware.rb +54 -0
  24. data/lib/grape/dsl/parameters.rb +266 -0
  25. data/lib/grape/dsl/request_response.rb +171 -0
  26. data/lib/grape/dsl/routing.rb +248 -0
  27. data/lib/grape/dsl/settings.rb +179 -0
  28. data/lib/grape/dsl/validations.rb +57 -0
  29. data/lib/grape/endpoint.rb +398 -68
  30. data/lib/grape/env.rb +20 -0
  31. data/lib/grape/error_formatter/base.rb +68 -0
  32. data/lib/grape/error_formatter/json.rb +28 -0
  33. data/lib/grape/error_formatter/serializable_hash.rb +7 -0
  34. data/lib/grape/error_formatter/txt.rb +21 -0
  35. data/lib/grape/error_formatter/xml.rb +11 -0
  36. data/lib/grape/error_formatter.rb +15 -0
  37. data/lib/grape/exceptions/base.rb +76 -0
  38. data/lib/grape/exceptions/conflicting_types.rb +11 -0
  39. data/lib/grape/exceptions/empty_message_body.rb +11 -0
  40. data/lib/grape/exceptions/incompatible_option_values.rb +11 -0
  41. data/lib/grape/exceptions/invalid_accept_header.rb +11 -0
  42. data/lib/grape/exceptions/invalid_formatter.rb +11 -0
  43. data/lib/grape/exceptions/invalid_message_body.rb +11 -0
  44. data/lib/grape/exceptions/invalid_parameters.rb +11 -0
  45. data/lib/grape/exceptions/invalid_response.rb +11 -0
  46. data/lib/grape/exceptions/invalid_version_header.rb +11 -0
  47. data/lib/grape/exceptions/invalid_versioner_option.rb +11 -0
  48. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +11 -0
  49. data/lib/grape/exceptions/method_not_allowed.rb +11 -0
  50. data/lib/grape/exceptions/missing_group_type.rb +13 -0
  51. data/lib/grape/exceptions/missing_mime_type.rb +11 -0
  52. data/lib/grape/exceptions/missing_option.rb +11 -0
  53. data/lib/grape/exceptions/missing_vendor_option.rb +11 -0
  54. data/lib/grape/exceptions/too_deep_parameters.rb +11 -0
  55. data/lib/grape/exceptions/too_many_multipart_files.rb +11 -0
  56. data/lib/grape/exceptions/unknown_auth_strategy.rb +11 -0
  57. data/lib/grape/exceptions/unknown_options.rb +11 -0
  58. data/lib/grape/exceptions/unknown_parameter.rb +11 -0
  59. data/lib/grape/exceptions/unknown_params_builder.rb +11 -0
  60. data/lib/grape/exceptions/unknown_validator.rb +11 -0
  61. data/lib/grape/exceptions/unsupported_group_type.rb +13 -0
  62. data/lib/grape/exceptions/validation.rb +25 -0
  63. data/lib/grape/exceptions/validation_array_errors.rb +14 -0
  64. data/lib/grape/exceptions/validation_errors.rb +53 -0
  65. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +24 -0
  66. data/lib/grape/extensions/hash.rb +27 -0
  67. data/lib/grape/extensions/hashie/mash.rb +24 -0
  68. data/lib/grape/formatter/base.rb +16 -0
  69. data/lib/grape/formatter/json.rb +13 -0
  70. data/lib/grape/formatter/serializable_hash.rb +39 -0
  71. data/lib/grape/formatter/txt.rb +11 -0
  72. data/lib/grape/formatter/xml.rb +13 -0
  73. data/lib/grape/formatter.rb +17 -0
  74. data/lib/grape/json.rb +10 -0
  75. data/lib/grape/locale/en.yml +59 -0
  76. data/lib/grape/middleware/auth/base.rb +23 -0
  77. data/lib/grape/middleware/auth/dsl.rb +39 -0
  78. data/lib/grape/middleware/auth/strategies.rb +25 -0
  79. data/lib/grape/middleware/auth/strategy_info.rb +15 -0
  80. data/lib/grape/middleware/base.rb +89 -18
  81. data/lib/grape/middleware/error.rb +129 -10
  82. data/lib/grape/middleware/filter.rb +19 -0
  83. data/lib/grape/middleware/formatter.rb +124 -82
  84. data/lib/grape/middleware/globals.rb +14 -0
  85. data/lib/grape/middleware/stack.rb +109 -0
  86. data/lib/grape/middleware/versioner/accept_version_header.rb +38 -0
  87. data/lib/grape/middleware/versioner/base.rb +74 -0
  88. data/lib/grape/middleware/versioner/header.rb +127 -0
  89. data/lib/grape/middleware/versioner/param.rb +32 -0
  90. data/lib/grape/middleware/versioner/path.rb +40 -0
  91. data/lib/grape/middleware/versioner.rb +21 -21
  92. data/lib/grape/namespace.rb +46 -0
  93. data/lib/grape/params_builder/base.rb +18 -0
  94. data/lib/grape/params_builder/hash.rb +11 -0
  95. data/lib/grape/params_builder/hash_with_indifferent_access.rb +11 -0
  96. data/lib/grape/params_builder/hashie_mash.rb +11 -0
  97. data/lib/grape/params_builder.rb +32 -0
  98. data/lib/grape/parser/base.rb +16 -0
  99. data/lib/grape/parser/json.rb +14 -0
  100. data/lib/grape/parser/xml.rb +14 -0
  101. data/lib/grape/parser.rb +15 -0
  102. data/lib/grape/path.rb +76 -0
  103. data/lib/grape/presenters/presenter.rb +11 -0
  104. data/lib/grape/railtie.rb +9 -0
  105. data/lib/grape/request.rb +190 -0
  106. data/lib/grape/router/base_route.rb +39 -0
  107. data/lib/grape/router/greedy_route.rb +20 -0
  108. data/lib/grape/router/pattern.rb +81 -0
  109. data/lib/grape/router/route.rb +62 -0
  110. data/lib/grape/router.rb +190 -0
  111. data/lib/grape/serve_stream/file_body.rb +36 -0
  112. data/lib/grape/serve_stream/sendfile_response.rb +21 -0
  113. data/lib/grape/serve_stream/stream_response.rb +23 -0
  114. data/lib/grape/types/invalid_value.rb +8 -0
  115. data/lib/grape/util/base_inheritable.rb +43 -0
  116. data/lib/grape/util/cache.rb +17 -0
  117. data/lib/grape/util/endpoint_configuration.rb +8 -0
  118. data/lib/grape/util/header.rb +13 -0
  119. data/lib/grape/util/inheritable_setting.rb +100 -0
  120. data/lib/grape/util/inheritable_values.rb +29 -0
  121. data/lib/grape/util/lazy/block.rb +29 -0
  122. data/lib/grape/util/lazy/value.rb +38 -0
  123. data/lib/grape/util/lazy/value_array.rb +21 -0
  124. data/lib/grape/util/lazy/value_enumerable.rb +34 -0
  125. data/lib/grape/util/lazy/value_hash.rb +21 -0
  126. data/lib/grape/util/media_type.rb +70 -0
  127. data/lib/grape/util/registry.rb +27 -0
  128. data/lib/grape/util/reverse_stackable_values.rb +15 -0
  129. data/lib/grape/util/stackable_values.rb +36 -0
  130. data/lib/grape/util/strict_hash_configuration.rb +108 -0
  131. data/lib/grape/validations/attributes_doc.rb +60 -0
  132. data/lib/grape/validations/attributes_iterator.rb +62 -0
  133. data/lib/grape/validations/contract_scope.rb +34 -0
  134. data/lib/grape/validations/multiple_attributes_iterator.rb +13 -0
  135. data/lib/grape/validations/params_scope.rb +538 -0
  136. data/lib/grape/validations/single_attribute_iterator.rb +26 -0
  137. data/lib/grape/validations/types/array_coercer.rb +61 -0
  138. data/lib/grape/validations/types/custom_type_coercer.rb +164 -0
  139. data/lib/grape/validations/types/custom_type_collection_coercer.rb +56 -0
  140. data/lib/grape/validations/types/dry_type_coercer.rb +66 -0
  141. data/lib/grape/validations/types/file.rb +31 -0
  142. data/lib/grape/validations/types/invalid_value.rb +17 -0
  143. data/lib/grape/validations/types/json.rb +71 -0
  144. data/lib/grape/validations/types/multiple_type_coercer.rb +57 -0
  145. data/lib/grape/validations/types/primitive_coercer.rb +73 -0
  146. data/lib/grape/validations/types/set_coercer.rb +35 -0
  147. data/lib/grape/validations/types/variant_collection_coercer.rb +51 -0
  148. data/lib/grape/validations/types.rb +213 -0
  149. data/lib/grape/validations/validator_factory.rb +15 -0
  150. data/lib/grape/validations/validators/all_or_none_of_validator.rb +16 -0
  151. data/lib/grape/validations/validators/allow_blank_validator.rb +20 -0
  152. data/lib/grape/validations/validators/as_validator.rb +14 -0
  153. data/lib/grape/validations/validators/at_least_one_of_validator.rb +15 -0
  154. data/lib/grape/validations/validators/base.rb +88 -0
  155. data/lib/grape/validations/validators/coerce_validator.rb +75 -0
  156. data/lib/grape/validations/validators/contract_scope_validator.rb +41 -0
  157. data/lib/grape/validations/validators/default_validator.rb +37 -0
  158. data/lib/grape/validations/validators/exactly_one_of_validator.rb +17 -0
  159. data/lib/grape/validations/validators/except_values_validator.rb +24 -0
  160. data/lib/grape/validations/validators/length_validator.rb +49 -0
  161. data/lib/grape/validations/validators/multiple_params_base.rb +34 -0
  162. data/lib/grape/validations/validators/mutual_exclusion_validator.rb +16 -0
  163. data/lib/grape/validations/validators/presence_validator.rb +15 -0
  164. data/lib/grape/validations/validators/regexp_validator.rb +16 -0
  165. data/lib/grape/validations/validators/same_as_validator.rb +29 -0
  166. data/lib/grape/validations/validators/values_validator.rb +60 -0
  167. data/lib/grape/validations.rb +21 -0
  168. data/lib/grape/version.rb +6 -0
  169. data/lib/grape/xml.rb +10 -0
  170. data/lib/grape.rb +81 -17
  171. metadata +250 -192
  172. data/.document +0 -5
  173. data/.gitignore +0 -23
  174. data/.rspec +0 -2
  175. data/.rvmrc +0 -1
  176. data/Gemfile +0 -21
  177. data/Gemfile.lock +0 -58
  178. data/README.markdown +0 -75
  179. data/Rakefile +0 -70
  180. data/VERSION +0 -1
  181. data/autotest/discover.rb +0 -1
  182. data/lib/grape/middleware/auth/basic.rb +0 -30
  183. data/lib/grape/middleware/auth/oauth2.rb +0 -55
  184. data/lib/grape/middleware/prefixer.rb +0 -21
  185. data/lib/grape/middleware_stack.rb +0 -35
  186. data/spec/grape/api_spec.rb +0 -343
  187. data/spec/grape/endpoint_spec.rb +0 -104
  188. data/spec/grape/middleware/auth/basic_spec.rb +0 -31
  189. data/spec/grape/middleware/auth/oauth2_spec.rb +0 -88
  190. data/spec/grape/middleware/base_spec.rb +0 -63
  191. data/spec/grape/middleware/error_spec.rb +0 -49
  192. data/spec/grape/middleware/formatter_spec.rb +0 -87
  193. data/spec/grape/middleware/prefixer_spec.rb +0 -30
  194. data/spec/grape/middleware/versioner_spec.rb +0 -40
  195. data/spec/grape/middleware_stack_spec.rb +0 -47
  196. data/spec/grape_spec.rb +0 -1
  197. data/spec/spec_helper.rb +0 -20
data/README.md ADDED
@@ -0,0 +1,4192 @@
1
+ ![grape logo](grape.png)
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/grape.svg)](http://badge.fury.io/rb/grape)
4
+ [![test](https://github.com/ruby-grape/grape/actions/workflows/test.yml/badge.svg)](https://github.com/ruby-grape/grape/actions/workflows/test.yml)
5
+ [![Coverage Status](https://coveralls.io/repos/github/ruby-grape/grape/badge.svg?branch=master)](https://coveralls.io/github/ruby-grape/grape?branch=master)
6
+
7
+ ## Table of Contents
8
+
9
+ - [What is Grape?](#what-is-grape)
10
+ - [Stable Release](#stable-release)
11
+ - [Project Resources](#project-resources)
12
+ - [Grape for Enterprise](#grape-for-enterprise)
13
+ - [Installation](#installation)
14
+ - [Basic Usage](#basic-usage)
15
+ - [Rails 7.1](#rails-71)
16
+ - [Mounting](#mounting)
17
+ - [All](#all)
18
+ - [Rack](#rack)
19
+ - [Alongside Sinatra (or other frameworks)](#alongside-sinatra-or-other-frameworks)
20
+ - [Rails](#rails)
21
+ - [Zeitwerk](#zeitwerk)
22
+ - [Modules](#modules)
23
+ - [Remounting](#remounting)
24
+ - [Mount Configuration](#mount-configuration)
25
+ - [Versioning](#versioning)
26
+ - [Strategies](#strategies)
27
+ - [Path](#path)
28
+ - [Header](#header)
29
+ - [Accept-Version Header](#accept-version-header)
30
+ - [Param](#param)
31
+ - [Linting](#linting)
32
+ - [Bug in Rack::ETag under Rack 3.X](#bug-in-racketag-under-rack-3x)
33
+ - [Describing Methods](#describing-methods)
34
+ - [Configuration](#configuration)
35
+ - [Parameters](#parameters)
36
+ - [Params Class](#params-class)
37
+ - [Declared](#declared)
38
+ - [Include Parent Namespaces](#include-parent-namespaces)
39
+ - [Include Missing](#include-missing)
40
+ - [Evaluate Given](#evaluate-given)
41
+ - [Parameter Precedence](#parameter-precedence)
42
+ - [Parameter Validation and Coercion](#parameter-validation-and-coercion)
43
+ - [Supported Parameter Types](#supported-parameter-types)
44
+ - [Integer/Fixnum and Coercions](#integerfixnum-and-coercions)
45
+ - [Custom Types and Coercions](#custom-types-and-coercions)
46
+ - [Multipart File Parameters](#multipart-file-parameters)
47
+ - [First-Class JSON Types](#first-class-json-types)
48
+ - [Multiple Allowed Types](#multiple-allowed-types)
49
+ - [Validation of Nested Parameters](#validation-of-nested-parameters)
50
+ - [Dependent Parameters](#dependent-parameters)
51
+ - [Group Options](#group-options)
52
+ - [Renaming](#renaming)
53
+ - [Built-in Validators](#built-in-validators)
54
+ - [allow_blank](#allow_blank)
55
+ - [values](#values)
56
+ - [except_values](#except_values)
57
+ - [same_as](#same_as)
58
+ - [length](#length)
59
+ - [regexp](#regexp)
60
+ - [mutually_exclusive](#mutually_exclusive)
61
+ - [exactly_one_of](#exactly_one_of)
62
+ - [at_least_one_of](#at_least_one_of)
63
+ - [all_or_none_of](#all_or_none_of)
64
+ - [Nested mutually_exclusive, exactly_one_of, at_least_one_of, all_or_none_of](#nested-mutually_exclusive-exactly_one_of-at_least_one_of-all_or_none_of)
65
+ - [Namespace Validation and Coercion](#namespace-validation-and-coercion)
66
+ - [Custom Validators](#custom-validators)
67
+ - [Validation Errors](#validation-errors)
68
+ - [I18n](#i18n)
69
+ - [Custom Validation messages](#custom-validation-messages)
70
+ - [presence, allow_blank, values, regexp](#presence-allow_blank-values-regexp)
71
+ - [same_as](#same_as-1)
72
+ - [length](#length-1)
73
+ - [all_or_none_of](#all_or_none_of-1)
74
+ - [mutually_exclusive](#mutually_exclusive-1)
75
+ - [exactly_one_of](#exactly_one_of-1)
76
+ - [at_least_one_of](#at_least_one_of-1)
77
+ - [Coerce](#coerce)
78
+ - [With Lambdas](#with-lambdas)
79
+ - [Pass symbols for i18n translations](#pass-symbols-for-i18n-translations)
80
+ - [Overriding Attribute Names](#overriding-attribute-names)
81
+ - [With Default](#with-default)
82
+ - [Using dry-validation or dry-schema](#using-dry-validation-or-dry-schema)
83
+ - [Headers](#headers)
84
+ - [Request](#request)
85
+ - [Header Case Handling](#header-case-handling)
86
+ - [Response](#response)
87
+ - [Routes](#routes)
88
+ - [Helpers](#helpers)
89
+ - [Path Helpers](#path-helpers)
90
+ - [Parameter Documentation](#parameter-documentation)
91
+ - [Cookies](#cookies)
92
+ - [HTTP Status Code](#http-status-code)
93
+ - [Redirecting](#redirecting)
94
+ - [Recognizing Path](#recognizing-path)
95
+ - [Allowed Methods](#allowed-methods)
96
+ - [Raising Exceptions](#raising-exceptions)
97
+ - [Default Error HTTP Status Code](#default-error-http-status-code)
98
+ - [Handling 404](#handling-404)
99
+ - [Exception Handling](#exception-handling)
100
+ - [Rescuing exceptions inside namespaces](#rescuing-exceptions-inside-namespaces)
101
+ - [Unrescuable Exceptions](#unrescuable-exceptions)
102
+ - [Exceptions that should be rescued explicitly](#exceptions-that-should-be-rescued-explicitly)
103
+ - [Logging](#logging)
104
+ - [API Formats](#api-formats)
105
+ - [JSONP](#jsonp)
106
+ - [CORS](#cors)
107
+ - [Content-type](#content-type)
108
+ - [API Data Formats](#api-data-formats)
109
+ - [JSON and XML Processors](#json-and-xml-processors)
110
+ - [RESTful Model Representations](#restful-model-representations)
111
+ - [Grape Entities](#grape-entities)
112
+ - [Hypermedia and Roar](#hypermedia-and-roar)
113
+ - [Rabl](#rabl)
114
+ - [Active Model Serializers](#active-model-serializers)
115
+ - [Sending Raw or No Data](#sending-raw-or-no-data)
116
+ - [Authentication](#authentication)
117
+ - [Basic Auth](#basic-auth)
118
+ - [Register custom middleware for authentication](#register-custom-middleware-for-authentication)
119
+ - [Describing and Inspecting an API](#describing-and-inspecting-an-api)
120
+ - [Current Route and Endpoint](#current-route-and-endpoint)
121
+ - [Before, After and Finally](#before-after-and-finally)
122
+ - [Anchoring](#anchoring)
123
+ - [Instance Variables](#instance-variables)
124
+ - [Using Custom Middleware](#using-custom-middleware)
125
+ - [Grape Middleware](#grape-middleware)
126
+ - [Rails Middleware](#rails-middleware)
127
+ - [Remote IP](#remote-ip)
128
+ - [Writing Tests](#writing-tests)
129
+ - [Writing Tests with Rack](#writing-tests-with-rack)
130
+ - [RSpec](#rspec)
131
+ - [Airborne](#airborne)
132
+ - [MiniTest](#minitest)
133
+ - [Writing Tests with Rails](#writing-tests-with-rails)
134
+ - [RSpec](#rspec-1)
135
+ - [MiniTest](#minitest-1)
136
+ - [Stubbing Helpers](#stubbing-helpers)
137
+ - [Reloading API Changes in Development](#reloading-api-changes-in-development)
138
+ - [Reloading in Rack Applications](#reloading-in-rack-applications)
139
+ - [Reloading in Rails Applications](#reloading-in-rails-applications)
140
+ - [Performance Monitoring](#performance-monitoring)
141
+ - [Active Support Instrumentation](#active-support-instrumentation)
142
+ - [endpoint_run.grape](#endpoint_rungrape)
143
+ - [endpoint_render.grape](#endpoint_rendergrape)
144
+ - [endpoint_run_filters.grape](#endpoint_run_filtersgrape)
145
+ - [endpoint_run_validators.grape](#endpoint_run_validatorsgrape)
146
+ - [format_response.grape](#format_responsegrape)
147
+ - [Monitoring Products](#monitoring-products)
148
+ - [Contributing to Grape](#contributing-to-grape)
149
+ - [Security](#security)
150
+ - [License](#license)
151
+ - [Copyright](#copyright)
152
+
153
+ ## What is Grape?
154
+
155
+ Grape is a REST-like API framework for Ruby. It's designed to run on Rack or complement existing web application frameworks such as Rails and Sinatra by providing a simple DSL to easily develop RESTful APIs. It has built-in support for common conventions, including multiple formats, subdomain/prefix restriction, content negotiation, versioning and much more.
156
+
157
+ ## Stable Release
158
+
159
+ You're reading the documentation for the stable release of Grape, 2.4.0.
160
+ Please read [UPGRADING](https://github.com/ruby-grape/grape/blob/v2.4.0/UPGRADING.md) when upgrading from a previous version.
161
+
162
+ ## Project Resources
163
+
164
+ * [Grape Website](http://www.ruby-grape.org)
165
+ * [Documentation](http://www.rubydoc.info/gems/grape)
166
+ * Need help? [Open an Issue](https://github.com/ruby-grape/grape/issues)
167
+ * [Follow us on Twitter](https://twitter.com/grapeframework)
168
+
169
+ ## Grape for Enterprise
170
+
171
+ Available as part of the Tidelift Subscription.
172
+
173
+ The maintainers of Grape are working with Tidelift to deliver commercial support and maintenance. Save time, reduce risk, and improve code health, while paying the maintainers of Grape. Click [here](https://tidelift.com/subscription/request-a-demo?utm_source=rubygems-grape&utm_medium=referral&utm_campaign=enterprise) for more details.
174
+
175
+ ## Installation
176
+
177
+ Ruby 2.7 or newer is required.
178
+
179
+ Grape is available as a gem, to install it run:
180
+
181
+ bundle add grape
182
+
183
+ ## Basic Usage
184
+
185
+ Grape APIs are Rack applications that are created by subclassing `Grape::API`.
186
+ Below is a simple example showing some of the more common features of Grape in the context of recreating parts of the Twitter API.
187
+
188
+ ```ruby
189
+ module Twitter
190
+ class API < Grape::API
191
+ version 'v1', using: :header, vendor: 'twitter'
192
+ format :json
193
+ prefix :api
194
+
195
+ helpers do
196
+ def current_user
197
+ @current_user ||= User.authorize!(env)
198
+ end
199
+
200
+ def authenticate!
201
+ error!('401 Unauthorized', 401) unless current_user
202
+ end
203
+ end
204
+
205
+ resource :statuses do
206
+ desc 'Return a public timeline.'
207
+ get :public_timeline do
208
+ Status.limit(20)
209
+ end
210
+
211
+ desc 'Return a personal timeline.'
212
+ get :home_timeline do
213
+ authenticate!
214
+ current_user.statuses.limit(20)
215
+ end
216
+
217
+ desc 'Return a status.'
218
+ params do
219
+ requires :id, type: Integer, desc: 'Status ID.'
220
+ end
221
+ route_param :id do
222
+ get do
223
+ Status.find(params[:id])
224
+ end
225
+ end
226
+
227
+ desc 'Create a status.'
228
+ params do
229
+ requires :status, type: String, desc: 'Your status.'
230
+ end
231
+ post do
232
+ authenticate!
233
+ Status.create!({
234
+ user: current_user,
235
+ text: params[:status]
236
+ })
237
+ end
238
+
239
+ desc 'Update a status.'
240
+ params do
241
+ requires :id, type: String, desc: 'Status ID.'
242
+ requires :status, type: String, desc: 'Your status.'
243
+ end
244
+ put ':id' do
245
+ authenticate!
246
+ current_user.statuses.find(params[:id]).update({
247
+ user: current_user,
248
+ text: params[:status]
249
+ })
250
+ end
251
+
252
+ desc 'Delete a status.'
253
+ params do
254
+ requires :id, type: String, desc: 'Status ID.'
255
+ end
256
+ delete ':id' do
257
+ authenticate!
258
+ current_user.statuses.find(params[:id]).destroy
259
+ end
260
+ end
261
+ end
262
+ end
263
+ ```
264
+
265
+ ## Rails 7.1
266
+
267
+ Grape's [deprecator](https://api.rubyonrails.org/v7.1.0/classes/ActiveSupport/Deprecation.html) will be added to your application's deprecators [automatically](lib/grape/railtie.rb) as `:grape`, so that your application's configuration can be applied to it.
268
+
269
+ ## Mounting
270
+
271
+ ### All
272
+
273
+
274
+ By default Grape will compile the routes on the first route, but it is possible to pre-load routes using the `compile!` method.
275
+
276
+ ```ruby
277
+ Twitter::API.compile!
278
+ ```
279
+
280
+ This can be added to your `config.ru` (if using rackup), `application.rb` (if using rails), or any file that loads your server.
281
+
282
+ ### Rack
283
+
284
+ The above sample creates a Rack application that can be run from a rackup `config.ru` file with `rackup`:
285
+
286
+ ```ruby
287
+ run Twitter::API
288
+ ```
289
+
290
+ (With pre-loading you can use)
291
+
292
+ ```ruby
293
+ Twitter::API.compile!
294
+ run Twitter::API
295
+ ```
296
+
297
+ And would respond to the following routes:
298
+
299
+ GET /api/statuses/public_timeline
300
+ GET /api/statuses/home_timeline
301
+ GET /api/statuses/:id
302
+ POST /api/statuses
303
+ PUT /api/statuses/:id
304
+ DELETE /api/statuses/:id
305
+
306
+ Grape will also automatically respond to HEAD and OPTIONS for all GET, and just OPTIONS for all other routes.
307
+
308
+ ### Alongside Sinatra (or other frameworks)
309
+
310
+ If you wish to mount Grape alongside another Rack framework such as Sinatra, you can do so easily using `Rack::Cascade`:
311
+
312
+ ```ruby
313
+ # Example config.ru
314
+
315
+ require 'sinatra'
316
+ require 'grape'
317
+
318
+ class API < Grape::API
319
+ get :hello do
320
+ { hello: 'world' }
321
+ end
322
+ end
323
+
324
+ class Web < Sinatra::Base
325
+ get '/' do
326
+ 'Hello world.'
327
+ end
328
+ end
329
+
330
+ use Rack::Session::Cookie
331
+ run Rack::Cascade.new [Web, API]
332
+ ```
333
+
334
+ Note that order of loading apps using `Rack::Cascade` matters. The grape application must be last if you want to raise custom 404 errors from grape (such as `error!('Not Found',404)`). If the grape application is not last and returns 404 or 405 response, [cascade utilizes that as a signal to try the next app](https://www.rubydoc.info/gems/rack/Rack/Cascade). This may lead to undesirable behavior showing the [wrong 404 page from the wrong app](https://github.com/ruby-grape/grape/issues/1515).
335
+
336
+
337
+ ### Rails
338
+
339
+ Place API files into `app/api`. Rails expects a subdirectory that matches the name of the Ruby module and a file name that matches the name of the class. In our example, the file name location and directory for `Twitter::API` should be `app/api/twitter/api.rb`.
340
+
341
+ Modify `config/routes`:
342
+
343
+ ```ruby
344
+ mount Twitter::API => '/'
345
+ ```
346
+ #### Zeitwerk
347
+ Rails's default autoloader is `Zeitwerk`. By default, it inflects `api` as `Api` instead of `API`. To make our example work, you need to uncomment the lines at the bottom of `config/initializers/inflections.rb`, and add `API` as an acronym:
348
+
349
+ ```ruby
350
+ ActiveSupport::Inflector.inflections(:en) do |inflect|
351
+ inflect.acronym 'API'
352
+ end
353
+ ```
354
+
355
+ ### Modules
356
+
357
+ You can mount multiple API implementations inside another one. These don't have to be different versions, but may be components of the same API.
358
+
359
+ ```ruby
360
+ class Twitter::API < Grape::API
361
+ mount Twitter::APIv1
362
+ mount Twitter::APIv2
363
+ end
364
+ ```
365
+
366
+ You can also mount on a path, which is similar to using `prefix` inside the mounted API itself.
367
+
368
+ ```ruby
369
+ class Twitter::API < Grape::API
370
+ mount Twitter::APIv1 => '/v1'
371
+ end
372
+ ```
373
+
374
+ Declarations as `before/after/rescue_from` can be placed before or after `mount`. In any case they will be inherited.
375
+
376
+ ```ruby
377
+ class Twitter::API < Grape::API
378
+ before do
379
+ header 'X-Base-Header', 'will be defined for all APIs that are mounted below'
380
+ end
381
+
382
+ rescue_from :all do
383
+ error!({ "error" => "Internal Server Error" }, 500)
384
+ end
385
+
386
+ mount Twitter::Users
387
+ mount Twitter::Search
388
+
389
+ after do
390
+ clean_cache!
391
+ end
392
+
393
+ rescue_from ZeroDivisionError do
394
+ error!({ "error" => "Not found" }, 404)
395
+ end
396
+ end
397
+ ```
398
+
399
+ ## Remounting
400
+
401
+ You can mount the same endpoints in two different locations.
402
+
403
+ ```ruby
404
+ class Voting::API < Grape::API
405
+ namespace 'votes' do
406
+ get do
407
+ # Your logic
408
+ end
409
+
410
+ post do
411
+ # Your logic
412
+ end
413
+ end
414
+ end
415
+
416
+ class Post::API < Grape::API
417
+ mount Voting::API
418
+ end
419
+
420
+ class Comment::API < Grape::API
421
+ mount Voting::API
422
+ end
423
+ ```
424
+
425
+ Assuming that the post and comment endpoints are mounted in `/posts` and `/comments`, you should now be able to do `get /posts/votes`, `post /posts/votes`, `get /comments/votes` and `post /comments/votes`.
426
+
427
+ ### Mount Configuration
428
+
429
+ You can configure remountable endpoints to change how they behave according to where they are mounted.
430
+
431
+ ```ruby
432
+ class Voting::API < Grape::API
433
+ namespace 'votes' do
434
+ desc "Vote for your #{configuration[:votable]}"
435
+ get do
436
+ # Your logic
437
+ end
438
+ end
439
+ end
440
+
441
+ class Post::API < Grape::API
442
+ mount Voting::API, with: { votable: 'posts' }
443
+ end
444
+
445
+ class Comment::API < Grape::API
446
+ mount Voting::API, with: { votable: 'comments' }
447
+ end
448
+ ```
449
+
450
+ Note that if you're passing a hash as the first parameter to `mount`, you will need to explicitly put `()` around parameters:
451
+ ```ruby
452
+ # good
453
+ mount({ ::Some::Api => '/some/api' }, with: { condition: true })
454
+
455
+ # bad
456
+ mount ::Some::Api => '/some/api', with: { condition: true }
457
+ ```
458
+
459
+ You can access `configuration` on the class (to use as dynamic attributes), inside blocks (like namespace)
460
+
461
+ If you want logic happening given on an `configuration`, you can use the helper `given`.
462
+
463
+ ```ruby
464
+ class ConditionalEndpoint::API < Grape::API
465
+ given configuration[:some_setting] do
466
+ get 'mount_this_endpoint_conditionally' do
467
+ configuration[:configurable_response]
468
+ end
469
+ end
470
+ end
471
+ ```
472
+
473
+ If you want a block of logic running every time an endpoint is mounted (within which you can access the `configuration` Hash)
474
+
475
+
476
+ ```ruby
477
+ class ConditionalEndpoint::API < Grape::API
478
+ mounted do
479
+ YourLogger.info "This API was mounted at: #{Time.now}"
480
+
481
+ get configuration[:endpoint_name] do
482
+ configuration[:configurable_response]
483
+ end
484
+ end
485
+ end
486
+ ```
487
+
488
+ More complex results can be achieved by using `mounted` as an expression within which the `configuration` is already evaluated as a Hash.
489
+
490
+ ```ruby
491
+ class ExpressionEndpointAPI < Grape::API
492
+ get(mounted { configuration[:route_name] || 'default_name' }) do
493
+ # some logic
494
+ end
495
+ end
496
+ ```
497
+
498
+ ```ruby
499
+ class BasicAPI < Grape::API
500
+ desc 'Statuses index' do
501
+ params: (configuration[:entity] || API::Entities::Status).documentation
502
+ end
503
+ params do
504
+ requires :all, using: (configuration[:entity] || API::Entities::Status).documentation
505
+ end
506
+ get '/statuses' do
507
+ statuses = Status.all
508
+ type = current_user.admin? ? :full : :default
509
+ present statuses, with: (configuration[:entity] || API::Entities::Status), type: type
510
+ end
511
+ end
512
+
513
+ class V1 < Grape::API
514
+ version 'v1'
515
+ mount BasicAPI, with: { entity: mounted { configuration[:entity] || API::Entities::Status } }
516
+ end
517
+
518
+ class V2 < Grape::API
519
+ version 'v2'
520
+ mount BasicAPI, with: { entity: mounted { configuration[:entity] || API::Entities::V2::Status } }
521
+ end
522
+ ```
523
+
524
+ ## Versioning
525
+
526
+ You have the option to provide various versions of your API by establishing a separate `Grape::API` class for each offered version and then integrating them into a primary `Grape::API` class. Ensure that newer versions are mounted before older ones. The default approach to versioning directs the request to the subsequent Rack middleware if a specific version is not found.
527
+
528
+ ```ruby
529
+ require 'v1'
530
+ require 'v2'
531
+ require 'v3'
532
+ class App < Grape::API
533
+ mount V3
534
+ mount V2
535
+ mount V1
536
+ end
537
+ ```
538
+
539
+ To maintain the same endpoints from earlier API versions without rewriting them, you can indicate multiple versions within the previous API versions.
540
+
541
+ ```ruby
542
+ class V1 < Grape::API
543
+ version 'v1', 'v2', 'v3'
544
+
545
+ get '/foo' do
546
+ # your code for GET /foo
547
+ end
548
+
549
+ get '/other' do
550
+ # your code for GET /other
551
+ end
552
+ end
553
+
554
+ class V2 < Grape::API
555
+ version 'v2', 'v3'
556
+
557
+ get '/var' do
558
+ # your code for GET /var
559
+ end
560
+ end
561
+
562
+ class V3 < Grape::API
563
+ version 'v3'
564
+
565
+ get '/foo' do
566
+ # your new code for GET /foo
567
+ end
568
+ end
569
+ ```
570
+
571
+ Using the example provided, the subsequent endpoints will be accessible across various versions:
572
+
573
+ ```shell
574
+ GET /v1/foo
575
+ GET /v1/other
576
+ GET /v2/foo # => Same behavior as v1
577
+ GET /v2/other # => Same behavior as v1
578
+ GET /v2/var # => New endpoint not available in v1
579
+ GET /v3/foo # => Different behavior to v1 and v2
580
+ GET /v3/other # => Same behavior as v1 and v2
581
+ GET /v3/var # => Same behavior as v2
582
+ ```
583
+
584
+ There are four strategies in which clients can reach your API's endpoints: `:path`, `:header`, `:accept_version_header` and `:param`. The default strategy is `:path`.
585
+
586
+ ### Strategies
587
+
588
+ #### Path
589
+
590
+ ```ruby
591
+ version 'v1', using: :path
592
+ ```
593
+
594
+ Using this versioning strategy, clients should pass the desired version in the URL.
595
+
596
+ curl http://localhost:9292/v1/statuses/public_timeline
597
+
598
+ #### Header
599
+
600
+ ```ruby
601
+ version 'v1', using: :header, vendor: 'twitter'
602
+ ```
603
+
604
+ Currently, Grape only supports versioned media types in the following format:
605
+
606
+ ```
607
+ vnd.vendor-and-or-resource-v1234+format
608
+ ```
609
+
610
+ Basically all tokens between the final `-` and the `+` will be interpreted as the version.
611
+
612
+ Using this versioning strategy, clients should pass the desired version in the HTTP `Accept` head.
613
+
614
+ curl -H Accept:application/vnd.twitter-v1+json http://localhost:9292/statuses/public_timeline
615
+
616
+ By default, the first matching version is used when no `Accept` header is supplied. This behavior is similar to routing in Rails. To circumvent this default behavior, one could use the `:strict` option. When this option is set to `true`, a `406 Not Acceptable` error is returned when no correct `Accept` header is supplied.
617
+
618
+ When an invalid `Accept` header is supplied, a `406 Not Acceptable` error is returned if the `:cascade` option is set to `false`. Otherwise a `404 Not Found` error is returned by Rack if no other route matches.
619
+
620
+ Grape will evaluate the relative quality preference included in Accept headers and default to a quality of 1.0 when omitted. In the following example a Grape API that supports XML and JSON in that order will return JSON:
621
+
622
+ curl -H "Accept: text/xml;q=0.8, application/json;q=0.9" localhost:1234/resource
623
+
624
+ #### Accept-Version Header
625
+
626
+ ```ruby
627
+ version 'v1', using: :accept_version_header
628
+ ```
629
+
630
+ Using this versioning strategy, clients should pass the desired version in the HTTP `Accept-Version` header.
631
+
632
+ curl -H "Accept-Version:v1" http://localhost:9292/statuses/public_timeline
633
+
634
+ By default, the first matching version is used when no `Accept-Version` header is supplied. This behavior is similar to routing in Rails. To circumvent this default behavior, one could use the `:strict` option. When this option is set to `true`, a `406 Not Acceptable` error is returned when no correct `Accept` header is supplied and the `:cascade` option is set to `false`. Otherwise a `404 Not Found` error is returned by Rack if no other route matches.
635
+
636
+ #### Param
637
+
638
+ ```ruby
639
+ version 'v1', using: :param
640
+ ```
641
+
642
+ Using this versioning strategy, clients should pass the desired version as a request parameter, either in the URL query string or in the request body.
643
+
644
+ curl http://localhost:9292/statuses/public_timeline?apiver=v1
645
+
646
+ The default name for the query parameter is 'apiver' but can be specified using the `:parameter` option.
647
+
648
+ ```ruby
649
+ version 'v1', using: :param, parameter: 'v'
650
+ ```
651
+
652
+ curl http://localhost:9292/statuses/public_timeline?v=v1
653
+
654
+
655
+ ## Linting
656
+
657
+ You can check whether your API is in conformance with the [Rack's specification](https://github.com/rack/rack/blob/main/SPEC.rdoc) by calling `lint!` at the API level or through [configuration](#configuration).
658
+
659
+ ```ruby
660
+ class Api < Grape::API
661
+ lint!
662
+ end
663
+ ```
664
+ ```ruby
665
+ Grape.configure do |config|
666
+ config.lint = true
667
+ end
668
+ ```
669
+ ```ruby
670
+ Grape.config.lint = true
671
+ ```
672
+
673
+ ### Bug in Rack::ETag under Rack 3.X
674
+ If you're using Rack 3.X and the `Rack::Etag` middleware (used by [Rails](https://guides.rubyonrails.org/rails_on_rack.html#inspecting-middleware-stack)), a [bug](https://github.com/rack/rack/pull/2324) related to linting has been fixed in [3.1.13](https://github.com/rack/rack/blob/v3.1.13/CHANGELOG.md#3113---2025-04-13) and [3.0.15](https://github.com/rack/rack/blob/v3.1.13/CHANGELOG.md#3015---2025-04-13) respectively.
675
+
676
+ ## Describing Methods
677
+
678
+ You can add a description to API methods and namespaces. The description would be used by [grape-swagger][grape-swagger] to generate swagger compliant documentation.
679
+
680
+ Note: Description block is only for documentation and won't affects API behavior.
681
+
682
+ ```ruby
683
+ desc 'Returns your public timeline.' do
684
+ summary 'summary'
685
+ detail 'more details'
686
+ params API::Entities::Status.documentation
687
+ success API::Entities::Entity
688
+ failure [[401, 'Unauthorized', 'Entities::Error']]
689
+ default { code: 500, message: 'InvalidRequest', model: Entities::Error }
690
+ named 'My named route'
691
+ headers XAuthToken: {
692
+ description: 'Validates your identity',
693
+ required: true
694
+ },
695
+ XOptionalHeader: {
696
+ description: 'Not really needed',
697
+ required: false
698
+ }
699
+ hidden false
700
+ deprecated false
701
+ is_array true
702
+ nickname 'nickname'
703
+ produces ['application/json']
704
+ consumes ['application/json']
705
+ tags ['tag1', 'tag2']
706
+ end
707
+ get :public_timeline do
708
+ Status.limit(20)
709
+ end
710
+ ```
711
+
712
+ * `detail`: A more enhanced description
713
+ * `params`: Define parameters directly from an `Entity`
714
+ * `success`: (former entity) The `Entity` to be used to present the success response for this route.
715
+ * `failure`: (former http_codes) A definition of the used failure HTTP Codes and Entities.
716
+ * `default`: The definition and `Entity` used to present the default response for this route.
717
+ * `named`: A helper to give a route a name and find it with this name in the documentation Hash
718
+ * `headers`: A definition of the used Headers
719
+ * Other options can be found in [grape-swagger][grape-swagger]
720
+
721
+ [grape-swagger]: https://github.com/ruby-grape/grape-swagger
722
+
723
+ ## Configuration
724
+
725
+ Use `Grape.configure` to set up global settings at load time.
726
+ Currently the configurable settings are:
727
+
728
+ * `param_builder`: Sets the [Parameter Builder](#parameters), defaults to `Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder`.
729
+
730
+ To change a setting value make sure that at some point during load time the following code runs
731
+
732
+ ```ruby
733
+ Grape.configure do |config|
734
+ config.setting = value
735
+ end
736
+ ```
737
+
738
+ For example, for the `param_builder`, the following code could run in an initializer:
739
+
740
+ ```ruby
741
+ Grape.configure do |config|
742
+ config.param_builder = :hashie_mash
743
+ end
744
+ ```
745
+
746
+ Available parameter builders are `:hash`, `:hash_with_indifferent_access`, and `:hashie_mash`.
747
+ See [params_builder](lib/grape/params_builder).
748
+
749
+ You can also configure a single API:
750
+
751
+ ```ruby
752
+ API.configure do |config|
753
+ config[key] = value
754
+ end
755
+ ```
756
+
757
+ This will be available inside the API with `configuration`, as if it were [mount configuration](#mount-configuration).
758
+
759
+ ## Parameters
760
+
761
+ Request parameters are available through the `params` hash object. This includes `GET`, `POST` and `PUT` parameters, along with any named parameters you specify in your route strings.
762
+
763
+ ```ruby
764
+ get :public_timeline do
765
+ Status.order(params[:sort_by])
766
+ end
767
+ ```
768
+
769
+ Parameters are automatically populated from the request body on `POST` and `PUT` for form input, JSON and XML content-types.
770
+
771
+ The request:
772
+
773
+ ```
774
+ curl -d '{"text": "140 characters"}' 'http://localhost:9292/statuses' -H Content-Type:application/json -v
775
+ ```
776
+
777
+ The Grape endpoint:
778
+
779
+ ```ruby
780
+ post '/statuses' do
781
+ Status.create!(text: params[:text])
782
+ end
783
+ ```
784
+
785
+ Multipart POSTs and PUTs are supported as well.
786
+
787
+ The request:
788
+
789
+ ```
790
+ curl --form image_file='@image.jpg;type=image/jpg' http://localhost:9292/upload
791
+ ```
792
+
793
+ The Grape endpoint:
794
+
795
+ ```ruby
796
+ post 'upload' do
797
+ # file in params[:image_file]
798
+ end
799
+ ```
800
+
801
+ In the case of conflict between either of:
802
+
803
+ * route string parameters
804
+ * `GET`, `POST` and `PUT` parameters
805
+ * the contents of the request body on `POST` and `PUT`
806
+
807
+ Route string parameters will have precedence.
808
+
809
+ ### Params Class
810
+
811
+ By default parameters are available as `ActiveSupport::HashWithIndifferentAccess`. This can be changed to, for example, Ruby `Hash` or `Hashie::Mash` for the entire API.
812
+
813
+ ```ruby
814
+ class API < Grape::API
815
+ build_with :hashie_mash
816
+
817
+ params do
818
+ optional :color, type: String
819
+ end
820
+ get do
821
+ params.color # instead of params[:color]
822
+ end
823
+ ```
824
+
825
+ The class can also be overridden on individual parameter blocks using `build_with` as follows.
826
+
827
+ ```ruby
828
+ params do
829
+ build_with :hash
830
+ optional :color, type: String
831
+ end
832
+ ```
833
+
834
+ In the example above, `params["color"]` will return `nil` since `params` is a plain `Hash`.
835
+
836
+ Available parameter builders are `:hash`, `:hash_with_indifferent_access`, and `:hashie_mash`.
837
+ See [params_builder](lib/grape/params_builder).
838
+
839
+ ### Declared
840
+
841
+ Grape allows you to access only the parameters that have been declared by your `params` block. It will:
842
+
843
+ * Filter out the params that have been passed, but are not allowed.
844
+ * Include any optional params that are declared but not passed.
845
+ * Perform any parameter renaming on the resulting hash.
846
+
847
+ Consider the following API endpoint:
848
+
849
+ ````ruby
850
+ format :json
851
+
852
+ post 'users/signup' do
853
+ { 'declared_params' => declared(params) }
854
+ end
855
+ ````
856
+
857
+ If you do not specify any parameters, `declared` will return an empty hash.
858
+
859
+ **Request**
860
+
861
+ ````bash
862
+ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d '{"user": {"first_name":"first name", "last_name": "last name"}}'
863
+ ````
864
+
865
+ **Response**
866
+
867
+ ````json
868
+ {
869
+ "declared_params": {}
870
+ }
871
+
872
+ ````
873
+
874
+ Once we add parameters requirements, grape will start returning only the declared parameters.
875
+
876
+ ````ruby
877
+ format :json
878
+
879
+ params do
880
+ optional :user, type: Hash do
881
+ optional :first_name, type: String
882
+ optional :last_name, type: String
883
+ end
884
+ end
885
+
886
+ post 'users/signup' do
887
+ { 'declared_params' => declared(params) }
888
+ end
889
+ ````
890
+
891
+ **Request**
892
+
893
+ ````bash
894
+ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d '{"user": {"first_name":"first name", "last_name": "last name", "random": "never shown"}}'
895
+ ````
896
+
897
+ **Response**
898
+
899
+ ````json
900
+ {
901
+ "declared_params": {
902
+ "user": {
903
+ "first_name": "first name",
904
+ "last_name": "last name"
905
+ }
906
+ }
907
+ }
908
+ ````
909
+
910
+ Missing params that are declared as type `Hash` or `Array` will be included.
911
+
912
+ ````ruby
913
+ format :json
914
+
915
+ params do
916
+ optional :user, type: Hash do
917
+ optional :first_name, type: String
918
+ optional :last_name, type: String
919
+ end
920
+ optional :widgets, type: Array
921
+ end
922
+
923
+ post 'users/signup' do
924
+ { 'declared_params' => declared(params) }
925
+ end
926
+ ````
927
+
928
+ **Request**
929
+
930
+ ````bash
931
+ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d '{}'
932
+ ````
933
+
934
+ **Response**
935
+
936
+ ````json
937
+ {
938
+ "declared_params": {
939
+ "user": {
940
+ "first_name": null,
941
+ "last_name": null
942
+ },
943
+ "widgets": []
944
+ }
945
+ }
946
+ ````
947
+
948
+ The returned hash is an `ActiveSupport::HashWithIndifferentAccess`.
949
+
950
+ The `#declared` method is not available to `before` filters, as those are evaluated prior to parameter coercion.
951
+
952
+ ### Include Parent Namespaces
953
+
954
+ By default `declared(params)` includes parameters that were defined in all parent namespaces. If you want to return only parameters from your current namespace, you can set `include_parent_namespaces` option to `false`.
955
+
956
+ ````ruby
957
+ format :json
958
+
959
+ namespace :parent do
960
+ params do
961
+ requires :parent_name, type: String
962
+ end
963
+
964
+ namespace ':parent_name' do
965
+ params do
966
+ requires :child_name, type: String
967
+ end
968
+ get ':child_name' do
969
+ {
970
+ 'without_parent_namespaces' => declared(params, include_parent_namespaces: false),
971
+ 'with_parent_namespaces' => declared(params, include_parent_namespaces: true),
972
+ }
973
+ end
974
+ end
975
+ end
976
+ ````
977
+
978
+ **Request**
979
+
980
+ ````bash
981
+ curl -X GET -H "Content-Type: application/json" localhost:9292/parent/foo/bar
982
+ ````
983
+
984
+ **Response**
985
+
986
+ ````json
987
+ {
988
+ "without_parent_namespaces": {
989
+ "child_name": "bar"
990
+ },
991
+ "with_parent_namespaces": {
992
+ "parent_name": "foo",
993
+ "child_name": "bar"
994
+ },
995
+ }
996
+ ````
997
+
998
+ ### Include Missing
999
+
1000
+ By default `declared(params)` includes parameters that have `nil` values. If you want to return only the parameters that are not `nil`, you can use the `include_missing` option. By default, `include_missing` is set to `true`. Consider the following API:
1001
+
1002
+ ````ruby
1003
+ format :json
1004
+
1005
+ params do
1006
+ requires :user, type: Hash do
1007
+ requires :first_name, type: String
1008
+ optional :last_name, type: String
1009
+ end
1010
+ end
1011
+
1012
+ post 'users/signup' do
1013
+ { 'declared_params' => declared(params, include_missing: false) }
1014
+ end
1015
+ ````
1016
+
1017
+ **Request**
1018
+
1019
+ ````bash
1020
+ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d '{"user": {"first_name":"first name", "random": "never shown"}}'
1021
+ ````
1022
+
1023
+ **Response with include_missing:false**
1024
+
1025
+ ````json
1026
+ {
1027
+ "declared_params": {
1028
+ "user": {
1029
+ "first_name": "first name"
1030
+ }
1031
+ }
1032
+ }
1033
+ ````
1034
+
1035
+ **Response with include_missing:true**
1036
+
1037
+ ````json
1038
+ {
1039
+ "declared_params": {
1040
+ "user": {
1041
+ "first_name": "first name",
1042
+ "last_name": null
1043
+ }
1044
+ }
1045
+ }
1046
+ ````
1047
+
1048
+ It also works on nested hashes:
1049
+
1050
+ ````ruby
1051
+ format :json
1052
+
1053
+ params do
1054
+ requires :user, type: Hash do
1055
+ requires :first_name, type: String
1056
+ optional :last_name, type: String
1057
+ requires :address, type: Hash do
1058
+ requires :city, type: String
1059
+ optional :region, type: String
1060
+ end
1061
+ end
1062
+ end
1063
+
1064
+ post 'users/signup' do
1065
+ { 'declared_params' => declared(params, include_missing: false) }
1066
+ end
1067
+ ````
1068
+
1069
+ **Request**
1070
+
1071
+ ````bash
1072
+ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d '{"user": {"first_name":"first name", "random": "never shown", "address": { "city": "SF"}}}'
1073
+ ````
1074
+
1075
+ **Response with include_missing:false**
1076
+
1077
+ ````json
1078
+ {
1079
+ "declared_params": {
1080
+ "user": {
1081
+ "first_name": "first name",
1082
+ "address": {
1083
+ "city": "SF"
1084
+ }
1085
+ }
1086
+ }
1087
+ }
1088
+ ````
1089
+
1090
+ **Response with include_missing:true**
1091
+
1092
+ ````json
1093
+ {
1094
+ "declared_params": {
1095
+ "user": {
1096
+ "first_name": "first name",
1097
+ "last_name": null,
1098
+ "address": {
1099
+ "city": "Zurich",
1100
+ "region": null
1101
+ }
1102
+ }
1103
+ }
1104
+ }
1105
+ ````
1106
+
1107
+ Note that an attribute with a `nil` value is not considered *missing* and will also be returned when `include_missing` is set to `false`:
1108
+
1109
+ **Request**
1110
+
1111
+ ````bash
1112
+ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d '{"user": {"first_name":"first name", "last_name": null, "address": { "city": "SF"}}}'
1113
+ ````
1114
+
1115
+ **Response with include_missing:false**
1116
+
1117
+ ````json
1118
+ {
1119
+ "declared_params": {
1120
+ "user": {
1121
+ "first_name": "first name",
1122
+ "last_name": null,
1123
+ "address": { "city": "SF"}
1124
+ }
1125
+ }
1126
+ }
1127
+ ````
1128
+
1129
+ ### Evaluate Given
1130
+
1131
+ By default `declared(params)` will not evaluate `given` and return all parameters. Use `evaluate_given` to evaluate all `given` blocks and return only parameters that satisfy `given` conditions. Consider the following API:
1132
+
1133
+ ````ruby
1134
+ format :json
1135
+
1136
+ params do
1137
+ optional :child_id, type: Integer
1138
+ given :child_id do
1139
+ requires :father_id, type: Integer
1140
+ end
1141
+ end
1142
+
1143
+ post 'child' do
1144
+ { 'declared_params' => declared(params, evaluate_given: true) }
1145
+ end
1146
+ ````
1147
+
1148
+ **Request**
1149
+
1150
+ ````bash
1151
+ curl -X POST -H "Content-Type: application/json" localhost:9292/child -d '{"father_id": 1}'
1152
+ ````
1153
+
1154
+ **Response with evaluate_given:false**
1155
+
1156
+ ````json
1157
+ {
1158
+ "declared_params": {
1159
+ "child_id": null,
1160
+ "father_id": 1
1161
+ }
1162
+ }
1163
+ ````
1164
+
1165
+ **Response with evaluate_given:true**
1166
+
1167
+ ````json
1168
+ {
1169
+ "declared_params": {
1170
+ "child_id": null
1171
+ }
1172
+ }
1173
+ ````
1174
+
1175
+ It also works on nested hashes:
1176
+
1177
+ ````ruby
1178
+ format :json
1179
+
1180
+ params do
1181
+ requires :child, type: Hash do
1182
+ optional :child_id, type: Integer
1183
+ given :child_id do
1184
+ requires :father_id, type: Integer
1185
+ end
1186
+ end
1187
+ end
1188
+
1189
+ post 'child' do
1190
+ { 'declared_params' => declared(params, evaluate_given: true) }
1191
+ end
1192
+ ````
1193
+
1194
+ **Request**
1195
+
1196
+ ````bash
1197
+ curl -X POST -H "Content-Type: application/json" localhost:9292/child -d '{"child": {"father_id": 1}}'
1198
+ ````
1199
+
1200
+ **Response with evaluate_given:false**
1201
+
1202
+ ````json
1203
+ {
1204
+ "declared_params": {
1205
+ "child": {
1206
+ "child_id": null,
1207
+ "father_id": 1
1208
+ }
1209
+ }
1210
+ }
1211
+ ````
1212
+
1213
+ **Response with evaluate_given:true**
1214
+
1215
+ ````json
1216
+ {
1217
+ "declared_params": {
1218
+ "child": {
1219
+ "child_id": null
1220
+ }
1221
+ }
1222
+ }
1223
+ ````
1224
+
1225
+ ### Parameter Precedence
1226
+
1227
+ Using `route_param` takes higher precedence over a regular parameter defined with same name:
1228
+
1229
+ ```ruby
1230
+ params do
1231
+ requires :foo, type: String
1232
+ end
1233
+ route_param :foo do
1234
+ get do
1235
+ { value: params[:foo] }
1236
+ end
1237
+ end
1238
+ ```
1239
+
1240
+ **Request**
1241
+
1242
+ ```bash
1243
+ curl -X POST -H "Content-Type: application/json" localhost:9292/bar -d '{"foo": "baz"}'
1244
+ ```
1245
+
1246
+ **Response**
1247
+
1248
+ ```json
1249
+ {
1250
+ "value": "bar"
1251
+ }
1252
+ ```
1253
+
1254
+ ## Parameter Validation and Coercion
1255
+
1256
+ You can define validations and coercion options for your parameters using a `params` block.
1257
+
1258
+ ```ruby
1259
+ params do
1260
+ requires :id, type: Integer
1261
+ optional :text, type: String, regexp: /\A[a-z]+\z/
1262
+ group :media, type: Hash do
1263
+ requires :url
1264
+ end
1265
+ optional :audio, type: Hash do
1266
+ requires :format, type: Symbol, values: [:mp3, :wav, :aac, :ogg], default: :mp3
1267
+ end
1268
+ mutually_exclusive :media, :audio
1269
+ end
1270
+ put ':id' do
1271
+ # params[:id] is an Integer
1272
+ end
1273
+ ```
1274
+
1275
+ When a type is specified an implicit validation is done after the coercion to ensure the output type is the one declared.
1276
+
1277
+ Optional parameters can have a default value.
1278
+
1279
+ ```ruby
1280
+ params do
1281
+ optional :color, type: String, default: 'blue'
1282
+ optional :random_number, type: Integer, default: -> { Random.rand(1..100) }
1283
+ optional :non_random_number, type: Integer, default: Random.rand(1..100)
1284
+ end
1285
+ ```
1286
+
1287
+ Default values are eagerly evaluated. Above `:non_random_number` will evaluate to the same number for each call to the endpoint of this `params` block. To have the default evaluate lazily with each request use a lambda, like `:random_number` above.
1288
+
1289
+ Note that default values will be passed through to any validation options specified.
1290
+ The following example will always fail if `:color` is not explicitly provided.
1291
+
1292
+ ```ruby
1293
+ params do
1294
+ optional :color, type: String, default: 'blue', values: ['red', 'green']
1295
+ end
1296
+ ```
1297
+
1298
+ The correct implementation is to ensure the default value passes all validations.
1299
+
1300
+ ```ruby
1301
+ params do
1302
+ optional :color, type: String, default: 'blue', values: ['blue', 'red', 'green']
1303
+ end
1304
+ ```
1305
+
1306
+ You can use the value of one parameter as the default value of some other parameter. In this case, if the `primary_color` parameter is not provided, it will have the same value as the `color` one. If both of them not provided, both of them will have `blue` value.
1307
+
1308
+ ```ruby
1309
+ params do
1310
+ optional :color, type: String, default: 'blue'
1311
+ optional :primary_color, type: String, default: -> (params) { params[:color] }
1312
+ end
1313
+ ```
1314
+
1315
+ ### Supported Parameter Types
1316
+
1317
+ The following are all valid types, supported out of the box by Grape:
1318
+
1319
+ * Integer
1320
+ * Float
1321
+ * BigDecimal
1322
+ * Numeric
1323
+ * Date
1324
+ * DateTime
1325
+ * Time
1326
+ * Boolean
1327
+ * String
1328
+ * Symbol
1329
+ * Rack::Multipart::UploadedFile (alias `File`)
1330
+ * JSON
1331
+
1332
+ ### Integer/Fixnum and Coercions
1333
+
1334
+ Please be aware that the behavior differs between Ruby 2.4 and earlier versions.
1335
+ In Ruby 2.4, values consisting of numbers are converted to Integer, but in earlier versions it will be treated as Fixnum.
1336
+
1337
+ ```ruby
1338
+ params do
1339
+ requires :integers, type: Hash do
1340
+ requires :int, coerce: Integer
1341
+ end
1342
+ end
1343
+ get '/int' do
1344
+ params[:integers][:int].class
1345
+ end
1346
+
1347
+ ...
1348
+
1349
+ get '/int' integers: { int: '45' }
1350
+ #=> Integer in ruby 2.4
1351
+ #=> Fixnum in earlier ruby versions
1352
+ ```
1353
+
1354
+ ### Custom Types and Coercions
1355
+
1356
+ Aside from the default set of supported types listed above, any class can be used as a type as long as an explicit coercion method is supplied. If the type implements a class-level `parse` method, Grape will use it automatically. This method must take one string argument and return an instance of the correct type, or return an instance of `Grape::Types::InvalidValue` which optionally accepts a message to be returned in the response.
1357
+
1358
+ ```ruby
1359
+ class Color
1360
+ attr_reader :value
1361
+ def initialize(color)
1362
+ @value = color
1363
+ end
1364
+
1365
+ def self.parse(value)
1366
+ return new(value) if %w[blue red green].include?(value)
1367
+
1368
+ Grape::Types::InvalidValue.new('Unsupported color')
1369
+ end
1370
+ end
1371
+
1372
+ params do
1373
+ requires :color, type: Color, default: Color.new('blue')
1374
+ requires :more_colors, type: Array[Color] # Collections work
1375
+ optional :unique_colors, type: Set[Color] # Duplicates discarded
1376
+ end
1377
+
1378
+ get '/stuff' do
1379
+ # params[:color] is already a Color.
1380
+ params[:color].value
1381
+ end
1382
+ ```
1383
+
1384
+ Alternatively, a custom coercion method may be supplied for any type of parameter using `coerce_with`. Any class or object may be given that implements a `parse` or `call` method, in that order of precedence. The method must accept a single string parameter, and the return value must match the given `type`.
1385
+
1386
+ ```ruby
1387
+ params do
1388
+ requires :passwd, type: String, coerce_with: Base64.method(:decode64)
1389
+ requires :loud_color, type: Color, coerce_with: ->(c) { Color.parse(c.downcase) }
1390
+
1391
+ requires :obj, type: Hash, coerce_with: JSON do
1392
+ requires :words, type: Array[String], coerce_with: ->(val) { val.split(/\s+/) }
1393
+ optional :time, type: Time, coerce_with: Chronic
1394
+ end
1395
+ end
1396
+ ```
1397
+ Note that, a `nil` value will call the custom coercion method, while a missing parameter will not.
1398
+
1399
+ Example of use of `coerce_with` with a lambda (a class with a `parse` method could also have been used)
1400
+ It will parse a string and return an Array of Integers, matching the `Array[Integer]` `type`.
1401
+
1402
+ ```ruby
1403
+ params do
1404
+ requires :values, type: Array[Integer], coerce_with: ->(val) { val.split(/\s+/).map(&:to_i) }
1405
+ end
1406
+ ```
1407
+
1408
+ Grape will assert that coerced values match the given `type`, and will reject the request if they do not. To override this behaviour, custom types may implement a `parsed?` method that should accept a single argument and return `true` if the value passes type validation.
1409
+
1410
+ ```ruby
1411
+ class SecureUri
1412
+ def self.parse(value)
1413
+ URI.parse value
1414
+ end
1415
+
1416
+ def self.parsed?(value)
1417
+ value.is_a? URI::HTTPS
1418
+ end
1419
+ end
1420
+
1421
+ params do
1422
+ requires :secure_uri, type: SecureUri
1423
+ end
1424
+ ```
1425
+
1426
+ ### Multipart File Parameters
1427
+
1428
+ Grape makes use of `Rack::Request`'s built-in support for multipart file parameters. Such parameters can be declared with `type: File`:
1429
+
1430
+ ```ruby
1431
+ params do
1432
+ requires :avatar, type: File
1433
+ end
1434
+ post '/' do
1435
+ params[:avatar][:filename] # => 'avatar.png'
1436
+ params[:avatar][:type] # => 'image/png'
1437
+ params[:avatar][:tempfile] # => #<File>
1438
+ end
1439
+ ```
1440
+
1441
+ ### First-Class `JSON` Types
1442
+
1443
+ Grape supports complex parameters given as JSON-formatted strings using the special `type: JSON` declaration. JSON objects and arrays of objects are accepted equally, with nested validation rules applied to all objects in either case:
1444
+
1445
+ ```ruby
1446
+ params do
1447
+ requires :json, type: JSON do
1448
+ requires :int, type: Integer, values: [1, 2, 3]
1449
+ end
1450
+ end
1451
+ get '/' do
1452
+ params[:json].inspect
1453
+ end
1454
+
1455
+ client.get('/', json: '{"int":1}') # => "{:int=>1}"
1456
+ client.get('/', json: '[{"int":"1"}]') # => "[{:int=>1}]"
1457
+
1458
+ client.get('/', json: '{"int":4}') # => HTTP 400
1459
+ client.get('/', json: '[{"int":4}]') # => HTTP 400
1460
+ ```
1461
+
1462
+ Additionally `type: Array[JSON]` may be used, which explicitly marks the parameter as an array of objects. If a single object is supplied it will be wrapped.
1463
+
1464
+ ```ruby
1465
+ params do
1466
+ requires :json, type: Array[JSON] do
1467
+ requires :int, type: Integer
1468
+ end
1469
+ end
1470
+ get '/' do
1471
+ params[:json].each { |obj| ... } # always works
1472
+ end
1473
+ ```
1474
+ For stricter control over the type of JSON structure which may be supplied, use `type: Array, coerce_with: JSON` or `type: Hash, coerce_with: JSON`.
1475
+
1476
+ ### Multiple Allowed Types
1477
+
1478
+ Variant-type parameters can be declared using the `types` option rather than `type`:
1479
+
1480
+ ```ruby
1481
+ params do
1482
+ requires :status_code, types: [Integer, String, Array[Integer, String]]
1483
+ end
1484
+ get '/' do
1485
+ params[:status_code].inspect
1486
+ end
1487
+
1488
+ client.get('/', status_code: 'OK_GOOD') # => "OK_GOOD"
1489
+ client.get('/', status_code: 300) # => 300
1490
+ client.get('/', status_code: %w(404 NOT FOUND)) # => [404, "NOT", "FOUND"]
1491
+ ```
1492
+
1493
+ As a special case, variant-member-type collections may also be declared, by passing a `Set` or `Array` with more than one member to `type`:
1494
+
1495
+ ```ruby
1496
+ params do
1497
+ requires :status_codes, type: Array[Integer,String]
1498
+ end
1499
+ get '/' do
1500
+ params[:status_codes].inspect
1501
+ end
1502
+
1503
+ client.get('/', status_codes: %w(1 two)) # => [1, "two"]
1504
+ ```
1505
+
1506
+ ### Validation of Nested Parameters
1507
+
1508
+ Parameters can be nested using `group` or by calling `requires` or `optional` with a block.
1509
+ In the [above example](#parameter-validation-and-coercion), this means `params[:media][:url]` is required along with `params[:id]`, and `params[:audio][:format]` is required only if `params[:audio]` is present.
1510
+ With a block, `group`, `requires` and `optional` accept an additional option `type` which can be either `Array` or `Hash`, and defaults to `Array`. Depending on the value, the nested parameters will be treated either as values of a hash or as values of hashes in an array.
1511
+
1512
+ ```ruby
1513
+ params do
1514
+ optional :preferences, type: Array do
1515
+ requires :key
1516
+ requires :value
1517
+ end
1518
+
1519
+ requires :name, type: Hash do
1520
+ requires :first_name
1521
+ requires :last_name
1522
+ end
1523
+ end
1524
+ ```
1525
+
1526
+ ### Dependent Parameters
1527
+
1528
+ Suppose some of your parameters are only relevant if another parameter is given; Grape allows you to express this relationship through the `given` method in your parameters block, like so:
1529
+
1530
+ ```ruby
1531
+ params do
1532
+ optional :shelf_id, type: Integer
1533
+ given :shelf_id do
1534
+ requires :bin_id, type: Integer
1535
+ end
1536
+ end
1537
+ ```
1538
+
1539
+ In the example above Grape will use `blank?` to check whether the `shelf_id` param is present.
1540
+
1541
+ `given` also takes a `Proc` with custom code. Below, the param `description` is required only if the value of `category` is equal `foo`:
1542
+
1543
+ ```ruby
1544
+ params do
1545
+ optional :category
1546
+ given category: ->(val) { val == 'foo' } do
1547
+ requires :description
1548
+ end
1549
+ end
1550
+ ```
1551
+
1552
+ You can rename parameters:
1553
+
1554
+ ```ruby
1555
+ params do
1556
+ optional :category, as: :type
1557
+ given type: ->(val) { val == 'foo' } do
1558
+ requires :description
1559
+ end
1560
+ end
1561
+ ```
1562
+
1563
+ Note: param in `given` should be the renamed one. In the example, it should be `type`, not `category`.
1564
+
1565
+ ### Group Options
1566
+
1567
+ Parameters options can be grouped. It can be useful if you want to extract common validation or types for several parameters.
1568
+ Within these groups, individual parameters can extend or selectively override the common settings, allowing you to maintain the defaults at the group level while still applying parameter-specific rules where necessary.
1569
+
1570
+ The example below presents a typical case when parameters share common options.
1571
+
1572
+ ```ruby
1573
+ params do
1574
+ requires :first_name, type: String, regexp: /w+/, desc: 'First name', documentation: { in: 'body' }
1575
+ optional :middle_name, type: String, regexp: /w+/, desc: 'Middle name', documentation: { in: 'body', x: { nullable: true } }
1576
+ requires :last_name, type: String, regexp: /w+/, desc: 'Last name', documentation: { in: 'body' }
1577
+ end
1578
+ ```
1579
+
1580
+ Grape allows you to present the same logic through the `with` method in your parameters block, like so:
1581
+
1582
+ ```ruby
1583
+ params do
1584
+ with(type: String, regexp: /w+/, documentation: { in: 'body' }) do
1585
+ requires :first_name, desc: 'First name'
1586
+ optional :middle_name, desc: 'Middle name', documentation: { x: { nullable: true } }
1587
+ requires :last_name, desc: 'Last name'
1588
+ end
1589
+ end
1590
+ ```
1591
+
1592
+ You can organize settings into layers using nested `with' blocks. Each layer can use, add to, or change the settings of the layer above it. This helps to keep complex parameters organized and consistent, while still allowing for specific customizations to be made.
1593
+
1594
+ ```ruby
1595
+ params do
1596
+ with(documentation: { in: 'body' }) do # Applies documentation to all nested parameters
1597
+ with(type: String, regexp: /\w+/) do # Applies type and validation to names
1598
+ requires :first_name, desc: 'First name'
1599
+ requires :last_name, desc: 'Last name'
1600
+ end
1601
+ optional :age, type: Integer, desc: 'Age', documentation: { x: { nullable: true } } # Specific settings for 'age'
1602
+ end
1603
+ end
1604
+ ```
1605
+
1606
+ ### Renaming
1607
+
1608
+ You can rename parameters using `as`, which can be useful when refactoring existing APIs:
1609
+
1610
+ ```ruby
1611
+ resource :users do
1612
+ params do
1613
+ requires :email_address, as: :email
1614
+ requires :password
1615
+ end
1616
+ post do
1617
+ User.create!(declared(params)) # User takes email and password
1618
+ end
1619
+ end
1620
+ ```
1621
+
1622
+ The value passed to `as` will be the key when calling `declared(params)`.
1623
+
1624
+ ### Built-in Validators
1625
+
1626
+ #### `allow_blank`
1627
+
1628
+ Parameters can be defined as `allow_blank`, ensuring that they contain a value. By default, `requires` only validates that a parameter was sent in the request, regardless its value. With `allow_blank: false`, empty values or whitespace only values are invalid.
1629
+
1630
+ `allow_blank` can be combined with both `requires` and `optional`. If the parameter is required, it has to contain a value. If it's optional, it's possible to not send it in the request, but if it's being sent, it has to have some value, and not an empty string/only whitespaces.
1631
+
1632
+
1633
+ ```ruby
1634
+ params do
1635
+ requires :username, allow_blank: false
1636
+ optional :first_name, allow_blank: false
1637
+ end
1638
+ ```
1639
+
1640
+ #### `values`
1641
+
1642
+ Parameters can be restricted to a specific set of values with the `:values` option.
1643
+
1644
+
1645
+ ```ruby
1646
+ params do
1647
+ requires :status, type: Symbol, values: [:not_started, :processing, :done]
1648
+ optional :numbers, type: Array[Integer], default: 1, values: [1, 2, 3, 5, 8]
1649
+ end
1650
+ ```
1651
+
1652
+ Supplying a range to the `:values` option ensures that the parameter is (or parameters are) included in that range (using `Range#include?`).
1653
+
1654
+ ```ruby
1655
+ params do
1656
+ requires :latitude, type: Float, values: -90.0..+90.0
1657
+ requires :longitude, type: Float, values: -180.0..+180.0
1658
+ optional :letters, type: Array[String], values: 'a'..'z'
1659
+ end
1660
+ ```
1661
+
1662
+ Note endless ranges are also supported with ActiveSupport >= 6.0, but they require that the type be provided.
1663
+
1664
+ ```ruby
1665
+ params do
1666
+ requires :minimum, type: Integer, values: 10..
1667
+ optional :maximum, type: Integer, values: ..10
1668
+ end
1669
+ ```
1670
+
1671
+ Note that *both* range endpoints have to be a `#kind_of?` your `:type` option (if you don't supply the `:type` option, it will be guessed to be equal to the class of the range's first endpoint). So the following is invalid:
1672
+
1673
+ ```ruby
1674
+ params do
1675
+ requires :invalid1, type: Float, values: 0..10 # 0.kind_of?(Float) => false
1676
+ optional :invalid2, values: 0..10.0 # 10.0.kind_of?(0.class) => false
1677
+ end
1678
+ ```
1679
+
1680
+ The `:values` option can also be supplied with a `Proc`, evaluated lazily with each request.
1681
+ If the Proc has arity zero (i.e. it takes no arguments) it is expected to return either a list or a range which will then be used to validate the parameter.
1682
+
1683
+ For example, given a status model you may want to restrict by hashtags that you have previously defined in the `HashTag` model.
1684
+
1685
+ ```ruby
1686
+ params do
1687
+ requires :hashtag, type: String, values: -> { Hashtag.all.map(&:tag) }
1688
+ end
1689
+ ```
1690
+
1691
+ Alternatively, a Proc with arity one (i.e. taking one argument) can be used to explicitly validate each parameter value. In that case, the Proc is expected to return a truthy value if the parameter value is valid. The parameter will be considered invalid if the Proc returns a falsy value or if it raises a StandardError.
1692
+
1693
+ ```ruby
1694
+ params do
1695
+ requires :number, type: Integer, values: ->(v) { v.even? && v < 25 }
1696
+ end
1697
+ ```
1698
+
1699
+ While Procs are convenient for single cases, consider using [Custom Validators](#custom-validators) in cases where a validation is used more than once.
1700
+
1701
+ Note that [allow_blank](#allow_blank) validator applies while using `:values`. In the following example the absence of `:allow_blank` does not prevent `:state` from receiving blank values because `:allow_blank` defaults to `true`.
1702
+
1703
+ ```ruby
1704
+ params do
1705
+ requires :state, type: Symbol, values: [:active, :inactive]
1706
+ end
1707
+ ```
1708
+
1709
+ #### `except_values`
1710
+
1711
+ Parameters can be restricted from having a specific set of values with the `:except_values` option.
1712
+
1713
+ The `except_values` validator behaves similarly to the `values` validator in that it accepts either an Array, a Range, or a Proc. Unlike the `values` validator, however, `except_values` only accepts Procs with arity zero.
1714
+
1715
+ ```ruby
1716
+ params do
1717
+ requires :browser, except_values: [ 'ie6', 'ie7', 'ie8' ]
1718
+ requires :port, except_values: { value: 0..1024, message: 'is not allowed' }
1719
+ requires :hashtag, except_values: -> { Hashtag.FORBIDDEN_LIST }
1720
+ end
1721
+ ```
1722
+
1723
+ #### `same_as`
1724
+
1725
+ A `same_as` option can be given to ensure that values of parameters match.
1726
+
1727
+ ```ruby
1728
+ params do
1729
+ requires :password
1730
+ requires :password_confirmation, same_as: :password
1731
+ end
1732
+ ```
1733
+
1734
+ #### `length`
1735
+
1736
+ Parameters with types that support `#length` method can be restricted to have a specific length with the `:length` option.
1737
+
1738
+ The validator accepts `:min` or `:max` or both options or only `:is` to validate that the value of the parameter is within the given limits.
1739
+
1740
+ ```ruby
1741
+ params do
1742
+ requires :code, type: String, length: { is: 2 }
1743
+ requires :str, type: String, length: { min: 3 }
1744
+ requires :list, type: [Integer], length: { min: 3, max: 5 }
1745
+ requires :hash, type: Hash, length: { max: 5 }
1746
+ end
1747
+ ```
1748
+
1749
+ #### `regexp`
1750
+
1751
+ Parameters can be restricted to match a specific regular expression with the `:regexp` option. If the value does not match the regular expression an error will be returned. Note that this is true for both `requires` and `optional` parameters.
1752
+
1753
+ ```ruby
1754
+ params do
1755
+ requires :email, regexp: /.+@.+/
1756
+ end
1757
+ ```
1758
+
1759
+ The validator will pass if the parameter was sent without value. To ensure that the parameter contains a value, use `allow_blank: false`.
1760
+
1761
+ ```ruby
1762
+ params do
1763
+ requires :email, allow_blank: false, regexp: /.+@.+/
1764
+ end
1765
+ ```
1766
+
1767
+ #### `mutually_exclusive`
1768
+
1769
+ Parameters can be defined as `mutually_exclusive`, ensuring that they aren't present at the same time in a request.
1770
+
1771
+ ```ruby
1772
+ params do
1773
+ optional :beer
1774
+ optional :wine
1775
+ mutually_exclusive :beer, :wine
1776
+ end
1777
+ ```
1778
+
1779
+ Multiple sets can be defined:
1780
+
1781
+ ```ruby
1782
+ params do
1783
+ optional :beer
1784
+ optional :wine
1785
+ mutually_exclusive :beer, :wine
1786
+ optional :scotch
1787
+ optional :aquavit
1788
+ mutually_exclusive :scotch, :aquavit
1789
+ end
1790
+ ```
1791
+
1792
+ **Warning**: Never define mutually exclusive sets with any required params. Two mutually exclusive required params will mean params are never valid, thus making the endpoint useless. One required param mutually exclusive with an optional param will mean the latter is never valid.
1793
+
1794
+ #### `exactly_one_of`
1795
+
1796
+ Parameters can be defined as 'exactly_one_of', ensuring that exactly one parameter gets selected.
1797
+
1798
+ ```ruby
1799
+ params do
1800
+ optional :beer
1801
+ optional :wine
1802
+ exactly_one_of :beer, :wine
1803
+ end
1804
+ ```
1805
+
1806
+ Note that using `:default` with `mutually_exclusive` will cause multiple parameters to always have a default value and raise a `Grape::Exceptions::Validation` mutually exclusive exception.
1807
+
1808
+ #### `at_least_one_of`
1809
+
1810
+ Parameters can be defined as 'at_least_one_of', ensuring that at least one parameter gets selected.
1811
+
1812
+ ```ruby
1813
+ params do
1814
+ optional :beer
1815
+ optional :wine
1816
+ optional :juice
1817
+ at_least_one_of :beer, :wine, :juice
1818
+ end
1819
+ ```
1820
+
1821
+ #### `all_or_none_of`
1822
+
1823
+ Parameters can be defined as 'all_or_none_of', ensuring that all or none of parameters gets selected.
1824
+
1825
+ ```ruby
1826
+ params do
1827
+ optional :beer
1828
+ optional :wine
1829
+ optional :juice
1830
+ all_or_none_of :beer, :wine, :juice
1831
+ end
1832
+ ```
1833
+
1834
+ #### Nested `mutually_exclusive`, `exactly_one_of`, `at_least_one_of`, `all_or_none_of`
1835
+
1836
+ All of these methods can be used at any nested level.
1837
+
1838
+ ```ruby
1839
+ params do
1840
+ requires :food, type: Hash do
1841
+ optional :meat
1842
+ optional :fish
1843
+ optional :rice
1844
+ at_least_one_of :meat, :fish, :rice
1845
+ end
1846
+ group :drink, type: Hash do
1847
+ optional :beer
1848
+ optional :wine
1849
+ optional :juice
1850
+ exactly_one_of :beer, :wine, :juice
1851
+ end
1852
+ optional :dessert, type: Hash do
1853
+ optional :cake
1854
+ optional :icecream
1855
+ mutually_exclusive :cake, :icecream
1856
+ end
1857
+ optional :recipe, type: Hash do
1858
+ optional :oil
1859
+ optional :meat
1860
+ all_or_none_of :oil, :meat
1861
+ end
1862
+ end
1863
+ ```
1864
+
1865
+ ### Namespace Validation and Coercion
1866
+
1867
+ Namespaces allow parameter definitions and apply to every method within the namespace.
1868
+
1869
+ ```ruby
1870
+ namespace :statuses do
1871
+ params do
1872
+ requires :user_id, type: Integer, desc: 'A user ID.'
1873
+ end
1874
+ namespace ':user_id' do
1875
+ desc "Retrieve a user's status."
1876
+ params do
1877
+ requires :status_id, type: Integer, desc: 'A status ID.'
1878
+ end
1879
+ get ':status_id' do
1880
+ User.find(params[:user_id]).statuses.find(params[:status_id])
1881
+ end
1882
+ end
1883
+ end
1884
+ ```
1885
+
1886
+ The `namespace` method has a number of aliases, including: `group`, `resource`, `resources`, and `segment`. Use whichever reads the best for your API.
1887
+
1888
+ You can conveniently define a route parameter as a namespace using `route_param`.
1889
+
1890
+ ```ruby
1891
+ namespace :statuses do
1892
+ route_param :id do
1893
+ desc 'Returns all replies for a status.'
1894
+ get 'replies' do
1895
+ Status.find(params[:id]).replies
1896
+ end
1897
+ desc 'Returns a status.'
1898
+ get do
1899
+ Status.find(params[:id])
1900
+ end
1901
+ end
1902
+ end
1903
+ ```
1904
+
1905
+ You can also define a route parameter type by passing to `route_param`'s options.
1906
+
1907
+ ```ruby
1908
+ namespace :arithmetic do
1909
+ route_param :n, type: Integer do
1910
+ desc 'Returns in power'
1911
+ get 'power' do
1912
+ params[:n] ** params[:n]
1913
+ end
1914
+ end
1915
+ end
1916
+ ```
1917
+
1918
+ ### Custom Validators
1919
+
1920
+ ```ruby
1921
+ class AlphaNumeric < Grape::Validations::Validators::Base
1922
+ def validate_param!(attr_name, params)
1923
+ unless params[attr_name] =~ /\A[[:alnum:]]+\z/
1924
+ raise Grape::Exceptions::Validation.new params: [@scope.full_name(attr_name)], message: 'must consist of alpha-numeric characters'
1925
+ end
1926
+ end
1927
+ end
1928
+ ```
1929
+
1930
+ ```ruby
1931
+ params do
1932
+ requires :text, alpha_numeric: true
1933
+ end
1934
+ ```
1935
+
1936
+ You can also create custom classes that take parameters.
1937
+
1938
+ ```ruby
1939
+ class Length < Grape::Validations::Validators::Base
1940
+ def validate_param!(attr_name, params)
1941
+ unless params[attr_name].length <= @option
1942
+ raise Grape::Exceptions::Validation.new params: [@scope.full_name(attr_name)], message: "must be at the most #{@option} characters long"
1943
+ end
1944
+ end
1945
+ end
1946
+ ```
1947
+
1948
+ ```ruby
1949
+ params do
1950
+ requires :text, length: 140
1951
+ end
1952
+ ```
1953
+
1954
+ You can also create custom validation that use request to validate the attribute. For example if you want to have parameters that are available to only admins, you can do the following.
1955
+
1956
+ ```ruby
1957
+ class Admin < Grape::Validations::Validators::Base
1958
+ def validate(request)
1959
+ # return if the param we are checking was not in request
1960
+ # @attrs is a list containing the attribute we are currently validating
1961
+ # in our sample case this method once will get called with
1962
+ # @attrs being [:admin_field] and once with @attrs being [:admin_false_field]
1963
+ return unless request.params.key?(@attrs.first)
1964
+ # check if admin flag is set to true
1965
+ return unless @option
1966
+ # check if user is admin or not
1967
+ # as an example get a token from request and check if it's admin or not
1968
+ raise Grape::Exceptions::Validation.new params: @attrs, message: 'Can not set admin-only field.' unless request.headers['X-Access-Token'] == 'admin'
1969
+ end
1970
+ end
1971
+ ```
1972
+
1973
+ And use it in your endpoint definition as:
1974
+
1975
+ ```ruby
1976
+ params do
1977
+ optional :admin_field, type: String, admin: true
1978
+ optional :non_admin_field, type: String
1979
+ optional :admin_false_field, type: String, admin: false
1980
+ end
1981
+ ```
1982
+
1983
+ Every validation will have its own instance of the validator, which means that the validator can have a state.
1984
+
1985
+ ### Validation Errors
1986
+
1987
+ Validation and coercion errors are collected and an exception of type `Grape::Exceptions::ValidationErrors` is raised. If the exception goes uncaught it will respond with a status of 400 and an error message. The validation errors are grouped by parameter name and can be accessed via `Grape::Exceptions::ValidationErrors#errors`.
1988
+
1989
+
1990
+ The default response from a `Grape::Exceptions::ValidationErrors` is a humanly readable string, such as "beer, wine are mutually exclusive", in the following example.
1991
+
1992
+ ```ruby
1993
+ params do
1994
+ optional :beer
1995
+ optional :wine
1996
+ optional :juice
1997
+ exactly_one_of :beer, :wine, :juice
1998
+ end
1999
+ ```
2000
+
2001
+ You can rescue a `Grape::Exceptions::ValidationErrors` and respond with a custom response or turn the response into well-formatted JSON for a JSON API that separates individual parameters and the corresponding error messages. The following `rescue_from` example produces `[{"params":["beer","wine"],"messages":["are mutually exclusive"]}]`.
2002
+
2003
+ ```ruby
2004
+ format :json
2005
+ subject.rescue_from Grape::Exceptions::ValidationErrors do |e|
2006
+ error! e, 400
2007
+ end
2008
+ ```
2009
+
2010
+ `Grape::Exceptions::ValidationErrors#full_messages` returns the validation messages as an array. `Grape::Exceptions::ValidationErrors#message` joins the messages to one string.
2011
+
2012
+ For responding with an array of validation messages, you can use `Grape::Exceptions::ValidationErrors#full_messages`.
2013
+ ```ruby
2014
+ format :json
2015
+ subject.rescue_from Grape::Exceptions::ValidationErrors do |e|
2016
+ error!({ messages: e.full_messages }, 400)
2017
+ end
2018
+ ```
2019
+
2020
+ Grape returns all validation and coercion errors found by default.
2021
+ To skip all subsequent validation checks when a specific param is found invalid, use `fail_fast: true`.
2022
+
2023
+ The following example will not check if `:wine` is present unless it finds `:beer`.
2024
+ ```ruby
2025
+ params do
2026
+ required :beer, fail_fast: true
2027
+ required :wine
2028
+ end
2029
+ ```
2030
+ The result of empty params would be a single `Grape::Exceptions::ValidationErrors` error.
2031
+
2032
+ Similarly, no regular expression test will be performed if `:blah` is blank in the following example.
2033
+ ```ruby
2034
+ params do
2035
+ required :blah, allow_blank: false, regexp: /blah/, fail_fast: true
2036
+ end
2037
+ ```
2038
+
2039
+ ### I18n
2040
+
2041
+ Grape supports I18n for parameter-related error messages, but will fallback to English if translations for the default locale have not been provided. See [en.yml](lib/grape/locale/en.yml) for message keys.
2042
+
2043
+ In case your app enforces available locales only and :en is not included in your available locales, Grape cannot fall back to English and will return the translation key for the error message. To avoid this behaviour, either provide a translation for your default locale or add :en to your available locales.
2044
+
2045
+ ### Custom Validation messages
2046
+
2047
+ Grape supports custom validation messages for parameter-related and coerce-related error messages.
2048
+
2049
+ #### `presence`, `allow_blank`, `values`, `regexp`
2050
+
2051
+ ```ruby
2052
+ params do
2053
+ requires :name, values: { value: 1..10, message: 'not in range from 1 to 10' }, allow_blank: { value: false, message: 'cannot be blank' }, regexp: { value: /^[a-z]+$/, message: 'format is invalid' }, message: 'is required'
2054
+ end
2055
+ ```
2056
+
2057
+ #### `same_as`
2058
+
2059
+ ```ruby
2060
+ params do
2061
+ requires :password
2062
+ requires :password_confirmation, same_as: { value: :password, message: 'not match' }
2063
+ end
2064
+ ```
2065
+
2066
+ #### `length`
2067
+
2068
+ ```ruby
2069
+ params do
2070
+ requires :code, type: String, length: { is: 2, message: 'code is expected to be exactly 2 characters long' }
2071
+ requires :str, type: String, length: { min: 5, message: 'str is expected to be at least 5 characters long' }
2072
+ requires :list, type: [Integer], length: { min: 2, max: 3, message: 'list is expected to have between 2 and 3 elements' }
2073
+ end
2074
+ ```
2075
+
2076
+ #### `all_or_none_of`
2077
+
2078
+ ```ruby
2079
+ params do
2080
+ optional :beer
2081
+ optional :wine
2082
+ optional :juice
2083
+ all_or_none_of :beer, :wine, :juice, message: "all params are required or none is required"
2084
+ end
2085
+ ```
2086
+
2087
+ #### `mutually_exclusive`
2088
+
2089
+ ```ruby
2090
+ params do
2091
+ optional :beer
2092
+ optional :wine
2093
+ optional :juice
2094
+ mutually_exclusive :beer, :wine, :juice, message: "are mutually exclusive cannot pass both params"
2095
+ end
2096
+ ```
2097
+
2098
+ #### `exactly_one_of`
2099
+
2100
+ ```ruby
2101
+ params do
2102
+ optional :beer
2103
+ optional :wine
2104
+ optional :juice
2105
+ exactly_one_of :beer, :wine, :juice, message: { exactly_one: "are missing, exactly one parameter is required", mutual_exclusion: "are mutually exclusive, exactly one parameter is required" }
2106
+ end
2107
+ ```
2108
+
2109
+ #### `at_least_one_of`
2110
+
2111
+ ```ruby
2112
+ params do
2113
+ optional :beer
2114
+ optional :wine
2115
+ optional :juice
2116
+ at_least_one_of :beer, :wine, :juice, message: "are missing, please specify at least one param"
2117
+ end
2118
+ ```
2119
+
2120
+ #### `Coerce`
2121
+
2122
+ ```ruby
2123
+ params do
2124
+ requires :int, type: { value: Integer, message: "type cast is invalid" }
2125
+ end
2126
+ ```
2127
+
2128
+ #### `With Lambdas`
2129
+
2130
+ ```ruby
2131
+ params do
2132
+ requires :name, values: { value: -> { (1..10).to_a }, message: 'not in range from 1 to 10' }
2133
+ end
2134
+ ```
2135
+
2136
+ #### `Pass symbols for i18n translations`
2137
+
2138
+ You can pass a symbol if you want i18n translations for your custom validation messages.
2139
+
2140
+ ```ruby
2141
+ params do
2142
+ requires :name, message: :name_required
2143
+ end
2144
+ ```
2145
+ ```ruby
2146
+ # en.yml
2147
+
2148
+ en:
2149
+ grape:
2150
+ errors:
2151
+ format: ! '%{attributes} %{message}'
2152
+ messages:
2153
+ name_required: 'must be present'
2154
+ ```
2155
+
2156
+ #### Overriding Attribute Names
2157
+
2158
+ You can also override attribute names.
2159
+
2160
+ ```ruby
2161
+ # en.yml
2162
+
2163
+ en:
2164
+ grape:
2165
+ errors:
2166
+ format: ! '%{attributes} %{message}'
2167
+ messages:
2168
+ name_required: 'must be present'
2169
+ attributes:
2170
+ name: 'Oops! Name'
2171
+ ```
2172
+ Will produce 'Oops! Name must be present'
2173
+
2174
+ #### With Default
2175
+
2176
+ You cannot set a custom message option for Default as it requires interpolation `%{option1}: %{value1} is incompatible with %{option2}: %{value2}`. You can change the default error message for Default by changing the `incompatible_option_values` message key inside [en.yml](lib/grape/locale/en.yml)
2177
+
2178
+ ```ruby
2179
+ params do
2180
+ requires :name, values: { value: -> { (1..10).to_a }, message: 'not in range from 1 to 10' }, default: 5
2181
+ end
2182
+ ```
2183
+
2184
+ ### Using `dry-validation` or `dry-schema`
2185
+
2186
+ As an alternative to the `params` DSL described above, you can use a schema or `dry-validation` contract to describe an endpoint's parameters. This can be especially useful if you use the above already in some other parts of your application. If not, you'll need to add `dry-validation` or `dry-schema` to your `Gemfile`.
2187
+
2188
+ Then call `contract` with a contract or schema defined previously:
2189
+
2190
+ ```rb
2191
+ CreateOrdersSchema = Dry::Schema.Params do
2192
+ required(:orders).array(:hash) do
2193
+ required(:name).filled(:string)
2194
+ optional(:volume).maybe(:integer, lt?: 9)
2195
+ end
2196
+ end
2197
+
2198
+ # ...
2199
+
2200
+ contract CreateOrdersSchema
2201
+ ```
2202
+
2203
+ or with a block, using the [schema definition syntax](https://dry-rb.org/gems/dry-schema/1.13/#quick-start):
2204
+
2205
+ ```rb
2206
+ contract do
2207
+ required(:orders).array(:hash) do
2208
+ required(:name).filled(:string)
2209
+ optional(:volume).maybe(:integer, lt?: 9)
2210
+ end
2211
+ end
2212
+ ```
2213
+
2214
+ The latter will define a coercing schema (`Dry::Schema.Params`). When using the former approach, it's up to you to decide whether the input will need coercing.
2215
+
2216
+ The `params` and `contract` declarations can also be used together in the same API, e.g. to describe different parts of a nested namespace for an endpoint.
2217
+
2218
+ ## Headers
2219
+
2220
+ ### Request
2221
+ Request headers are available through the `headers` helper or from `env` in their original form.
2222
+
2223
+ ```ruby
2224
+ get do
2225
+ error!('Unauthorized', 401) unless headers['Secret-Password'] == 'swordfish'
2226
+ end
2227
+ ```
2228
+
2229
+ ```ruby
2230
+ get do
2231
+ error!('Unauthorized', 401) unless env['HTTP_SECRET_PASSWORD'] == 'swordfish'
2232
+ end
2233
+ ```
2234
+
2235
+ #### Header Case Handling
2236
+
2237
+ The above example may have been requested as follows:
2238
+
2239
+ ``` shell
2240
+ curl -H "secret_PassWord: swordfish" ...
2241
+ ```
2242
+
2243
+ The header name will have been normalized for you.
2244
+
2245
+ - In the `header` helper names will be coerced into a downcased kebab case as `secret-password` if using Rack 3.
2246
+ - In the `header` helper names will be coerced into a capitalized kebab case as `Secret-PassWord` if using Rack < 3.
2247
+ - In the `env` collection they appear in all uppercase, in snake case, and prefixed with 'HTTP_' as `HTTP_SECRET_PASSWORD`
2248
+
2249
+ The header name will have been normalized per HTTP standards defined in [RFC2616 Section 4.2](https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2) regardless of what is being sent by a client.
2250
+
2251
+ ### Response
2252
+
2253
+ You can set a response header with `header` inside an API.
2254
+
2255
+ ```ruby
2256
+ header 'X-Robots-Tag', 'noindex'
2257
+ ```
2258
+
2259
+ When raising `error!`, pass additional headers as arguments. Additional headers will be merged with headers set before `error!` call.
2260
+
2261
+ ```ruby
2262
+ error! 'Unauthorized', 401, 'X-Error-Detail' => 'Invalid token.'
2263
+ ```
2264
+
2265
+ ## Routes
2266
+
2267
+ To define routes you can use the `route` method or the shorthands for the HTTP verbs. To define a route that accepts any route set to `:any`.
2268
+ Parts of the path that are denoted with a colon will be interpreted as route parameters.
2269
+
2270
+ ```ruby
2271
+ route :get, 'status' do
2272
+ end
2273
+
2274
+ # is the same as
2275
+
2276
+ get 'status' do
2277
+ end
2278
+
2279
+ # is the same as
2280
+
2281
+ get :status do
2282
+ end
2283
+
2284
+ # is NOT the same as
2285
+
2286
+ get ':status' do # this makes params[:status] available
2287
+ end
2288
+
2289
+ # This will make both params[:status_id] and params[:id] available
2290
+
2291
+ get 'statuses/:status_id/reviews/:id' do
2292
+ end
2293
+ ```
2294
+
2295
+ To declare a namespace that prefixes all routes within, use the `namespace` method. `group`, `resource`, `resources` and `segment` are aliases to this method. Any endpoints within will share their parent context as well as any configuration done in the namespace context.
2296
+
2297
+ The `route_param` method is a convenient method for defining a parameter route segment. If you define a type, it will add a validation for this parameter.
2298
+
2299
+ ```ruby
2300
+ route_param :id, type: Integer do
2301
+ get 'status' do
2302
+ end
2303
+ end
2304
+
2305
+ # is the same as
2306
+
2307
+ namespace ':id' do
2308
+ params do
2309
+ requires :id, type: Integer
2310
+ end
2311
+
2312
+ get 'status' do
2313
+ end
2314
+ end
2315
+ ```
2316
+
2317
+ Optionally, you can define requirements for your named route parameters using regular expressions on namespace or endpoint. The route will match only if all requirements are met.
2318
+
2319
+ ```ruby
2320
+ get ':id', requirements: { id: /[0-9]*/ } do
2321
+ Status.find(params[:id])
2322
+ end
2323
+
2324
+ namespace :outer, requirements: { id: /[0-9]*/ } do
2325
+ get :id do
2326
+ end
2327
+
2328
+ get ':id/edit' do
2329
+ end
2330
+ end
2331
+ ```
2332
+
2333
+ ## Helpers
2334
+
2335
+ You can define helper methods that your endpoints can use with the `helpers` macro by either giving a block or an array of modules.
2336
+
2337
+ ```ruby
2338
+ module StatusHelpers
2339
+ def user_info(user)
2340
+ "#{user} has statused #{user.statuses} status(s)"
2341
+ end
2342
+ end
2343
+
2344
+ module HttpCodesHelpers
2345
+ def unauthorized
2346
+ 401
2347
+ end
2348
+ end
2349
+
2350
+ class API < Grape::API
2351
+ # define helpers with a block
2352
+ helpers do
2353
+ def current_user
2354
+ User.find(params[:user_id])
2355
+ end
2356
+ end
2357
+
2358
+ # or mix in an array of modules
2359
+ helpers StatusHelpers, HttpCodesHelpers
2360
+
2361
+ before do
2362
+ error!('Access Denied', unauthorized) unless current_user
2363
+ end
2364
+
2365
+ get 'info' do
2366
+ # helpers available in your endpoint and filters
2367
+ user_info(current_user)
2368
+ end
2369
+ end
2370
+ ```
2371
+
2372
+ You can define reusable `params` using `helpers`.
2373
+
2374
+ ```ruby
2375
+ class API < Grape::API
2376
+ helpers do
2377
+ params :pagination do
2378
+ optional :page, type: Integer
2379
+ optional :per_page, type: Integer
2380
+ end
2381
+ end
2382
+
2383
+ desc 'Get collection'
2384
+ params do
2385
+ use :pagination # aliases: includes, use_scope
2386
+ end
2387
+ get do
2388
+ Collection.page(params[:page]).per(params[:per_page])
2389
+ end
2390
+ end
2391
+ ```
2392
+
2393
+ You can also define reusable `params` using shared helpers.
2394
+
2395
+ ```ruby
2396
+ module SharedParams
2397
+ extend Grape::API::Helpers
2398
+
2399
+ params :period do
2400
+ optional :start_date
2401
+ optional :end_date
2402
+ end
2403
+
2404
+ params :pagination do
2405
+ optional :page, type: Integer
2406
+ optional :per_page, type: Integer
2407
+ end
2408
+ end
2409
+
2410
+ class API < Grape::API
2411
+ helpers SharedParams
2412
+
2413
+ desc 'Get collection.'
2414
+ params do
2415
+ use :period, :pagination
2416
+ end
2417
+
2418
+ get do
2419
+ Collection
2420
+ .from(params[:start_date])
2421
+ .to(params[:end_date])
2422
+ .page(params[:page])
2423
+ .per(params[:per_page])
2424
+ end
2425
+ end
2426
+ ```
2427
+
2428
+ Helpers support blocks that can help set default values. The following API can return a collection sorted by `id` or `created_at` in `asc` or `desc` order.
2429
+
2430
+ ```ruby
2431
+ module SharedParams
2432
+ extend Grape::API::Helpers
2433
+
2434
+ params :order do |options|
2435
+ optional :order_by, type: Symbol, values: options[:order_by], default: options[:default_order_by]
2436
+ optional :order, type: Symbol, values: %i(asc desc), default: options[:default_order]
2437
+ end
2438
+ end
2439
+
2440
+ class API < Grape::API
2441
+ helpers SharedParams
2442
+
2443
+ desc 'Get a sorted collection.'
2444
+ params do
2445
+ use :order, order_by: %i(id created_at), default_order_by: :created_at, default_order: :asc
2446
+ end
2447
+
2448
+ get do
2449
+ Collection.send(params[:order], params[:order_by])
2450
+ end
2451
+ end
2452
+ ```
2453
+
2454
+ ## Path Helpers
2455
+
2456
+ If you need methods for generating paths inside your endpoints, please see the [grape-route-helpers](https://github.com/reprah/grape-route-helpers) gem.
2457
+
2458
+ ## Parameter Documentation
2459
+
2460
+ You can attach additional documentation to `params` using a `documentation` hash.
2461
+
2462
+ ```ruby
2463
+ params do
2464
+ optional :first_name, type: String, documentation: { example: 'Jim' }
2465
+ requires :last_name, type: String, documentation: { example: 'Smith' }
2466
+ end
2467
+ ```
2468
+
2469
+ If documentation isn't needed (for instance, it is an internal API), documentation can be disabled.
2470
+
2471
+ ```ruby
2472
+ class API < Grape::API
2473
+ do_not_document!
2474
+
2475
+ # endpoints...
2476
+ end
2477
+ ```
2478
+
2479
+ In this case, Grape won't create objects related to documentation which are retained in RAM forever.
2480
+
2481
+ ## Cookies
2482
+
2483
+ You can set, get and delete your cookies very simply using `cookies` method.
2484
+
2485
+ ```ruby
2486
+ class API < Grape::API
2487
+ get 'status_count' do
2488
+ cookies[:status_count] ||= 0
2489
+ cookies[:status_count] += 1
2490
+ { status_count: cookies[:status_count] }
2491
+ end
2492
+
2493
+ delete 'status_count' do
2494
+ { status_count: cookies.delete(:status_count) }
2495
+ end
2496
+ end
2497
+ ```
2498
+
2499
+ Use a hash-based syntax to set more than one value.
2500
+
2501
+ ```ruby
2502
+ cookies[:status_count] = {
2503
+ value: 0,
2504
+ expires: Time.tomorrow,
2505
+ domain: '.twitter.com',
2506
+ path: '/'
2507
+ }
2508
+
2509
+ cookies[:status_count][:value] +=1
2510
+ ```
2511
+
2512
+ Delete a cookie with `delete`.
2513
+
2514
+ ```ruby
2515
+ cookies.delete :status_count
2516
+ ```
2517
+
2518
+ Specify an optional path.
2519
+
2520
+ ```ruby
2521
+ cookies.delete :status_count, path: '/'
2522
+ ```
2523
+
2524
+ ## HTTP Status Code
2525
+
2526
+ By default Grape returns a 201 for `POST`-Requests, 204 for `DELETE`-Requests that don't return any content, and 200 status code for all other Requests.
2527
+ You can use `status` to query and set the actual HTTP Status Code
2528
+
2529
+ ```ruby
2530
+ post do
2531
+ status 202
2532
+
2533
+ if status == 200
2534
+ # do some thing
2535
+ end
2536
+ end
2537
+ ```
2538
+
2539
+ You can also use one of status codes symbols that are provided by [Rack utils](http://www.rubydoc.info/github/rack/rack/Rack/Utils#HTTP_STATUS_CODES-constant)
2540
+
2541
+ ```ruby
2542
+ post do
2543
+ status :no_content
2544
+ end
2545
+ ```
2546
+
2547
+ ## Redirecting
2548
+
2549
+ You can redirect to a new url temporarily (302) or permanently (301).
2550
+
2551
+ ```ruby
2552
+ redirect '/statuses'
2553
+ ```
2554
+
2555
+ ```ruby
2556
+ redirect '/statuses', permanent: true
2557
+ ```
2558
+
2559
+ ## Recognizing Path
2560
+
2561
+ You can recognize the endpoint matched with given path.
2562
+
2563
+ This API returns an instance of `Grape::Endpoint`.
2564
+
2565
+ ```ruby
2566
+ class API < Grape::API
2567
+ get '/statuses' do
2568
+ end
2569
+ end
2570
+
2571
+ API.recognize_path '/statuses'
2572
+ ```
2573
+
2574
+ Since version `2.1.0`, the `recognize_path` method takes into account the parameters type to determine which endpoint should match with given path.
2575
+
2576
+ ```ruby
2577
+ class Books < Grape::API
2578
+ resource :books do
2579
+ route_param :id, type: Integer do
2580
+ # GET /books/:id
2581
+ get do
2582
+ #...
2583
+ end
2584
+ end
2585
+
2586
+ resource :share do
2587
+ # POST /books/share
2588
+ post do
2589
+ # ....
2590
+ end
2591
+ end
2592
+ end
2593
+ end
2594
+
2595
+ API.recognize_path '/books/1' # => /books/:id
2596
+ API.recognize_path '/books/share' # => /books/share
2597
+ API.recognize_path '/books/other' # => nil
2598
+ ```
2599
+
2600
+
2601
+ ## Allowed Methods
2602
+
2603
+ When you add a `GET` route for a resource, a route for the `HEAD` method will also be added automatically. You can disable this behavior with `do_not_route_head!`.
2604
+
2605
+ ``` ruby
2606
+ class API < Grape::API
2607
+ do_not_route_head!
2608
+
2609
+ get '/example' do
2610
+ # only responds to GET
2611
+ end
2612
+ end
2613
+ ```
2614
+
2615
+ When you add a route for a resource, a route for the `OPTIONS` method will also be added. The response to an OPTIONS request will include an "Allow" header listing the supported methods. If the resource has `before` and `after` callbacks they will be executed, but no other callbacks will run.
2616
+
2617
+ ```ruby
2618
+ class API < Grape::API
2619
+ get '/rt_count' do
2620
+ { rt_count: current_user.rt_count }
2621
+ end
2622
+
2623
+ params do
2624
+ requires :value, type: Integer, desc: 'Value to add to the rt count.'
2625
+ end
2626
+ put '/rt_count' do
2627
+ current_user.rt_count += params[:value].to_i
2628
+ { rt_count: current_user.rt_count }
2629
+ end
2630
+ end
2631
+ ```
2632
+
2633
+ ``` shell
2634
+ curl -v -X OPTIONS http://localhost:3000/rt_count
2635
+
2636
+ > OPTIONS /rt_count HTTP/1.1
2637
+ >
2638
+ < HTTP/1.1 204 No Content
2639
+ < Allow: OPTIONS, GET, PUT
2640
+ ```
2641
+
2642
+ You can disable this behavior with `do_not_route_options!`.
2643
+
2644
+ If a request for a resource is made with an unsupported HTTP method, an HTTP 405 (Method Not Allowed) response will be returned. If the resource has `before` callbacks they will be executed, but no other callbacks will run.
2645
+
2646
+ ``` shell
2647
+ curl -X DELETE -v http://localhost:3000/rt_count/
2648
+
2649
+ > DELETE /rt_count/ HTTP/1.1
2650
+ > Host: localhost:3000
2651
+ >
2652
+ < HTTP/1.1 405 Method Not Allowed
2653
+ < Allow: OPTIONS, GET, PUT
2654
+ ```
2655
+
2656
+ ## Raising Exceptions
2657
+
2658
+ You can abort the execution of an API method by raising errors with `error!`.
2659
+
2660
+ ```ruby
2661
+ error! 'Access Denied', 401
2662
+ ```
2663
+
2664
+ Anything that responds to `#to_s` can be given as a first argument to `error!`.
2665
+
2666
+ ```ruby
2667
+ error! :not_found, 404
2668
+ ```
2669
+
2670
+ You can also return JSON formatted objects by raising error! and passing a hash instead of a message.
2671
+
2672
+ ```ruby
2673
+ error!({ error: 'unexpected error', detail: 'missing widget' }, 500)
2674
+ ```
2675
+
2676
+ You can set additional headers for the response. They will be merged with headers set before `error!` call.
2677
+
2678
+ ```ruby
2679
+ error!('Something went wrong', 500, 'X-Error-Detail' => 'Invalid token.')
2680
+ ```
2681
+
2682
+ You can present documented errors with a Grape entity using the the [grape-entity](https://github.com/ruby-grape/grape-entity) gem.
2683
+
2684
+ ```ruby
2685
+ module API
2686
+ class Error < Grape::Entity
2687
+ expose :code
2688
+ expose :message
2689
+ end
2690
+ end
2691
+ ```
2692
+
2693
+ The following example specifies the entity to use in the `http_codes` definition.
2694
+
2695
+ ```ruby
2696
+ desc 'My Route' do
2697
+ failure [[408, 'Unauthorized', API::Error]]
2698
+ end
2699
+ error!({ message: 'Unauthorized' }, 408)
2700
+ ```
2701
+
2702
+ The following example specifies the presented entity explicitly in the error message.
2703
+
2704
+ ```ruby
2705
+ desc 'My Route' do
2706
+ failure [[408, 'Unauthorized']]
2707
+ end
2708
+ error!({ message: 'Unauthorized', with: API::Error }, 408)
2709
+ ```
2710
+
2711
+ ### Default Error HTTP Status Code
2712
+
2713
+ By default Grape returns a 500 status code from `error!`. You can change this with `default_error_status`.
2714
+
2715
+ ``` ruby
2716
+ class API < Grape::API
2717
+ default_error_status 400
2718
+ get '/example' do
2719
+ error! 'This should have http status code 400'
2720
+ end
2721
+ end
2722
+ ```
2723
+
2724
+ ### Handling 404
2725
+
2726
+ For Grape to handle all the 404s for your API, it can be useful to use a catch-all.
2727
+ In its simplest form, it can be like:
2728
+
2729
+ ```ruby
2730
+ route :any, '*path' do
2731
+ error! # or something else
2732
+ end
2733
+ ```
2734
+
2735
+ It is very crucial to __define this endpoint at the very end of your API__, as it literally accepts every request.
2736
+
2737
+ ## Exception Handling
2738
+
2739
+ Grape can be told to rescue all `StandardError` exceptions and return them in the API format.
2740
+
2741
+ ```ruby
2742
+ class Twitter::API < Grape::API
2743
+ rescue_from :all
2744
+ end
2745
+ ```
2746
+
2747
+ This mimics [default `rescue` behaviour](https://ruby-doc.org/core/StandardError.html) when an exception type is not provided.
2748
+ Any other exception should be rescued explicitly, see [below](#exceptions-that-should-be-rescued-explicitly).
2749
+
2750
+ Grape can also rescue from all exceptions and still use the built-in exception handing.
2751
+ This will give the same behavior as `rescue_from :all` with the addition that Grape will use the exception handling defined by all Exception classes that inherit `Grape::Exceptions::Base`.
2752
+
2753
+ The intent of this setting is to provide a simple way to cover the most common exceptions and return any unexpected exceptions in the API format.
2754
+
2755
+ ```ruby
2756
+ class Twitter::API < Grape::API
2757
+ rescue_from :grape_exceptions
2758
+ end
2759
+ ```
2760
+
2761
+ If you want to customize the shape of grape exceptions returned to the user, to match your `:all` handler for example, you can pass a block to `rescue_from :grape_exceptions`.
2762
+
2763
+ ```ruby
2764
+ rescue_from :grape_exceptions do |e|
2765
+ error!(e, e.status)
2766
+ end
2767
+ ```
2768
+
2769
+ You can also rescue specific exceptions.
2770
+
2771
+ ```ruby
2772
+ class Twitter::API < Grape::API
2773
+ rescue_from ArgumentError, UserDefinedError
2774
+ end
2775
+ ```
2776
+
2777
+ In this case ```UserDefinedError``` must be inherited from ```StandardError```.
2778
+
2779
+ Notice that you could combine these two approaches (rescuing custom errors takes precedence). For example, it's useful for handling all exceptions except Grape validation errors.
2780
+
2781
+ ```ruby
2782
+ class Twitter::API < Grape::API
2783
+ rescue_from Grape::Exceptions::ValidationErrors do |e|
2784
+ error!(e, 400)
2785
+ end
2786
+
2787
+ rescue_from :all
2788
+ end
2789
+ ```
2790
+
2791
+ The error format will match the request format. See "Content-Types" below.
2792
+
2793
+ Custom error formatters for existing and additional types can be defined with a proc.
2794
+
2795
+ ```ruby
2796
+ class Twitter::API < Grape::API
2797
+ error_formatter :txt, ->(message, backtrace, options, env, original_exception) {
2798
+ "error: #{message} from #{backtrace}"
2799
+ }
2800
+ end
2801
+ ```
2802
+
2803
+ You can also use a module or class.
2804
+
2805
+ ```ruby
2806
+ module CustomFormatter
2807
+ def self.call(message, backtrace, options, env, original_exception)
2808
+ { message: message, backtrace: backtrace }
2809
+ end
2810
+ end
2811
+
2812
+ class Twitter::API < Grape::API
2813
+ error_formatter :custom, CustomFormatter
2814
+ end
2815
+ ```
2816
+
2817
+ You can rescue all exceptions with a code block. The `error!` wrapper automatically sets the default error code and content-type.
2818
+
2819
+ ```ruby
2820
+ class Twitter::API < Grape::API
2821
+ rescue_from :all do |e|
2822
+ error!("rescued from #{e.class.name}")
2823
+ end
2824
+ end
2825
+ ```
2826
+
2827
+ Optionally, you can set the format, status code and headers.
2828
+
2829
+ ```ruby
2830
+ class Twitter::API < Grape::API
2831
+ format :json
2832
+ rescue_from :all do |e|
2833
+ error!({ error: 'Server error.' }, 500, { 'Content-Type' => 'text/error' })
2834
+ end
2835
+ end
2836
+ ```
2837
+
2838
+ You can also rescue all exceptions with a code block and handle the Rack response at the lowest level.
2839
+
2840
+ ```ruby
2841
+ class Twitter::API < Grape::API
2842
+ rescue_from :all do |e|
2843
+ Rack::Response.new([ e.message ], 500, { 'Content-type' => 'text/error' })
2844
+ end
2845
+ end
2846
+ ```
2847
+
2848
+ Or rescue specific exceptions.
2849
+
2850
+ ```ruby
2851
+ class Twitter::API < Grape::API
2852
+ rescue_from ArgumentError do |e|
2853
+ error!("ArgumentError: #{e.message}")
2854
+ end
2855
+
2856
+ rescue_from NoMethodError do |e|
2857
+ error!("NoMethodError: #{e.message}")
2858
+ end
2859
+ end
2860
+ ```
2861
+
2862
+ By default, `rescue_from` will rescue the exceptions listed and all their subclasses.
2863
+
2864
+ Assume you have the following exception classes defined.
2865
+
2866
+ ```ruby
2867
+ module APIErrors
2868
+ class ParentError < StandardError; end
2869
+ class ChildError < ParentError; end
2870
+ end
2871
+ ```
2872
+
2873
+ Then the following `rescue_from` clause will rescue exceptions of type `APIErrors::ParentError` and its subclasses (in this case `APIErrors::ChildError`).
2874
+
2875
+ ```ruby
2876
+ rescue_from APIErrors::ParentError do |e|
2877
+ error!({
2878
+ error: "#{e.class} error",
2879
+ message: e.message
2880
+ }, e.status)
2881
+ end
2882
+ ```
2883
+
2884
+ To only rescue the base exception class, set `rescue_subclasses: false`.
2885
+ The code below will rescue exceptions of type `RuntimeError` but _not_ its subclasses.
2886
+
2887
+ ```ruby
2888
+ rescue_from RuntimeError, rescue_subclasses: false do |e|
2889
+ error!({
2890
+ status: e.status,
2891
+ message: e.message,
2892
+ errors: e.errors
2893
+ }, e.status)
2894
+ end
2895
+ ```
2896
+
2897
+ Helpers are also available inside `rescue_from`.
2898
+
2899
+ ```ruby
2900
+ class Twitter::API < Grape::API
2901
+ format :json
2902
+ helpers do
2903
+ def server_error!
2904
+ error!({ error: 'Server error.' }, 500, { 'Content-Type' => 'text/error' })
2905
+ end
2906
+ end
2907
+
2908
+ rescue_from :all do |e|
2909
+ server_error!
2910
+ end
2911
+ end
2912
+ ```
2913
+
2914
+ The `rescue_from` handler must return a `Rack::Response` object, call `error!`, or raise an exception (either the original exception or another custom one). The exception raised in `rescue_from` will be handled outside Grape. For example, if you mount Grape in Rails, the exception will be handle by [Rails Action Controller](https://guides.rubyonrails.org/action_controller_overview.html#rescue).
2915
+
2916
+ Alternately, use the `with` option in `rescue_from` to specify a method or a `proc`.
2917
+
2918
+ ```ruby
2919
+ class Twitter::API < Grape::API
2920
+ format :json
2921
+ helpers do
2922
+ def server_error!
2923
+ error!({ error: 'Server error.' }, 500, { 'Content-Type' => 'text/error' })
2924
+ end
2925
+ end
2926
+
2927
+ rescue_from :all, with: :server_error!
2928
+ rescue_from ArgumentError, with: -> { Rack::Response.new('rescued with a method', 400) }
2929
+ end
2930
+ ```
2931
+
2932
+ Inside the `rescue_from` block, the environment of the original controller method(`.self` receiver) is accessible through the `#context` method.
2933
+
2934
+ ```ruby
2935
+ class Twitter::API < Grape::API
2936
+ rescue_from :all do |e|
2937
+ user_id = context.params[:user_id]
2938
+ error!("error for #{user_id}")
2939
+ end
2940
+ end
2941
+ ```
2942
+
2943
+ #### Rescuing exceptions inside namespaces
2944
+
2945
+ You could put `rescue_from` clauses inside a namespace and they will take precedence over ones
2946
+ defined in the root scope:
2947
+
2948
+ ```ruby
2949
+ class Twitter::API < Grape::API
2950
+ rescue_from ArgumentError do |e|
2951
+ error!("outer")
2952
+ end
2953
+
2954
+ namespace :statuses do
2955
+ rescue_from ArgumentError do |e|
2956
+ error!("inner")
2957
+ end
2958
+ get do
2959
+ raise ArgumentError.new
2960
+ end
2961
+ end
2962
+ end
2963
+ ```
2964
+
2965
+ Here `'inner'` will be result of handling occurred `ArgumentError`.
2966
+
2967
+ #### Unrescuable Exceptions
2968
+
2969
+ `Grape::Exceptions::InvalidVersionHeader`, which is raised when the version in the request header doesn't match the currently evaluated version for the endpoint, will _never_ be rescued from a `rescue_from` block (even a `rescue_from :all`) This is because Grape relies on Rack to catch that error and try the next versioned-route for cases where there exist identical Grape endpoints with different versions.
2970
+
2971
+ #### Exceptions that should be rescued explicitly
2972
+
2973
+ Any exception that is not subclass of `StandardError` should be rescued explicitly.
2974
+ Usually it is not a case for an application logic as such errors point to problems in Ruby runtime.
2975
+ This is following [standard recommendations for exceptions handling](https://ruby-doc.org/core/Exception.html).
2976
+
2977
+ ## Logging
2978
+
2979
+ `Grape::API` provides a `logger` method which by default will return an instance of the `Logger` class from Ruby's standard library.
2980
+
2981
+ To log messages from within an endpoint, you need to define a helper to make the logger available in the endpoint context.
2982
+
2983
+ ```ruby
2984
+ class API < Grape::API
2985
+ helpers do
2986
+ def logger
2987
+ API.logger
2988
+ end
2989
+ end
2990
+ post '/statuses' do
2991
+ logger.info "#{current_user} has statused"
2992
+ end
2993
+ end
2994
+ ```
2995
+
2996
+ To change the logger level.
2997
+
2998
+ ```ruby
2999
+ class API < Grape::API
3000
+ self.logger.level = Logger::INFO
3001
+ end
3002
+ ```
3003
+
3004
+ You can also set your own logger.
3005
+
3006
+ ```ruby
3007
+ class MyLogger
3008
+ def warning(message)
3009
+ puts "this is a warning: #{message}"
3010
+ end
3011
+ end
3012
+
3013
+ class API < Grape::API
3014
+ logger MyLogger.new
3015
+ helpers do
3016
+ def logger
3017
+ API.logger
3018
+ end
3019
+ end
3020
+ get '/statuses' do
3021
+ logger.warning "#{current_user} has statused"
3022
+ end
3023
+ end
3024
+ ```
3025
+
3026
+ For similar to Rails request logging try the [grape_logging](https://github.com/aserafin/grape_logging) or [grape-middleware-logger](https://github.com/ridiculous/grape-middleware-logger) gems.
3027
+
3028
+ ## API Formats
3029
+
3030
+ Your API can declare which content-types to support by using `content_type`. If you do not specify any, Grape will support _XML_, _JSON_, _BINARY_, and _TXT_ content-types. The default format is `:txt`; you can change this with `default_format`. Essentially, the two APIs below are equivalent.
3031
+
3032
+ ```ruby
3033
+ class Twitter::API < Grape::API
3034
+ # no content_type declarations, so Grape uses the defaults
3035
+ end
3036
+
3037
+ class Twitter::API < Grape::API
3038
+ # the following declarations are equivalent to the defaults
3039
+
3040
+ content_type :xml, 'application/xml'
3041
+ content_type :json, 'application/json'
3042
+ content_type :binary, 'application/octet-stream'
3043
+ content_type :txt, 'text/plain'
3044
+
3045
+ default_format :txt
3046
+ end
3047
+ ```
3048
+
3049
+ If you declare any `content_type` whatsoever, the Grape defaults will be overridden. For example, the following API will only support the `:xml` and `:rss` content-types, but not `:txt`, `:json`, or `:binary`. Importantly, this means the `:txt` default format is not supported! So, make sure to set a new `default_format`.
3050
+
3051
+ ```ruby
3052
+ class Twitter::API < Grape::API
3053
+ content_type :xml, 'application/xml'
3054
+ content_type :rss, 'application/xml+rss'
3055
+
3056
+ default_format :xml
3057
+ end
3058
+ ```
3059
+
3060
+ Serialization takes place automatically. For example, you do not have to call `to_json` in each JSON API endpoint implementation. The response format (and thus the automatic serialization) is determined in the following order:
3061
+ * Use the file extension, if specified. If the file is .json, choose the JSON format.
3062
+ * Use the value of the `format` parameter in the query string, if specified.
3063
+ * Use the format set by the `format` option, if specified.
3064
+ * Attempt to find an acceptable format from the `Accept` header.
3065
+ * Use the default format, if specified by the `default_format` option.
3066
+ * Default to `:txt`.
3067
+
3068
+ For example, consider the following API.
3069
+
3070
+ ```ruby
3071
+ class MultipleFormatAPI < Grape::API
3072
+ content_type :xml, 'application/xml'
3073
+ content_type :json, 'application/json'
3074
+
3075
+ default_format :json
3076
+
3077
+ get :hello do
3078
+ { hello: 'world' }
3079
+ end
3080
+ end
3081
+ ```
3082
+
3083
+ * `GET /hello` (with an `Accept: */*` header) does not have an extension or a `format` parameter, so it will respond with JSON (the default format).
3084
+ * `GET /hello.xml` has a recognized extension, so it will respond with XML.
3085
+ * `GET /hello?format=xml` has a recognized `format` parameter, so it will respond with XML.
3086
+ * `GET /hello.xml?format=json` has a recognized extension (which takes precedence over the `format` parameter), so it will respond with XML.
3087
+ * `GET /hello.xls` (with an `Accept: */*` header) has an extension, but that extension is not recognized, so it will respond with JSON (the default format).
3088
+ * `GET /hello.xls` with an `Accept: application/xml` header has an unrecognized extension, but the `Accept` header corresponds to a recognized format, so it will respond with XML.
3089
+ * `GET /hello.xls` with an `Accept: text/plain` header has an unrecognized extension *and* an unrecognized `Accept` header, so it will respond with JSON (the default format).
3090
+
3091
+ You can override this process explicitly by calling `api_format` in the API itself.
3092
+ For example, the following API will let you upload arbitrary files and return their contents as an attachment with the correct MIME type.
3093
+
3094
+ ```ruby
3095
+ class Twitter::API < Grape::API
3096
+ post 'attachment' do
3097
+ filename = params[:file][:filename]
3098
+ content_type MIME::Types.type_for(filename)[0].to_s
3099
+ api_format :binary # there's no formatter for :binary, data will be returned "as is"
3100
+ header 'Content-Disposition', "attachment; filename*=UTF-8''#{CGI.escape(filename)}"
3101
+ params[:file][:tempfile].read
3102
+ end
3103
+ end
3104
+ ```
3105
+
3106
+ You can have your API only respond to a single format with `format`. If you use this, the API will **not** respond to file extensions other than specified in `format`. For example, consider the following API.
3107
+
3108
+ ```ruby
3109
+ class SingleFormatAPI < Grape::API
3110
+ format :json
3111
+
3112
+ get :hello do
3113
+ { hello: 'world' }
3114
+ end
3115
+ end
3116
+ ```
3117
+
3118
+ * `GET /hello` will respond with JSON.
3119
+ * `GET /hello.json` will respond with JSON.
3120
+ * `GET /hello.xml`, `GET /hello.foobar`, or *any* other extension will respond with an HTTP 404 error code.
3121
+ * `GET /hello?format=xml` will respond with an HTTP 406 error code, because the XML format specified by the request parameter is not supported.
3122
+ * `GET /hello` with an `Accept: application/xml` header will still respond with JSON, since it could not negotiate a recognized content-type from the headers and JSON is the effective default.
3123
+
3124
+ The formats apply to parsing, too. The following API will only respond to the JSON content-type and will not parse any other input than `application/json`, `application/x-www-form-urlencoded`, `multipart/form-data`, `multipart/related` and `multipart/mixed`. All other requests will fail with an HTTP 406 error code.
3125
+
3126
+ ```ruby
3127
+ class Twitter::API < Grape::API
3128
+ format :json
3129
+ end
3130
+ ```
3131
+
3132
+ When the content-type is omitted, Grape will return a 406 error code unless `default_format` is specified.
3133
+ The following API will try to parse any data without a content-type using a JSON parser.
3134
+
3135
+ ```ruby
3136
+ class Twitter::API < Grape::API
3137
+ format :json
3138
+ default_format :json
3139
+ end
3140
+ ```
3141
+
3142
+ If you combine `format` with `rescue_from :all`, errors will be rendered using the same format.
3143
+ If you do not want this behavior, set the default error formatter with `default_error_formatter`.
3144
+
3145
+ ```ruby
3146
+ class Twitter::API < Grape::API
3147
+ format :json
3148
+ content_type :txt, 'text/plain'
3149
+ default_error_formatter :txt
3150
+ end
3151
+ ```
3152
+
3153
+ Custom formatters for existing and additional types can be defined with a proc.
3154
+
3155
+ ```ruby
3156
+ class Twitter::API < Grape::API
3157
+ content_type :xls, 'application/vnd.ms-excel'
3158
+ formatter :xls, ->(object, env) { object.to_xls }
3159
+ end
3160
+ ```
3161
+
3162
+ You can also use a module or class.
3163
+
3164
+ ```ruby
3165
+ module XlsFormatter
3166
+ def self.call(object, env)
3167
+ object.to_xls
3168
+ end
3169
+ end
3170
+
3171
+ class Twitter::API < Grape::API
3172
+ content_type :xls, 'application/vnd.ms-excel'
3173
+ formatter :xls, XlsFormatter
3174
+ end
3175
+ ```
3176
+
3177
+ Built-in formatters are the following.
3178
+
3179
+ * `:json`: use object's `to_json` when available, otherwise call `MultiJson.dump`
3180
+ * `:xml`: use object's `to_xml` when available, usually via `MultiXml`
3181
+ * `:txt`: use object's `to_txt` when available, otherwise `to_s`
3182
+ * `:serializable_hash`: use object's `serializable_hash` when available, otherwise fallback to `:json`
3183
+ * `:binary`: data will be returned "as is"
3184
+
3185
+ If a body is present in a request to an API, with a Content-Type header value that is of an unsupported type a "415 Unsupported Media Type" error code will be returned by Grape.
3186
+
3187
+ Response statuses that indicate no content as defined by [Rack](https://github.com/rack) [here](https://github.com/rack/rack/blob/master/lib/rack/utils.rb#L567) will bypass serialization and the body entity - though there should be none - will not be modified.
3188
+
3189
+ ### JSONP
3190
+
3191
+ Grape supports JSONP via [Rack::JSONP](https://github.com/rack/rack-contrib), part of the [rack-contrib](https://github.com/rack/rack-contrib) gem. Add `rack-contrib` to your `Gemfile`.
3192
+
3193
+ ```ruby
3194
+ require 'rack/contrib'
3195
+
3196
+ class API < Grape::API
3197
+ use Rack::JSONP
3198
+ format :json
3199
+ get '/' do
3200
+ 'Hello World'
3201
+ end
3202
+ end
3203
+ ```
3204
+
3205
+ ### CORS
3206
+
3207
+ Grape supports CORS via [Rack::CORS](https://github.com/cyu/rack-cors), part of the [rack-cors](https://github.com/cyu/rack-cors) gem. Add `rack-cors` to your `Gemfile`, then use the middleware in your config.ru file.
3208
+
3209
+ ```ruby
3210
+ require 'rack/cors'
3211
+
3212
+ use Rack::Cors do
3213
+ allow do
3214
+ origins '*'
3215
+ resource '*', headers: :any, methods: :get
3216
+ end
3217
+ end
3218
+
3219
+ run Twitter::API
3220
+
3221
+ ```
3222
+
3223
+ ## Content-type
3224
+
3225
+ Content-type is set by the formatter. You can override the content-type of the response at runtime by setting the `Content-Type` header.
3226
+
3227
+ ```ruby
3228
+ class API < Grape::API
3229
+ get '/home_timeline_js' do
3230
+ content_type 'application/javascript'
3231
+ "var statuses = ...;"
3232
+ end
3233
+ end
3234
+ ```
3235
+
3236
+ ## API Data Formats
3237
+
3238
+ Grape accepts and parses input data sent with the POST and PUT methods as described in the Parameters section above. It also supports custom data formats. You must declare additional content-types via `content_type` and optionally supply a parser via `parser` unless a parser is already available within Grape to enable a custom format. Such a parser can be a function or a class.
3239
+
3240
+ With a parser, parsed data is available "as-is" in `env['api.request.body']`.
3241
+ Without a parser, data is available "as-is" and in `env['api.request.input']`.
3242
+
3243
+ The following example is a trivial parser that will assign any input with the "text/custom" content-type to `:value`. The parameter will be available via `params[:value]` inside the API call.
3244
+
3245
+ ```ruby
3246
+ module CustomParser
3247
+ def self.call(object, env)
3248
+ { value: object.to_s }
3249
+ end
3250
+ end
3251
+ ```
3252
+
3253
+ ```ruby
3254
+ content_type :txt, 'text/plain'
3255
+ content_type :custom, 'text/custom'
3256
+ parser :custom, CustomParser
3257
+
3258
+ put 'value' do
3259
+ params[:value]
3260
+ end
3261
+ ```
3262
+
3263
+ You can invoke the above API as follows.
3264
+
3265
+ ```
3266
+ curl -X PUT -d 'data' 'http://localhost:9292/value' -H Content-Type:text/custom -v
3267
+ ```
3268
+
3269
+ You can disable parsing for a content-type with `nil`. For example, `parser :json, nil` will disable JSON parsing altogether. The request data is then available as-is in `env['api.request.body']`.
3270
+
3271
+ ## JSON and XML Processors
3272
+
3273
+ Grape uses `JSON` and `ActiveSupport::XmlMini` for JSON and XML parsing by default. It also detects and supports [multi_json](https://github.com/intridea/multi_json) and [multi_xml](https://github.com/sferik/multi_xml). Adding those gems to your Gemfile and requiring them will enable them and allow you to swap the JSON and XML back-ends.
3274
+
3275
+ ## RESTful Model Representations
3276
+
3277
+ Grape supports a range of ways to present your data with some help from a generic `present` method, which accepts two arguments: the object to be presented and the options associated with it. The options hash may include `:with`, which defines the entity to expose.
3278
+
3279
+ ### Grape Entities
3280
+
3281
+ Add the [grape-entity](https://github.com/ruby-grape/grape-entity) gem to your Gemfile.
3282
+ Please refer to the [grape-entity documentation](https://github.com/ruby-grape/grape-entity/blob/master/README.md)
3283
+ for more details.
3284
+
3285
+ The following example exposes statuses.
3286
+
3287
+ ```ruby
3288
+ module API
3289
+ module Entities
3290
+ class Status < Grape::Entity
3291
+ expose :user_name
3292
+ expose :text, documentation: { type: 'string', desc: 'Status update text.' }
3293
+ expose :ip, if: { type: :full }
3294
+ expose :user_type, :user_id, if: ->(status, options) { status.user.public? }
3295
+ expose :digest do |status, options|
3296
+ Digest::MD5.hexdigest(status.txt)
3297
+ end
3298
+ expose :replies, using: API::Status, as: :replies
3299
+ end
3300
+ end
3301
+
3302
+ class Statuses < Grape::API
3303
+ version 'v1'
3304
+
3305
+ desc 'Statuses index' do
3306
+ params: API::Entities::Status.documentation
3307
+ end
3308
+ get '/statuses' do
3309
+ statuses = Status.all
3310
+ type = current_user.admin? ? :full : :default
3311
+ present statuses, with: API::Entities::Status, type: type
3312
+ end
3313
+ end
3314
+ end
3315
+ ```
3316
+
3317
+ You can use entity documentation directly in the params block with `using: Entity.documentation`.
3318
+
3319
+ ```ruby
3320
+ module API
3321
+ class Statuses < Grape::API
3322
+ version 'v1'
3323
+
3324
+ desc 'Create a status'
3325
+ params do
3326
+ requires :all, except: [:ip], using: API::Entities::Status.documentation.except(:id)
3327
+ end
3328
+ post '/status' do
3329
+ Status.create! params
3330
+ end
3331
+ end
3332
+ end
3333
+ ```
3334
+
3335
+ You can present with multiple entities using an optional Symbol argument.
3336
+
3337
+ ```ruby
3338
+ get '/statuses' do
3339
+ statuses = Status.all.page(1).per(20)
3340
+ present :total_page, 10
3341
+ present :per_page, 20
3342
+ present :statuses, statuses, with: API::Entities::Status
3343
+ end
3344
+ ```
3345
+
3346
+ The response will be
3347
+
3348
+ ```
3349
+ {
3350
+ total_page: 10,
3351
+ per_page: 20,
3352
+ statuses: []
3353
+ }
3354
+ ```
3355
+
3356
+ In addition to separately organizing entities, it may be useful to put them as namespaced classes underneath the model they represent.
3357
+
3358
+ ```ruby
3359
+ class Status
3360
+ def entity
3361
+ Entity.new(self)
3362
+ end
3363
+
3364
+ class Entity < Grape::Entity
3365
+ expose :text, :user_id
3366
+ end
3367
+ end
3368
+ ```
3369
+
3370
+ If you organize your entities this way, Grape will automatically detect the `Entity` class and use it to present your models. In this example, if you added `present Status.new` to your endpoint, Grape will automatically detect that there is a `Status::Entity` class and use that as the representative entity. This can still be overridden by using the `:with` option or an explicit `represents` call.
3371
+
3372
+ You can present `hash` with `Grape::Presenters::Presenter` to keep things consistent.
3373
+
3374
+ ```ruby
3375
+ get '/users' do
3376
+ present { id: 10, name: :dgz }, with: Grape::Presenters::Presenter
3377
+ end
3378
+ ````
3379
+ The response will be
3380
+
3381
+ ```ruby
3382
+ {
3383
+ id: 10,
3384
+ name: 'dgz'
3385
+ }
3386
+ ```
3387
+
3388
+ It has the same result with
3389
+
3390
+ ```ruby
3391
+ get '/users' do
3392
+ present :id, 10
3393
+ present :name, :dgz
3394
+ end
3395
+ ```
3396
+
3397
+ ### Hypermedia and Roar
3398
+
3399
+ You can use [Roar](https://github.com/apotonick/roar) to render HAL or Collection+JSON with the help of [grape-roar](https://github.com/ruby-grape/grape-roar), which defines a custom JSON formatter and enables presenting entities with Grape's `present` keyword.
3400
+
3401
+ ### Rabl
3402
+
3403
+ You can use [Rabl](https://github.com/nesquena/rabl) templates with the help of the [grape-rabl](https://github.com/ruby-grape/grape-rabl) gem, which defines a custom Grape Rabl formatter.
3404
+
3405
+ ### Active Model Serializers
3406
+
3407
+ You can use [Active Model Serializers](https://github.com/rails-api/active_model_serializers) serializers with the help of the [grape-active_model_serializers](https://github.com/jrhe/grape-active_model_serializers) gem, which defines a custom Grape AMS formatter.
3408
+
3409
+ ## Sending Raw or No Data
3410
+
3411
+ In general, use the binary format to send raw data.
3412
+
3413
+ ```ruby
3414
+ class API < Grape::API
3415
+ get '/file' do
3416
+ content_type 'application/octet-stream'
3417
+ File.binread 'file.bin'
3418
+ end
3419
+ end
3420
+ ```
3421
+
3422
+ You can set the response body explicitly with `body`.
3423
+
3424
+ ```ruby
3425
+ class API < Grape::API
3426
+ get '/' do
3427
+ content_type 'text/plain'
3428
+ body 'Hello World'
3429
+ # return value ignored
3430
+ end
3431
+ end
3432
+ ```
3433
+
3434
+ Use `body false` to return `204 No Content` without any data or content-type.
3435
+
3436
+ If you want to empty the body with an HTTP status code other than `204 No Content`, you can override the status code after specifying `body false` as follows
3437
+
3438
+ ```ruby
3439
+ class API < Grape::API
3440
+ get '/' do
3441
+ body false
3442
+ status 304
3443
+ end
3444
+ end
3445
+ ```
3446
+
3447
+ You can also set the response to a file with `sendfile`. This works with the [Rack::Sendfile](https://www.rubydoc.info/gems/rack/Rack/Sendfile) middleware to optimally send the file through your web server software.
3448
+
3449
+ ```ruby
3450
+ class API < Grape::API
3451
+ get '/' do
3452
+ sendfile '/path/to/file'
3453
+ end
3454
+ end
3455
+ ```
3456
+
3457
+ To stream a file in chunks use `stream`
3458
+
3459
+ ```ruby
3460
+ class API < Grape::API
3461
+ get '/' do
3462
+ stream '/path/to/file'
3463
+ end
3464
+ end
3465
+ ```
3466
+
3467
+ If you want to stream non-file data use the `stream` method and a `Stream` object.
3468
+ This is an object that responds to `each` and yields for each chunk to send to the client.
3469
+ Each chunk will be sent as it is yielded instead of waiting for all of the content to be available.
3470
+
3471
+ ```ruby
3472
+ class MyStream
3473
+ def each
3474
+ yield 'part 1'
3475
+ yield 'part 2'
3476
+ yield 'part 3'
3477
+ end
3478
+ end
3479
+
3480
+ class API < Grape::API
3481
+ get '/' do
3482
+ stream MyStream.new
3483
+ end
3484
+ end
3485
+ ```
3486
+
3487
+ ## Authentication
3488
+
3489
+ ### Basic Auth
3490
+
3491
+ Grape has built-in Basic authentication (the given `block` is executed in the context of the current `Endpoint`). Authentication applies to the current namespace and any children, but not parents.
3492
+
3493
+ ```ruby
3494
+ http_basic do |username, password|
3495
+ # verify user's password here
3496
+ # IMPORTANT: make sure you use a comparison method which isn't prone to a timing attack
3497
+ end
3498
+ ```
3499
+
3500
+ ### Register custom middleware for authentication
3501
+
3502
+ Grape can use custom Middleware for authentication. How to implement these Middleware have a look at `Rack::Auth::Basic` or similar implementations.
3503
+
3504
+ For registering a Middleware you need the following options:
3505
+
3506
+ * `label` - the name for your authenticator to use it later
3507
+ * `MiddlewareClass` - the MiddlewareClass to use for authentication
3508
+ * `option_lookup_proc` - A Proc with one Argument to lookup the options at runtime (return value is an `Array` as Parameter for the Middleware).
3509
+
3510
+ Example:
3511
+
3512
+ ```ruby
3513
+
3514
+ Grape::Middleware::Auth::Strategies.add(:my_auth, AuthMiddleware, ->(options) { [options[:realm]] } )
3515
+
3516
+
3517
+ auth :my_auth, { realm: 'Test Api'} do |credentials|
3518
+ # lookup the user's password here
3519
+ { 'user1' => 'password1' }[username]
3520
+ end
3521
+
3522
+ ```
3523
+
3524
+ Use [Doorkeeper](https://github.com/doorkeeper-gem/doorkeeper), [warden-oauth2](https://github.com/opperator/warden-oauth2) or [rack-oauth2](https://github.com/nov/rack-oauth2) for OAuth2 support.
3525
+
3526
+ You can access the controller params, headers, and helpers through the context with the `#context` method inside any auth middleware inherited from `Grape::Middleware::Auth::Base`.
3527
+
3528
+ ## Describing and Inspecting an API
3529
+
3530
+ Grape routes can be reflected at runtime. This can notably be useful for generating documentation.
3531
+
3532
+ Grape exposes arrays of API versions and compiled routes. Each route contains a `prefix`, `version`, `namespace`, `method` and `params`. You can add custom route settings to the route metadata with `route_setting`.
3533
+
3534
+ ```ruby
3535
+ class TwitterAPI < Grape::API
3536
+ version 'v1'
3537
+ desc 'Includes custom settings.'
3538
+ route_setting :custom, key: 'value'
3539
+ get do
3540
+
3541
+ end
3542
+ end
3543
+ ```
3544
+
3545
+ Examine the routes at runtime.
3546
+
3547
+ ```ruby
3548
+ TwitterAPI::versions # yields [ 'v1', 'v2' ]
3549
+ TwitterAPI::routes # yields an array of Grape::Route objects
3550
+ TwitterAPI::routes[0].version # => 'v1'
3551
+ TwitterAPI::routes[0].description # => 'Includes custom settings.'
3552
+ TwitterAPI::routes[0].settings[:custom] # => { key: 'value' }
3553
+ ```
3554
+
3555
+ Note that `Route#route_xyz` methods have been deprecated since 0.15.0 and removed since 2.0.1.
3556
+
3557
+ Please use `Route#xyz` instead.
3558
+
3559
+ Note that difference of `Route#options` and `Route#settings`.
3560
+
3561
+ The `options` can be referred from your route, it should be set by specifying key and value on verb methods such as `get`, `post` and `put`.
3562
+ The `settings` can also be referred from your route, but it should be set by specifying key and value on `route_setting`.
3563
+
3564
+ ## Current Route and Endpoint
3565
+
3566
+ It's possible to retrieve the information about the current route from within an API call with `route`.
3567
+
3568
+ ```ruby
3569
+ class MyAPI < Grape::API
3570
+ desc 'Returns a description of a parameter.'
3571
+ params do
3572
+ requires :id, type: Integer, desc: 'Identity.'
3573
+ end
3574
+ get 'params/:id' do
3575
+ route.params[params[:id]] # yields the parameter description
3576
+ end
3577
+ end
3578
+ ```
3579
+
3580
+ The current endpoint responding to the request is `self` within the API block or `env['api.endpoint']` elsewhere. The endpoint has some interesting properties, such as `source` which gives you access to the original code block of the API implementation. This can be particularly useful for building a logger middleware.
3581
+
3582
+ ```ruby
3583
+ class ApiLogger < Grape::Middleware::Base
3584
+ def before
3585
+ file = env['api.endpoint'].source.source_location[0]
3586
+ line = env['api.endpoint'].source.source_location[1]
3587
+ logger.debug "[api] #{file}:#{line}"
3588
+ end
3589
+ end
3590
+ ```
3591
+
3592
+ ## Before, After and Finally
3593
+
3594
+ Blocks can be executed before or after every API call, using `before`, `after`, `before_validation` and `after_validation`.
3595
+ If the API fails the `after` call will not be triggered, if you need code to execute for sure use the `finally`.
3596
+
3597
+ Before and after callbacks execute in the following order:
3598
+
3599
+ 1. `before`
3600
+ 2. `before_validation`
3601
+ 3. _validations_
3602
+ 4. `after_validation` (upon successful validation)
3603
+ 5. _the API call_ (upon successful validation)
3604
+ 6. `after` (upon successful validation and API call)
3605
+ 7. `finally` (always)
3606
+
3607
+ Steps 4, 5 and 6 only happen if validation succeeds.
3608
+
3609
+ If a request for a resource is made with an unsupported HTTP method (returning HTTP 405) only `before` callbacks will be executed. The remaining callbacks will be bypassed.
3610
+
3611
+ If a request for a resource is made that triggers the built-in `OPTIONS` handler, only `before` and `after` callbacks will be executed. The remaining callbacks will be bypassed.
3612
+
3613
+ For example, using a simple `before` block to set a header.
3614
+
3615
+ ```ruby
3616
+ before do
3617
+ header 'X-Robots-Tag', 'noindex'
3618
+ end
3619
+ ```
3620
+
3621
+ You can ensure a block of code runs after every request (including failures) with `finally`:
3622
+
3623
+ ```ruby
3624
+ finally do
3625
+ # this code will run after every request (successful or failed)
3626
+ end
3627
+ ```
3628
+
3629
+ **Namespaces**
3630
+
3631
+ Callbacks apply to each API call within and below the current namespace:
3632
+
3633
+ ```ruby
3634
+ class MyAPI < Grape::API
3635
+ get '/' do
3636
+ "root - #{@blah}"
3637
+ end
3638
+
3639
+ namespace :foo do
3640
+ before do
3641
+ @blah = 'blah'
3642
+ end
3643
+
3644
+ get '/' do
3645
+ "root - foo - #{@blah}"
3646
+ end
3647
+
3648
+ namespace :bar do
3649
+ get '/' do
3650
+ "root - foo - bar - #{@blah}"
3651
+ end
3652
+ end
3653
+ end
3654
+ end
3655
+ ```
3656
+
3657
+ The behavior is then:
3658
+
3659
+ ```bash
3660
+ GET / # 'root - '
3661
+ GET /foo # 'root - foo - blah'
3662
+ GET /foo/bar # 'root - foo - bar - blah'
3663
+ ```
3664
+
3665
+ Params on a `namespace` (or whichever alias you are using) will also be available when using `before_validation` or `after_validation`:
3666
+
3667
+ ```ruby
3668
+ class MyAPI < Grape::API
3669
+ params do
3670
+ requires :blah, type: Integer
3671
+ end
3672
+ resource ':blah' do
3673
+ after_validation do
3674
+ # if we reach this point validations will have passed
3675
+ @blah = declared(params, include_missing: false)[:blah]
3676
+ end
3677
+
3678
+ get '/' do
3679
+ @blah.class
3680
+ end
3681
+ end
3682
+ end
3683
+ ```
3684
+
3685
+ The behavior is then:
3686
+
3687
+ ```bash
3688
+ GET /123 # 'Integer'
3689
+ GET /foo # 400 error - 'blah is invalid'
3690
+ ```
3691
+
3692
+ **Versioning**
3693
+
3694
+ When a callback is defined within a version block, it's only called for the routes defined in that block.
3695
+
3696
+ ```ruby
3697
+ class Test < Grape::API
3698
+ resource :foo do
3699
+ version 'v1', :using => :path do
3700
+ before do
3701
+ @output ||= 'v1-'
3702
+ end
3703
+ get '/' do
3704
+ @output += 'hello'
3705
+ end
3706
+ end
3707
+
3708
+ version 'v2', :using => :path do
3709
+ before do
3710
+ @output ||= 'v2-'
3711
+ end
3712
+ get '/' do
3713
+ @output += 'hello'
3714
+ end
3715
+ end
3716
+ end
3717
+ end
3718
+ ```
3719
+
3720
+ The behavior is then:
3721
+
3722
+ ```bash
3723
+ GET /foo/v1 # 'v1-hello'
3724
+ GET /foo/v2 # 'v2-hello'
3725
+ ```
3726
+
3727
+ **Altering Responses**
3728
+
3729
+ Using `present` in any callback allows you to add data to a response:
3730
+
3731
+ ```ruby
3732
+ class MyAPI < Grape::API
3733
+ format :json
3734
+
3735
+ after_validation do
3736
+ present :name, params[:name] if params[:name]
3737
+ end
3738
+
3739
+ get '/greeting' do
3740
+ present :greeting, 'Hello!'
3741
+ end
3742
+ end
3743
+ ```
3744
+
3745
+ The behavior is then:
3746
+
3747
+ ```bash
3748
+ GET /greeting # {"greeting":"Hello!"}
3749
+ GET /greeting?name=Alan # {"name":"Alan","greeting":"Hello!"}
3750
+ ```
3751
+
3752
+ Instead of altering a response, you can also terminate and rewrite it from any callback using `error!`, including `after`. This will cause all subsequent steps in the process to not be called. **This includes the actual api call and any callbacks**
3753
+
3754
+ ## Anchoring
3755
+
3756
+ Grape by default anchors all request paths, which means that the request URL should match from start to end to match, otherwise a `404 Not Found` is returned. However, this is sometimes not what you want, because it is not always known upfront what can be expected from the call. This is because Rack-mount by default anchors requests to match from the start to the end, or not at all.
3757
+ Rails solves this problem by using a `anchor: false` option in your routes.
3758
+ In Grape this option can be used as well when a method is defined.
3759
+
3760
+ For instance when your API needs to get part of an URL, for instance:
3761
+
3762
+ ```ruby
3763
+ class TwitterAPI < Grape::API
3764
+ namespace :statuses do
3765
+ get '/(*:status)', anchor: false do
3766
+
3767
+ end
3768
+ end
3769
+ end
3770
+ ```
3771
+
3772
+ This will match all paths starting with '/statuses/'. There is one caveat though: the `params[:status]` parameter only holds the first part of the request url.
3773
+ Luckily this can be circumvented by using the described above syntax for path specification and using the `PATH_INFO` Rack environment variable, using `env['PATH_INFO']`. This will hold everything that comes after the '/statuses/' part.
3774
+
3775
+ ## Instance Variables
3776
+
3777
+ You can use instance variables to pass information across the various stages of a request. An instance variable set within a `before` validator is accessible within the endpoint's code and can also be utilized within the `rescue_from` handler.
3778
+
3779
+ ```ruby
3780
+ class TwitterAPI < Grape::API
3781
+ before do
3782
+ @var = 1
3783
+ end
3784
+
3785
+ get '/' do
3786
+ puts @var # => 1
3787
+ raise
3788
+ end
3789
+
3790
+ rescue_from :all do
3791
+ puts @var # => 1
3792
+ end
3793
+ end
3794
+ ```
3795
+
3796
+ The values of instance variables cannot be shared among various endpoints within the same API. This limitation arises due to Grape generating a new instance for each request made. Consequently, instance variables set within an endpoint during one request differ from those set during a subsequent request, as they exist within separate instances.
3797
+
3798
+ ```ruby
3799
+ class TwitterAPI < Grape::API
3800
+ get '/first' do
3801
+ @var = 1
3802
+ puts @var # => 1
3803
+ end
3804
+
3805
+ get '/second' do
3806
+ puts @var # => nil
3807
+ end
3808
+ end
3809
+ ```
3810
+
3811
+ ## Using Custom Middleware
3812
+
3813
+ ### Grape Middleware
3814
+
3815
+ You can make a custom middleware by using `Grape::Middleware::Base`.
3816
+ It's inherited from some grape official middlewares in fact.
3817
+
3818
+ For example, you can write a middleware to log application exception.
3819
+
3820
+ ```ruby
3821
+ class LoggingError < Grape::Middleware::Base
3822
+ def after
3823
+ return unless @app_response && @app_response[0] == 500
3824
+ env['rack.logger'].error("Raised error on #{env['PATH_INFO']}")
3825
+ end
3826
+ end
3827
+ ```
3828
+
3829
+ Your middleware can overwrite application response as follows, except error case.
3830
+
3831
+ ```ruby
3832
+ class Overwriter < Grape::Middleware::Base
3833
+ def after
3834
+ [200, { 'Content-Type' => 'text/plain' }, ['Overwritten.']]
3835
+ end
3836
+ end
3837
+ ```
3838
+
3839
+ You can add your custom middleware with `use`, that push the middleware onto the stack, and you can also control where the middleware is inserted using `insert`, `insert_before` and `insert_after`.
3840
+
3841
+ ```ruby
3842
+ class CustomOverwriter < Grape::Middleware::Base
3843
+ def after
3844
+ [200, { 'Content-Type' => 'text/plain' }, [@options[:message]]]
3845
+ end
3846
+ end
3847
+
3848
+
3849
+ class API < Grape::API
3850
+ use Overwriter
3851
+ insert_before Overwriter, CustomOverwriter, message: 'Overwritten again.'
3852
+ insert 0, CustomOverwriter, message: 'Overwrites all other middleware.'
3853
+
3854
+ get '/' do
3855
+ end
3856
+ end
3857
+ ```
3858
+
3859
+ You can access the controller params, headers, and helpers through the context with the `#context` method inside any middleware inherited from `Grape::Middleware::Base`.
3860
+
3861
+ ### Rails Middleware
3862
+
3863
+ Note that when you're using Grape mounted on Rails you don't have to use Rails middleware because it's already included into your middleware stack.
3864
+ You only have to implement the helpers to access the specific `env` variable.
3865
+
3866
+ If you are using a custom application that is inherited from `Rails::Application` and need to insert a new middleware among the ones initiated via Rails, you will need to register it manually in your custom application class.
3867
+
3868
+ ```ruby
3869
+ class Company::Application < Rails::Application
3870
+ config.middleware.insert_before(Rack::Attack, Middleware::ApiLogger)
3871
+ end
3872
+ ```
3873
+
3874
+ ### Remote IP
3875
+
3876
+ By default you can access remote IP with `request.ip`. This is the remote IP address implemented by Rack. Sometimes it is desirable to get the remote IP [Rails-style](http://stackoverflow.com/questions/10997005/whats-the-difference-between-request-remote-ip-and-request-ip-in-rails) with `ActionDispatch::RemoteIp`.
3877
+
3878
+ Add `gem 'actionpack'` to your Gemfile and `require 'action_dispatch/middleware/remote_ip.rb'`. Use the middleware in your API and expose a `client_ip` helper. See [this documentation](http://api.rubyonrails.org/classes/ActionDispatch/RemoteIp.html) for additional options.
3879
+
3880
+ ```ruby
3881
+ class API < Grape::API
3882
+ use ActionDispatch::RemoteIp
3883
+
3884
+ helpers do
3885
+ def client_ip
3886
+ env['action_dispatch.remote_ip'].to_s
3887
+ end
3888
+ end
3889
+
3890
+ get :remote_ip do
3891
+ { ip: client_ip }
3892
+ end
3893
+ end
3894
+ ```
3895
+
3896
+ ## Writing Tests
3897
+
3898
+ ### Writing Tests with Rack
3899
+
3900
+ Use `rack-test` and define your API as `app`.
3901
+
3902
+ #### RSpec
3903
+
3904
+ You can test a Grape API with RSpec by making HTTP requests and examining the response.
3905
+
3906
+ ```ruby
3907
+
3908
+
3909
+ describe Twitter::API do
3910
+ include Rack::Test::Methods
3911
+
3912
+ def app
3913
+ Twitter::API
3914
+ end
3915
+
3916
+ context 'GET /api/statuses/public_timeline' do
3917
+ it 'returns an empty array of statuses' do
3918
+ get '/api/statuses/public_timeline'
3919
+ expect(last_response.status).to eq(200)
3920
+ expect(JSON.parse(last_response.body)).to eq []
3921
+ end
3922
+ end
3923
+ context 'GET /api/statuses/:id' do
3924
+ it 'returns a status by id' do
3925
+ status = Status.create!
3926
+ get "/api/statuses/#{status.id}"
3927
+ expect(last_response.body).to eq status.to_json
3928
+ end
3929
+ end
3930
+ end
3931
+ ```
3932
+
3933
+ There's no standard way of sending arrays of objects via an HTTP GET, so POST JSON data and specify the correct content-type.
3934
+
3935
+ ```ruby
3936
+ describe Twitter::API do
3937
+ context 'POST /api/statuses' do
3938
+ it 'creates many statuses' do
3939
+ statuses = [{ text: '...' }, { text: '...'}]
3940
+ post '/api/statuses', statuses.to_json, 'CONTENT_TYPE' => 'application/json'
3941
+ expect(last_response.body).to eq 201
3942
+ end
3943
+ end
3944
+ end
3945
+ ```
3946
+
3947
+ #### Airborne
3948
+
3949
+ You can test with other RSpec-based frameworks, including [Airborne](https://github.com/brooklynDev/airborne), which uses `rack-test` to make requests.
3950
+
3951
+ ```ruby
3952
+ require 'airborne'
3953
+
3954
+ Airborne.configure do |config|
3955
+ config.rack_app = Twitter::API
3956
+ end
3957
+
3958
+ describe Twitter::API do
3959
+ context 'GET /api/statuses/:id' do
3960
+ it 'returns a status by id' do
3961
+ status = Status.create!
3962
+ get "/api/statuses/#{status.id}"
3963
+ expect_json(status.as_json)
3964
+ end
3965
+ end
3966
+ end
3967
+ ```
3968
+
3969
+ #### MiniTest
3970
+
3971
+ ```ruby
3972
+ require 'test_helper'
3973
+
3974
+ class Twitter::APITest < MiniTest::Test
3975
+ include Rack::Test::Methods
3976
+
3977
+ def app
3978
+ Twitter::API
3979
+ end
3980
+
3981
+ def test_get_api_statuses_public_timeline_returns_an_empty_array_of_statuses
3982
+ get '/api/statuses/public_timeline'
3983
+ assert last_response.ok?
3984
+ assert_equal [], JSON.parse(last_response.body)
3985
+ end
3986
+
3987
+ def test_get_api_statuses_id_returns_a_status_by_id
3988
+ status = Status.create!
3989
+ get "/api/statuses/#{status.id}"
3990
+ assert_equal status.to_json, last_response.body
3991
+ end
3992
+ end
3993
+ ```
3994
+
3995
+ ### Writing Tests with Rails
3996
+
3997
+ #### RSpec
3998
+
3999
+ ```ruby
4000
+ describe Twitter::API do
4001
+ context 'GET /api/statuses/public_timeline' do
4002
+ it 'returns an empty array of statuses' do
4003
+ get '/api/statuses/public_timeline'
4004
+ expect(response.status).to eq(200)
4005
+ expect(JSON.parse(response.body)).to eq []
4006
+ end
4007
+ end
4008
+ context 'GET /api/statuses/:id' do
4009
+ it 'returns a status by id' do
4010
+ status = Status.create!
4011
+ get "/api/statuses/#{status.id}"
4012
+ expect(response.body).to eq status.to_json
4013
+ end
4014
+ end
4015
+ end
4016
+ ```
4017
+
4018
+ In Rails, HTTP request tests would go into the `spec/requests` group. You may want your API code to go into `app/api` - you can match that layout under `spec` by adding the following in `spec/rails_helper.rb`.
4019
+
4020
+ ```ruby
4021
+ RSpec.configure do |config|
4022
+ config.include RSpec::Rails::RequestExampleGroup, type: :request, file_path: /spec\/api/
4023
+ end
4024
+ ```
4025
+
4026
+ #### MiniTest
4027
+
4028
+ ```ruby
4029
+ class Twitter::APITest < ActiveSupport::TestCase
4030
+ include Rack::Test::Methods
4031
+
4032
+ def app
4033
+ Rails.application
4034
+ end
4035
+
4036
+ test 'GET /api/statuses/public_timeline returns an empty array of statuses' do
4037
+ get '/api/statuses/public_timeline'
4038
+ assert last_response.ok?
4039
+ assert_equal [], JSON.parse(last_response.body)
4040
+ end
4041
+
4042
+ test 'GET /api/statuses/:id returns a status by id' do
4043
+ status = Status.create!
4044
+ get "/api/statuses/#{status.id}"
4045
+ assert_equal status.to_json, last_response.body
4046
+ end
4047
+ end
4048
+ ```
4049
+
4050
+ ### Stubbing Helpers
4051
+
4052
+ Because helpers are mixed in based on the context when an endpoint is defined, it can be difficult to stub or mock them for testing. The `Grape::Endpoint.before_each` method can help by allowing you to define behavior on the endpoint that will run before every request.
4053
+
4054
+ ```ruby
4055
+ describe 'an endpoint that needs helpers stubbed' do
4056
+ before do
4057
+ Grape::Endpoint.before_each do |endpoint|
4058
+ allow(endpoint).to receive(:helper_name).and_return('desired_value')
4059
+ end
4060
+ end
4061
+
4062
+ after do
4063
+ Grape::Endpoint.before_each nil
4064
+ end
4065
+
4066
+ it 'stubs the helper' do
4067
+
4068
+ end
4069
+ end
4070
+ ```
4071
+
4072
+ ## Reloading API Changes in Development
4073
+
4074
+ ### Reloading in Rack Applications
4075
+
4076
+ Use [grape-reload](https://github.com/AlexYankee/grape-reload).
4077
+
4078
+ ### Reloading in Rails Applications
4079
+
4080
+ Add API paths to `config/application.rb`.
4081
+
4082
+ ```ruby
4083
+ # Auto-load API and its subdirectories
4084
+ config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb')
4085
+ config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]
4086
+ ```
4087
+
4088
+ Create `config/initializers/reload_api.rb`.
4089
+
4090
+ ```ruby
4091
+ if Rails.env.development?
4092
+ ActiveSupport::Dependencies.explicitly_unloadable_constants << 'Twitter::API'
4093
+
4094
+ api_files = Dir[Rails.root.join('app', 'api', '**', '*.rb')]
4095
+ api_reloader = ActiveSupport::FileUpdateChecker.new(api_files) do
4096
+ Rails.application.reload_routes!
4097
+ end
4098
+ ActionDispatch::Callbacks.to_prepare do
4099
+ api_reloader.execute_if_updated
4100
+ end
4101
+ end
4102
+ ```
4103
+
4104
+ For Rails >= 5.1.4, change this:
4105
+
4106
+ ```ruby
4107
+ ActionDispatch::Callbacks.to_prepare do
4108
+ api_reloader.execute_if_updated
4109
+ end
4110
+ ```
4111
+
4112
+ to this:
4113
+
4114
+ ```ruby
4115
+ ActiveSupport::Reloader.to_prepare do
4116
+ api_reloader.execute_if_updated
4117
+ end
4118
+ ```
4119
+
4120
+ See [StackOverflow #3282655](http://stackoverflow.com/questions/3282655/ruby-on-rails-3-reload-lib-directory-for-each-request/4368838#4368838) for more information.
4121
+
4122
+ ## Performance Monitoring
4123
+
4124
+ ### Active Support Instrumentation
4125
+
4126
+ Grape has built-in support for [ActiveSupport::Notifications](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html) which provides simple hook points to instrument key parts of your application.
4127
+
4128
+ The following are currently supported:
4129
+
4130
+ #### endpoint_run.grape
4131
+
4132
+ The main execution of an endpoint, includes filters and rendering.
4133
+
4134
+ * *endpoint* - The endpoint instance
4135
+
4136
+ #### endpoint_render.grape
4137
+
4138
+ The execution of the main content block of the endpoint.
4139
+
4140
+ * *endpoint* - The endpoint instance
4141
+
4142
+ #### endpoint_run_filters.grape
4143
+
4144
+ * *endpoint* - The endpoint instance
4145
+ * *filters* - The filters being executed
4146
+ * *type* - The type of filters (before, before_validation, after_validation, after)
4147
+
4148
+ #### endpoint_run_validators.grape
4149
+
4150
+ The execution of validators.
4151
+
4152
+ * *endpoint* - The endpoint instance
4153
+ * *validators* - The validators being executed
4154
+ * *request* - The request being validated
4155
+
4156
+ #### format_response.grape
4157
+
4158
+ Serialization or template rendering.
4159
+
4160
+ * *env* - The request environment
4161
+ * *formatter* - The formatter object (e.g., `Grape::Formatter::Json`)
4162
+
4163
+ See the [ActiveSupport::Notifications documentation](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html) for information on how to subscribe to these events.
4164
+
4165
+ ### Monitoring Products
4166
+
4167
+ Grape integrates with following third-party tools:
4168
+
4169
+ * **New Relic** - [built-in support](https://docs.newrelic.com/docs/agents/ruby-agent/frameworks/grape-instrumentation) from v3.10.0 of the official [newrelic_rpm](https://github.com/newrelic/rpm) gem, also [newrelic-grape](https://github.com/xinminlabs/newrelic-grape) gem
4170
+ * **Librato Metrics** - [grape-librato](https://github.com/seanmoon/grape-librato) gem
4171
+ * **[Skylight](https://www.skylight.io/)** - [skylight](https://github.com/skylightio/skylight-ruby) gem, [documentation](https://docs.skylight.io/grape/)
4172
+ * **[AppSignal](https://www.appsignal.com)** - [appsignal-ruby](https://github.com/appsignal/appsignal-ruby) gem, [documentation](http://docs.appsignal.com/getting-started/supported-frameworks.html#grape)
4173
+ * **[ElasticAPM](https://www.elastic.co/products/apm)** - [elastic-apm](https://github.com/elastic/apm-agent-ruby) gem, [documentation](https://www.elastic.co/guide/en/apm/agent/ruby/3.x/getting-started-rack.html#getting-started-grape)
4174
+ * **[Datadog APM](https://docs.datadoghq.com/tracing/)** - [ddtrace](https://github.com/datadog/dd-trace-rb) gem, [documentation](https://docs.datadoghq.com/tracing/setup_overview/setup/ruby/#grape)
4175
+
4176
+ ## Contributing to Grape
4177
+
4178
+ Grape is work of hundreds of contributors. You're encouraged to submit pull requests, propose features and discuss issues.
4179
+
4180
+ See [CONTRIBUTING](CONTRIBUTING.md).
4181
+
4182
+ ## Security
4183
+
4184
+ See [SECURITY](SECURITY.md) for details.
4185
+
4186
+ ## License
4187
+
4188
+ MIT License. See [LICENSE](LICENSE) for details.
4189
+
4190
+ ## Copyright
4191
+
4192
+ Copyright (c) 2010-2020 Michael Bleigh, Intridea Inc. and Contributors.