Scramble 0.11.0 is here! Laravel Data support, schema enforcement and more
Learn more

Responses

Are you a visual learner?
Get most out of Scramble with in-depth screencasts.

Scramble generates documentation for endpoint responses by analyzing the source code of the controller’s method. It examines the inferred return type, calls to various methods (e.g., validate or authorize), and other statements to identify the different types of responses that an endpoint may produce.

Currently, these types will be automatically documented as responses:

  • API resources (instances of classes extending JsonResource)
  • Collections of API resources (both anonymous collections and dedicated classes)
  • Some response() (ResponseFactory) support: response()->make(...), response()->json(...), response()->noContent()
  • JsonResponse and Response instances
  • Models
  • Simple types: arrays, strings, numbers, etc.

Paginated responses are also supported, but they need to be documented in PHPDoc for now.

On type inference

Scramble uses type inference based on source code analysis to automatically determine the types of variables and expressions. This means that Scramble can understand what type of data (e.g., strings, numbers, objects) each part of the code is working with, even if the code does not explicitly declare it.

With this technique, Scramble can figure out the likely data types that a controller’s method will return without needing extra explanations. It looks at how the method processes data, the methods it uses, and other hints to infer the possible return types.

This process allows Scramble to generate comprehensive and accurate documentation for the API. It also reduces the need to manually document the responses, making the documentation process more efficient and less error-prone.

Types priority

Scramble extracts controller’s method return type based on the following priority:

  1. PHPDoc comment (@response tag) - gives ability to document arbitrary response types
  2. Typehint - if the method has a typehint and doesn’t have a PHPDoc comment, the typehint will be used
  3. Code return statements - if the method doesn’t have a PHPDoc @response tag, and inferred return type is more specific than typehint, the inferred type will be used
use App\Models\TodoItem;
class TodoItemsController
{
/**
* @response TodoItemResource // 1 - PHPDoc
*/
public function update(Request $request, TodoItem $item): TodoItemResource // 2 - typehint
{
return new TodoItemResource($item); // 3 - code inference
}
}

This is implemented in this way so if needed, you can override the inferred type with a typehint or PHPDoc comment.

Manual hinting in PHPDoc

Also, the response can be documented in PHPDoc using @response tag. This is useful when you notice that the inferred type is not correct or you want to document a custom response type.

Read more about the proper PHPDoc type syntax in PhpStan docs.

use App\Models\TodoItem;
class TodoItemsController
{
/**
* List available todo items.
*
* @response array{data: TodoItemResource[], meta: array{permissions: bool}}
*/
public function index(Request $request)
{
return app(TodoService::class)->listAll();
}
}

API resources

Model resolution

All resource property types that are accessed on $this or $this->resource will be resolved from the corresponding model attributes.

By default, Scramble tries to find a model in App\Models namespace, based on resource name. Before the lookup resource name is converted to singular form (TodoItemsResourceTodoItem).

You can provide the name of the model manually in case you have your custom models namespaces by adding PHPDoc to the resource class. You can either document $resource property type, or define the model as a @mixin:

use App\Domains\Todo\Models\TodoItem;
/**
* @property TodoItem $resource
*/
class TodoItemResource extends JsonResource
{
public function toArray
{
return [/*...*/];
}
}

And @mixin example:

use App\Domains\Todo\Models\TodoItem;
/**
* @mixin TodoItem
*/
class TodoItemResource extends JsonResource
{
public function toArray
{
return [/*...*/];
}
}

If model for resource is not found, all the fields will have string type in docs.

Automatically supported fields types

  • Model attribute type ($this->id, $this->resource->id)
return [
'id' => $this->id,
'content' => $this->resource->content,
];
  • Nested API resources
return [
'list' => new ListResource($this->list),
];
  • Conditional merging of nested API resources
return [
'list' => new ListResource($this->whenLoaded('list')),
];
  • Arrays
return [
'list' => [
'name' => $this->list_name,
],
];
  • Conditional merging of the arrays in the response:
    • merge
    • mergeWhen
    • when

All the conditional fields will be marked as optional in resulting docs.

Also, API resources are stored in OpenAPI docs as reference and usage of the resources across other resources will be rendered as using schema reference in docs.

Manually describing the fields

For the cases when automatic field type resolution is not working, or you need to add a description to the field, you can add PHPDoc comment:

return [
'id' => $this->id,
/**
* The content of todo item, truncated.
* @var string
*/
'content' => $this->someMethodCall(),
];

You can use other resource classes or more complex types to document more complex structures:

return [
'id' => $this->id,
/** @var array<string, ThreadResource> */
'threads' => $this->threads->keyBy('name')->mapInto(ThreadResource::class),
];

This is the format of type annotations used by PhpStan. You can read more about it here: https://phpstan.org/writing-php-code/phpdoc-types.

API resources collection

This sort of response can be automatically analyzed from code return statement.

If you need some code manipulation on the object and it cannot be automatically inferred, you can add the type manually.

To manually add this response type you add this to your PHPDoc like this:

