Requests
Scramble documents request part of endpoints by automatically extracting route parameters and generating parameter types and descriptions based on route model binding. Query parameters or request body parameters are documented based on validation rules.
Path params
Scramble identifies and documents all route path parameters automatically. For example:
1// routes/api.php2Route::put('/todo-items/{id}', [TodoItemsController::class, 'update']);3
4// app/Http/Api/Controllers/TodoItemsController.php5class TodoItemsController6{7 public function update(Request $request, int $id) {/* ... */}8}In this example, the {id} path parameter is documented without any additional setup. Scramble generates default documentation, including the parameter type.
If the parameter is implicitly (or explicitly) bound to a model, Scramble determines the parameter type based on the model’s route key.
int key: The parameter type will be integer.
UUID key: The parameter type will be string, with uuid format.
For example:
1// routes/api.php2Route::put('/todo-items/{todoItem}', [TodoItemsController::class, 'update']);3
4// app/Http/Api/Controllers/TodoItemsController.php5class TodoItemsController6{7 public function update(Request $request, TodoItem $todoItem) {/* ... */}8}In this case assuming the TodoItem model uses an integer key, Scramble will document the {todoItem} parameter as an integer with the default description "The todo item ID.".
Scramble supports custom route keys. If you override the getRouteKeyName method in your model, Scramble will document the parameter type based on the type of the property specified in that method.
You can enhance or override the default documentation by adding PHPDoc to the corresponding controller method parameters. For example:
1use App\Models\TodoItem;2
3class TodoItemsController4{5 /**6 * @param TodoItem $todoItem The todo item being updated.7 */8 public function update(Request $request, TodoItem $todoItem) {/*...*/}9}Scramble will override the default description of the parameter with the description from PHPDoc (“The todo item being updated”).
If you want to have the entire control on path parameter (to add examples, override the type, etc.), you can use PathParameter attribute:
1use App\Models\TodoItem;2use Dedoc\Scramble\Attributes\PathParameter;3
4class TodoItemsController5{6 #[PathParameter('todoItem', description: 'Todo item being updated', type: 'string', format: 'uuid', example: '550e8400-e29b-41d4-a716-446655440000')]7 public function update(Request $request, TodoItem $todoItem) {/*...*/}8}Explicit binding and parameter matching caveats
Laravel allows you to explicitly define how route parameters are resolved to models using the Route::bind method. For example:
1Route::bind('any_user', function (string $value) {2 return User::where('name', $value)->firstOrFail();3});Here’s a route and controller that utilize this binding:
1// routes/api.php2Route::get('/users/{any_user}', UserController::class);3
4// app/Http/Controllers/UserController.php5class UserController6{7 /**8 * @param User $user The resolved user instance.9 */10 public function __invoke(Request $request, User $user)11 {12 // $user is the resolved model instance13 }14}When the {any_user} parameter is used in the route, Laravel resolves it to a User instance using the custom binding. This instance is then passed to the controller method as the $user parameter. Laravel recognizes the parameter type (User) and ensures it is provided from the route parameters instead of resolving it from the service container.
In this scenario, Scramble cannot automatically associate the {any_user} route parameter with the $user argument in the controller. As a result, the $user parameter’s PHPDoc documentation will not appear correctly in the generated API documentation.
To address this issue, you need to explicitly add a return type to the closure provided to the Route::bind method:
1Route::bind('any_user', function (string $value): User {2 return User::where('name', $value)->firstOrFail();3});Adding the return type (User) helps Scramble correctly identify that {any_user} resolves to a User instance. This ensures the parameter is correctly mapped in the API documentation, and the $user argument in your controller is documented as expected.
Body
Request body documentation is generated based on using request validation rules and attributes retrieval (methods calls) on request object.
When request HTTP method is GET, DELETE, or HEAD parameters are documented as a part of a query string. For other HTTP methods, parameters are documented as requests body.
Validation rules
Currently, there are 3 ways of validating requests that are understood by Scramble:
- Call to
validateon$requestor$thisin controller’s method - Call to
Validatorfacade’smakemethod with method call on request ($request->?()) as a first argument rulesmethod on customFormRequestclass
1use App\Models\TodoItem;2
3class TodoItemsController4{5 public function update(Request $request, TodoItem $item)6 {7 $request->validate([8 'body' => ['required', 'string'],9 'is_complete' => 'bool',10 ]);11 }12}Based on these validation rules Scramble knows that there are 2 request body params: body and is_complete. Same applies to Validator::make call.
1use App\Models\TodoItem;2use Illuminate\Support\Facades\Validator;3
4class TodoItemsController5{6 public function update(Request $request, TodoItem $item)7 {8 Validator::make($request->all(), [9 'body' => ['required', 'string'],10 'is_complete' => 'bool',11 ]);12 // ...13 }14}The same applies to the rules method in custom FormRequest.
When you need to mark a parameter as a part of query string for non-GET requests, you can use @query PHPDoc annotation:
1$request->validate([2 /** @query */3 'per_page' => ['required', 'integer'],4]);Documenting request params manually
You can add docs to your request params by adding PHPDoc block near a validation rules of the param:
1use App\Models\Location;2
3class LocationsController4{5 public function update(Request $request, Location $location)6 {7 $request->validate([8 /**9 * The location coordinates.10 * @var array{lat: float, long: float}11 * @example {"lat": 50.450001, "long": 30.523333}12 */13 'coordinates' => 'array',14 ]);15 }16}@example should be either a string, or valid JSON.
You can use @var to re-define or clarify type inferred from validation rules. Manually defined type will always take precedence over the automatically inferred type.
A simple PHP comment before a param will also be used as a request body parameter description:
1use App\Models\TodoItem;2
3class TodoItemsController4{5 public function update(Request $request, TodoItem $item)6 {7 $request->validate([8 // Whether the task is complete.9 'is_complete' => 'bool',10 ]);11 }12}Ignoring params
If you need to avoid a parameter being added to documentation, you can mark it with @ignoreParam PHPDoc annotation. In the following example id parameter won’t be documented.
1use App\Models\TodoItem;2
3class TodoItemsController4{5 public function index(Request $request)6 {7 $request->validate([8 /** @ignoreParam */9 'id' => 'integer',10 ]);11 // ...12 }13}Rules evaluation caveats
It is important to keep in mind that rules are evaluated to be analyzed: rules method is called when there is a custom request class and the array with rules passed to the validate is evaluated as well.
This adds not obvious benefits to the resulting documentation when Rule::in validation rule is being represented as enum with all possible values in the docs.
But also it requires a developer to write rules in certain way when using validation via validate method call in controller. Only these expressions can be evaluated correctly:
- Using parameters passed to the route action
- Static calls to classes
- Using global functions (
app()) - Using locally defined variables
Declaring local variable in method before calling validate and using it there will cause an error.
Supported rules
requiredstringbool,booleannumberint,integerarrayin,Rule::innullableemailuuidexists,Rule::exists– When using theexistsrule, Scramble queries the database to determine the field type, using the default connection or the model’s connection if a model class is passed. If the table does not exist (for example, migrations were not run), Scramble guesses the type and assumesintfor fields namedidor ending with_id. Scramble cannot inferuuidcolumns automatically. You must explicitly add theuuidrule, because UUIDs are usually stored asvarcharorchar, making them indistinguishable by column type alone.minmaxEnumconfirmedfileimagedatedate_formatsizebetweenRule::whenRule::unlessregex– Scramble uses the pattern JSON Schema property to document regular expressions. JSON Schema uses the ECMA-262 flavor, while PHP uses PCRE, so not all PCRE expressions can be converted. Scramble cannot convert regex with lookbehind ((?<=,(?<!), conditional ((?(), backtracking verbs ((*), or inline PCRE flags. In such cases, it skips theregexvalidation rule.
You can add custom rules documentation support via rule transformers API.
Method calls on a request object
When you manually retrieve attributes from a request object, Scramble will automatically document them as part of the request.
For example, if we want to get a paginated list of to-do items, a user can pass an integer per_page as a request parameter:
1use App\Models\TodoItem;2use Illuminate\Support\Facades\Validator;3use Illuminate\Http\Request;4
5class TodoItemsController6{7 public function index(Request $request)8 {9 $items = TodoItem::query()10 ->paginate($request->integer('per_page', 15));11
12 // ...13 }14}Scramble will add per_page to the API documentation as integer type and will document that 15 is a default value.
These Request methods are supported:
integerfloatbooleanenumquery- Scramble will mark a parameter as a part of query string for non-GETrequestsstring,str,get,input,post
You can add description, example, default value for parameters manually when needed. Using @query annotation, you can mark the parameter as a part of query string for non-GET requests.
1/**2 * Amount of items per page.3 * @example 504 * @default 155 */6$perPage = $request->integer('per_page') ?: 15;When you want to avoid adding attributes to API documentation, use @ignoreParam PHPDoc attribute. In this case foo attribute will not be documented.
1/** @ignoreParam */2$data = $request->get('foo');Adding title and description
Scramble can get endpoint docs from PHPDoc comment of the route’s method.
summary is the first row of the doc. description is the other text in doc. When there is only one line of text in PHPDoc it is treated as summary, as you can expect.
1/**2 * This is summary.3 *4 * This is a description. In can be as large as needed and contain `markdown`.5 */Manually documenting parameters
Despite Scramble documenting parameters automatically, you may still want to manually document the resulting parameters. For example when you want to add some description, or provide default values or some examples.
You can do this using the *Parameter attributes: #[QueryParameter], #[HeaderParameter], #[CookieParameter], #[PathParameter], #[BodyParameter].
Each of these attributes are for documenting parameters that appear in the corresponding part of the request.
These attributes are applied to the route’s controller method.
For example, if you have the API like /items?per_page=20, here is how you can document per_page parameter: add the description, document its type, default value, and provide an example:
1use Dedoc\Scramble\Attributes\QueryParameter;2
3class TodoItemsController4{5 #[QueryParameter('per_page', description: 'Number of items per page.', type: 'int', default: 10, example: 20)]6 public function index()7 {8 // ...9 }10}By default, Scramble merges the information it inferred with the manual attribute documentation. Manual documentation overwrites what is inferred automatically:
1use Dedoc\Scramble\Attributes\QueryParameter;2
3class TodoItemsController4{5 #[QueryParameter('per_page', default: 10, example: 20)]6 public function index(Request $request)7 {8 $request->validate(['per_page' => 'integer']);9 }10}In this example, Scramble will document per_page as integer (this comes from validation rules), and add the default value and example to it (from attribute).
You can opt out of this behavior by providing the infer argument as false:
1use Dedoc\Scramble\Attributes\QueryParameter;2
3class TodoItemsController4{5 #[QueryParameter('per_page', infer: false)]6 public function index(Request $request)7 {8 $request->validate(['per_page' => 'integer']);9 }10}In this case, type information from validation rules will be ignored.
You can document the format of the parameter and set if it required using format and required parameters.
1use Dedoc\Scramble\Attributes\QueryParameter;2
3class TodoItemsController4{5 #[QueryParameter('uuid', type: 'string', format: 'uuid', required: true)]6 public function index(Request $request)7 {8 //9 }10}Grouping endpoints
By default, all the endpoints are grouped by the name of a controller the routes are defined in. Under the hood, OpenAPI operation’s tags are used for groups.
You can use Group attribute to name the endpoint group yourself:
1use Dedoc\Scramble\Attributes\Group;2
3#[Group('Todo Items API')]4class TodoItemsController5{6 // ...7}In case you want to add multiple tags for controller’s endpoints in your OpenAPI specification, you can use @tags in class’ PHPDoc by simply providing the list via comma.
Please note that the Stoplight Elements (the UI Scramble uses) doesn’t support nesting. It uses the first tag only. Other tags will still be there in OpenAPI specification but won’t be shown in the UI.
Sorting endpoints groups
By default, the groups are sorted alphabetically by their name. In case you need to redefine the order, you can use weight parameter on Group attribute.
For example, Todos group will be shown before TodoItems group.
1use Dedoc\Scramble\Attributes\Group;2
3#[Group(weight: 1)]4class TodoItemsController5{6 // ...7}8
9#[Group(weight: 0)]10class TodosController11{12 // ...13}Operation ID
Scramble generates unique operation ID for your endpoints. It is based on the route name. If route name is not unique, Scramble will use controller and method name and will create a unique ID based on that.
You always can override operation ID by adding @operationId to the route’s method PHPDoc.
1class DownloadMediaController2{3 /**4 * @operationId getMediaItem5 */6 public function show(Media $mediaItem)7 {8 return $mediaItem;9 }10}Request media type
By default, Scramble specifies application/json as request media type (Content-Type header). When some field in validation rules has file rule, Scramble will use multipart/form-data.
You always can override request media type by adding @requestMediaType to the route’s method PHPDoc.
1class UploadMediaController2{3 /**4 * @requestMediaType multipart/form-data5 */6 public function show(Request $request)7 {8 // ...9 }10}Preferring PATCH method
By default, Scramble uses the first HTTP method found in the route definition as the method of the endpoint. So, for example, for API resource’s update endpoint, it will be PUT (as the method of update is PUT|PATCH).
If you want to always prefer PATCH method over PUT method, you can configure Scramble to always prefer PATCH in such cases:
1use Dedoc\Scramble\Scramble;2
3public function boot(): void4{5 Scramble::configure()6 ->preferPatchMethod();7}In case you want to prefer PATCH method only for certain endpoints, you can use method parameter on #[Endpoint] attribute:
1use Dedoc\Scramble\Attributes\Endpoint;2
3class TodoItemsController4{5 #[Endpoint(method: 'PATCH')]6 public function update(Request $request, TodoItem $item)7 {8 // ...9 }10}Learn more about #[Endpoint] attribute in the developers reference.
Customize documented operation methods
For advanced use cases, you can provide a custom resolver closure that decides which route methods are documented. This gives you full control over how Scramble determines the HTTP method for each endpoint.
For example, this is how you enable Scramble to document all route methods:
1use Dedoc\Scramble\Scramble;2use Illuminate\Routing\Route;3
4public function boot(): void5{6 Scramble::configure()7 ->resolveOperationMethodsUsing(function (Route $route) {8 return $route->methods();9 });10}The resolver receives the route instance and should return an array of methods (or a single method string) that will be used in the API documentation.