Scramble
Overview
Scramble is an automatic API documentation generator for Laravel applications. It automatically generates OpenAPI (Swagger) documentation by analyzing your routes, controllers, and form requests, providing interactive API documentation without manual specification writing.
Official Website: https://scramble.dedoc.co/
Why Scramble?
Scramble provides several benefits:
- Automatic generation - No manual documentation writing
- Always up-to-date - Docs reflect your actual code
- OpenAPI 3.1.0 - Industry-standard format
- Interactive UI - Built-in Swagger UI for testing
- Type inference - Analyzes PHPDoc and type hints
- Laravel integration - Understands Laravel patterns
- Zero configuration - Works out of the box
Installation
Install Scramble
# Install via Composer
composer require dedoc/scramble
# Publish configuration (optional)
php artisan vendor:publish --tag=scramble-configAccess Documentation
After installation, documentation is available at:
http://your-app.test/docs/apiConfiguration
Basic Configuration
Edit config/scramble.php:
<?php
return [
/*
* API path
*/
'api_path' => 'api',
/*
* API domain
*/
'api_domain' => null,
/*
* Documentation route
*/
'route' => [
'path' => 'docs/api',
'middleware' => ['web'],
],
/*
* Info
*/
'info' => [
'title' => config('app.name') . ' API',
'description' => '',
'version' => '1.0.0',
],
/*
* Servers
*/
'servers' => [
[
'url' => config('app.url'),
'description' => 'Default environment',
],
],
];Production Configuration
Disable in production:
// In config/scramble.php
'middleware' => [
'web',
function ($request, $next) {
if (app()->environment('production')) {
abort(404);
}
return $next($request);
},
],Or use authentication:
'middleware' => ['web', 'auth', 'admin'],Documenting Controllers
Basic Controller
Scramble automatically documents routes:
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Post;
use Illuminate\Http\JsonResponse;
class PostController extends Controller
{
/**
* Get all posts
*
* Returns a paginated list of all posts.
*/
public function index(): JsonResponse
{
$posts = Post::paginate(15);
return response()->json([
'data' => $posts->items(),
'meta' => [
'total' => $posts->total(),
'per_page' => $posts->perPage(),
],
]);
}
/**
* Get a specific post
*
* Returns a single post by ID.
*/
public function show(int $id): JsonResponse
{
$post = Post::findOrFail($id);
return response()->json([
'data' => $post,
]);
}
}With Form Requests
Scramble automatically extracts validation rules:
<?php
namespace App\Http\Controllers\Api;
use App\Http\Requests\CreatePostRequest;
use App\Models\Post;
class PostController extends Controller
{
/**
* Create a new post
*/
public function store(CreatePostRequest $request)
{
$post = Post::create($request->validated());
return response()->json([
'data' => $post,
], 201);
}
}Form Request:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class CreatePostRequest extends FormRequest
{
public function rules(): array
{
return [
'title' => 'required|string|max:255',
'content' => 'required|string',
'status' => 'required|in:draft,published',
'published_at' => 'nullable|date',
];
}
}Type Annotations
Return Types
Use PHPDoc to specify return structures:
<?php
/**
* Get user profile
*
* @return array{data: array{id: int, name: string, email: string}}
*/
public function profile(Request $request): JsonResponse
{
$user = $request->user();
return response()->json([
'data' => [
'id' => $user->id,
'name' => $user->name,
'email' => $user->email,
],
]);
}Using Resources
Laravel API Resources are automatically documented:
<?php
use App\Http\Resources\PostResource;
use App\Models\Post;
class PostController extends Controller
{
/**
* Get all posts
*
* @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection
*/
public function index()
{
return PostResource::collection(Post::paginate());
}
/**
* Get a specific post
*/
public function show(Post $post): PostResource
{
return new PostResource($post);
}
}Resource class:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class PostResource extends JsonResource
{
/**
* @param \Illuminate\Http\Request $request
* @return array{
* id: int,
* title: string,
* content: string,
* author: AuthorResource,
* published_at: string|null
* }
*/
public function toArray($request): array
{
return [
'id' => $this->id,
'title' => $this->title,
'content' => $this->content,
'author' => new AuthorResource($this->author),
'published_at' => $this->published_at?->toISOString(),
];
}
}Advanced Features
Tags and Grouping
Group endpoints using PHPDoc tags:
<?php
namespace App\Http\Controllers\Api;
/**
* @tags Posts
*/
class PostController extends Controller
{
// All methods will be tagged as "Posts"
}Or per method:
/**
* Create a new post
*
* @tags Posts, Content Management
*/
public function store(CreatePostRequest $request)
{
// ...
}Operation IDs
Customize operation IDs:
/**
* Get all posts
*
* @operationId posts.index
*/
public function index()
{
// ...
}Responses
Document different response codes:
/**
* Create a new post
*
* @response 201 {"data": {"id": 1, "title": "New Post"}}
* @response 422 {"message": "Validation failed", "errors": {}}
*/
public function store(CreatePostRequest $request)
{
// ...
}Authentication
Document authentication requirements:
<?php
namespace App\Http\Controllers\Api;
use Illuminate\Routing\Controller;
/**
* @authenticated
*/
class PostController extends Controller
{
// All endpoints require authentication
}Or per route:
/**
* Delete a post
*
* @authenticated
*/
public function destroy(Post $post)
{
$post->delete();
return response()->json(null, 204);
}Security Schemes
Sanctum Bearer Token
Configure in config/scramble.php:
'security' => [
[
'name' => 'sanctum',
'type' => 'http',
'scheme' => 'bearer',
'bearerFormat' => 'JWT',
],
],Multiple Schemes
'security' => [
[
'name' => 'bearer_token',
'type' => 'http',
'scheme' => 'bearer',
],
[
'name' => 'api_key',
'type' => 'apiKey',
'in' => 'header',
'name' => 'X-API-Key',
],
],API Versioning
Version in Path
// routes/api.php
Route::prefix('v1')->group(function () {
Route::get('/posts', [PostController::class, 'index']);
});
Route::prefix('v2')->group(function () {
Route::get('/posts', [PostControllerV2::class, 'index']);
});Multiple Documentation Pages
// config/scramble.php
'versions' => [
'v1' => [
'path' => 'api/v1',
'info' => [
'title' => 'API V1',
'version' => '1.0.0',
],
],
'v2' => [
'path' => 'api/v2',
'info' => [
'title' => 'API V2',
'version' => '2.0.0',
],
],
],Custom Types
Define Custom Response Types
<?php
namespace App\Http\Responses;
/**
* @property int $id
* @property string $title
* @property string $content
* @property string $created_at
*/
class PostResponse
{
// Type definition only, not used at runtime
}Use in controller:
/**
* Get a post
*
* @return PostResponse
*/
public function show(int $id)
{
return Post::findOrFail($id);
}Array Shapes
/**
* @return array{
* data: array{
* id: int,
* title: string,
* author: array{id: int, name: string}
* },
* meta: array{total: int, page: int}
* }
*/
public function index(): array
{
// ...
}Ignoring Routes
Ignore Specific Routes
// config/scramble.php
'ignore' => [
'api/internal/*',
'api/debug/*',
],Ignore in Controller
/**
* @hideFromAPIDocumentation
*/
public function internalMethod()
{
// Not shown in docs
}Examples and Testing
Adding Examples
/**
* Create a post
*
* @bodyParam title string required Post title Example: My First Post
* @bodyParam content string required Post content Example: This is the content
* @bodyParam status string Post status Example: published
*/
public function store(Request $request)
{
// ...
}Interactive Testing
Users can test endpoints directly from the documentation UI:
- Click "Try it out"
- Fill in parameters
- Click "Execute"
- See response
Export OpenAPI Spec
Generate JSON/YAML
# Export as JSON
php artisan scramble:export > openapi.json
# Export as YAML
php artisan scramble:export --format=yaml > openapi.yamlUse with External Tools
Generated spec can be used with:
- Postman (import OpenAPI spec)
- Insomnia
- API testing tools
- Code generators
Best Practices
1. Use Type Hints
// ✅ Good - Type hints help Scramble
public function show(int $id): JsonResponse
{
// ...
}
// ❌ Bad - No type information
public function show($id)
{
// ...
}2. Document Form Requests
class CreatePostRequest extends FormRequest
{
/**
* Validation rules for creating a post
*/
public function rules(): array
{
return [
'title' => 'required|string|max:255',
'content' => 'required|string',
];
}
}3. Use API Resources
// ✅ Good - Consistent structure
return new PostResource($post);
// ❌ Bad - Inconsistent, hard to document
return response()->json($post->toArray());4. Add Descriptions
/**
* Update a post
*
* Updates an existing post with the provided data.
* Only the post author or admins can update posts.
*
* @authenticated
*/
public function update(UpdatePostRequest $request, Post $post)
{
// ...
}5. Group Related Endpoints
/**
* @tags User Management
*/
class UserController extends Controller
{
// All endpoints grouped under "User Management"
}Troubleshooting
Documentation Not Updating
# Clear cache
php artisan cache:clear
php artisan config:clear
php artisan route:clear
# Force refresh browser
Ctrl+Shift+R (or Cmd+Shift+R)Missing Endpoints
Check that routes are:
- Under the configured
api_path - Not in the
ignorelist - Have proper controller methods
Type Inference Issues
Add explicit PHPDoc:
/**
* @return array{data: User[], meta: array{total: int}}
*/
public function index(): JsonResponse
{
// ...
}Integration with CI/CD
Validate Documentation
# In CI pipeline
php artisan scramble:export > /dev/nullAuto-Deploy Docs
# GitHub Actions
- name: Generate API Docs
run: php artisan scramble:export > public/openapi.json
- name: Deploy to S3
run: aws s3 cp public/openapi.json s3://my-bucket/api-docs/Related Documentation
- API Design Standards - API design guidelines
- API Testing - Testing API endpoints
- PHPStan - Static analysis for better type inference
Quick Reference
Installation
composer require dedoc/scrambleAccess Documentation
http://your-app.test/docs/apiExport Spec
php artisan scramble:export > openapi.jsonCommon Annotations
/**
* Endpoint description
*
* @tags TagName
* @authenticated
* @operationId custom.operation.id
* @response 200 {"data": {}}
* @response 404 {"message": "Not found"}
*/Configuration
// config/scramble.php
return [
'api_path' => 'api',
'route' => ['path' => 'docs/api'],
'info' => [
'title' => 'My API',
'version' => '1.0.0',
],
];