use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use App\Models\TodoItem;
class TodoItemsController
{
/**
* List available todo items.
*
* @response AnonymousResourceCollection<TodoItemResource>
*/
public function index(Request $request)
{
return TodoItemResource::collection(TodoItem::all());
}
}

Additional collection’s data

Scramble will automatically document additional data that is added to the collection using additional method.

use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use App\Models\TodoItem;
class TodoItemsController
{
public function index(Request $request)
{
return TodoItemResource::collection(TodoItem::all())
->additional(['permissions' => true]);
}
}

Models

When you don’t have a resource and simply return a model from controller’s method, Scramble will be able to document that as well.

When documenting models, Scramble will document what the model’s toArray returns. So if toArray is overridden, it should return an array to be properly analyzed.

public function show(Request $request, User $user)
{
return $user;
}

Please note that if you don’t type annotate a method’s return type, the model type should be specified in arguments list. Otherwise, Scramble won’t be able to infer the returned variable type and document it properly. Also, now only relations that are always present on a model ($with property) are documented.

Paginated responses

Paginated response cannot be inferred from code automatically for now, so you need to typehint it manually in PHPDoc.

For example, here is a paginated anonymous resource collection:

use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use App\Models\TodoItem;
use Illuminate\Pagination\LengthAwarePaginator;
class TodoItemsController
{
/**
* List available todo items.
*
* @response AnonymousResourceCollection<LengthAwarePaginator<TodoItemResource>>
*/
public function index(Request $request)
{
return TodoItemResource::collection(TodoItem::paginate());
}
}

In case the paginated response is not wrapped as a collection, you can annotate it like so:

use App\Models\TodoItem;
use Illuminate\Pagination\LengthAwarePaginator;
class TodoItemsController
{
/**
* @response LengthAwarePaginator<TodoItem>
*/
public function index(Request $request)
{
return TodoItem::paginate();
}
}

This will add all the meta and other information to the resulting docs.

Scramble supports all available paginator classes:

  • Illuminate\Pagination\LengthAwarePaginator (returned when you call paginate method on a query builder),
  • Illuminate\Pagination\Paginator (returned when you call simplePaginate method on a query builder),
  • Illuminate\Pagination\CursorPaginator (returned when you call cursorPaginate method on a query builder).

Error responses

By analyzing the codebase Scramble can not only understand OK responses, but error responses as well. Here are the cases which Scramble understand and can document.

Call to validate and authorize in controller

Calls to validate and authorize will be documented as corresponding error responses in resulting docs: responses with 422 and 403 statuses respectively.

Model binding in route

When model binding is used, Scramble adds possible 404 error response to the documentation. This is the case when a model is not found.

Abort helpers

When using abort helpers (abort, abort_if, abort_unless), Scramble will document the resulting response.

Currently only numeric code is supported. Message content (message) will be documented as an example in resulting docs.

For example, this line of code:

abort(400, 'This case is not supported');

Corresponds to the error response in documentation with code 400 and message property with an example (This case is not supported).

Exceptions

Under the hood Scramble understands that abort* helpers, *::validate, *::authorize methods throw exceptions. Then, these exceptions are documented as error responses.

This means that if you add @throws annotation with a supported exception to your route’s controller method, Scramble will pick it up and document it as the error response.

// app/Http/Controllers/API/UsersController.php
class UsersController
{
/** @throws \Illuminate\Auth\Access\AuthorizationException */
public function store(Request $request, UsersService $usersService)
{
$usersService->createUser($request->email, $request->password);
}
}

Here are the exceptions that will be automatically documented by Scramble as a response. All the exceptions that extend the following exceptions classes will also be documented as the same responses.

ExceptionStatus code
Illuminate\Auth\AuthenticationException401
Illuminate\Auth\Access\AuthorizationException403
Symfony\Component\HttpKernel\Exception\HttpException$statusCode property value
Illuminate\Database\RecordsNotFoundException404
Symfony\Component\HttpKernel\Exception\NotFoundHttpException404
Illuminate\Validation\ValidationException422

Any instance of Symfony\Component\HttpKernel\Exception\NotFoundHttpException will be documented as the error response with the status code in $statusCode property. But it is important to keep in mind, that the status code type must be literal integer so Scramble is able to “get” the value:

// ❌ will not be documented, $statusCode type is `int`
public function update(int $statusCode)
{
throw NotFoundHttpException($statusCode);
}
// ✅ will be documented properly, the passed type is `409`
public function update()
{
throw NotFoundHttpException(409);
}

In case you have custom exception, you can define how the exception is represented as a response using ExceptionToResponseExtension.

Here are some examples of this kind of extensions that add support for already mentioned exceptions:

Response description

Description can be added to the response by adding a comment right before the return in a controller.

public function show(Request $request, User $user)
{
// A user resource.
return new UserResource($user);
}

Response status and type can be added manually as well using @status and @body tags in PHPDoc block before the return.

public function create(Request $request)
{
/**
* A user resource.
*
* @status 201
* @body User
*/
return User::create($request->only(['email']));
}
Scramble PRO
Comprehensive API documentation generation for Spatie’s Laravel Data and Laravel Query Builder.