Migration Guide
This guide explains how to migrate from other API documentation generation tools to Laravel Spectrum.
🎯 Migration Overview
Laravel Spectrum works without modifying existing code, allowing for gradual migration. You can introduce Laravel Spectrum while keeping existing annotations and documentation.
📝 Migrating from Swagger-PHP
Current State Assessment
If you're using Swagger-PHP, you should have annotations like these:
/**
* @OA\Post(
* path="/api/users",
* summary="Create a new user",
* tags={"Users"},
* @OA\RequestBody(
* required=true,
* @OA\JsonContent(
* required={"name","email","password"},
* @OA\Property(property="name", type="string", example="John Doe"),
* @OA\Property(property="email", type="string", format="email"),
* @OA\Property(property="password", type="string", format="password")
* )
* ),
* @OA\Response(
* response=201,
* description="User created successfully",
* @OA\JsonContent(ref="#/components/schemas/User")
* )
* )
*/
public function store(Request $request)
{
// ...
}
Step 1: Install Laravel Spectrum
composer require wadakatu/laravel-spectrum --dev
Step 2: Initial Comparison
Compare existing Swagger output with Laravel Spectrum output:
# Backup existing Swagger documentation
cp storage/api-docs/api-docs.json storage/api-docs/swagger-backup.json
# Generate documentation with Laravel Spectrum
php artisan spectrum:generate
# Check with comparison tool
Step 3: Migrate to FormRequest
Gradually migrate from annotations to FormRequest:
Before (Swagger-PHP):
/**
* @OA\RequestBody(
* required=true,
* @OA\JsonContent(
* required={"name","email","password"},
* @OA\Property(property="name", type="string"),
* @OA\Property(property="email", type="string", format="email"),
* @OA\Property(property="password", type="string", minLength=8)
* )
* )
*/
public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users',
'password' => 'required|min:8',
]);
}
After (Laravel Spectrum):
// Create FormRequest
class StoreUserRequest extends FormRequest
{
public function rules()
{
return [
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users',
'password' => 'required|string|min:8',
];
}
}
// Update controller
public function store(StoreUserRequest $request)
{
$validated = $request->validated();
// Annotations can be removed
}
Step 4: Migrate Responses
Before:
/**
* @OA\Response(
* response=200,
* @OA\JsonContent(
* type="object",
* @OA\Property(property="id", type="integer"),
* @OA\Property(property="name", type="string"),
* @OA\Property(property="email", type="string")
* )
* )
*/
public function show($id)
{
$user = User::findOrFail($id);
return response()->json($user);
}
After:
// Create API Resource
class UserResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
];
}
}
// Update controller
public function show($id)
{
$user = User::findOrFail($id);
return new UserResource($user);
}
Step 5: Migrate Configuration
// Migrate settings from config/l5-swagger.php to spectrum.php
return [
'title' => config('l5-swagger.documentations.default.info.title'),
'version' => config('l5-swagger.documentations.default.info.version'),
'description' => config('l5-swagger.documentations.default.info.description'),
'servers' => array_map(function ($server) {
return [
'url' => $server['url'],
'description' => $server['description'] ?? '',
];
}, config('l5-swagger.documentations.default.servers', [])),
];
Post-Migration Cleanup
# Uninstall Swagger-PHP (optional)
composer remove darkaonline/l5-swagger
# Create annotation removal script
php artisan make:command RemoveSwaggerAnnotations
🔧 Migrating from L5-Swagger
L5-Swagger is a Laravel wrapper for Swagger-PHP, so the basic migration steps are the same.
Additional Considerations
-
Route Configuration Migration
// Disable L5-Swagger route configuration
// config/l5-swagger.php
'routes' => [
'api' => false, // Disable documentation routes
], -
View Migration
// Update existing Swagger UI view for Laravel Spectrum
// resources/views/api/documentation.blade.php
<script>
SwaggerUIBundle({
url: "{{ asset('storage/app/spectrum/openapi.json') }}",
// L5-Swagger settings can be used as-is
});
</script>
📚 Migrating from Scribe
Key Differences
Scribe is partially annotation-free, but not completely:
// Scribe annotation example
/**
* @group User Management
* @authenticated
* @response {
* "id": 1,
* "name": "John Doe"
* }
*/
Migration Steps
-
Group/Tag Migration
// config/spectrum.php
'tags' => [
'api/users/*' => 'User Management',
'api/posts/*' => 'Content Management',
], -
Authentication Configuration Migration
// Replace Scribe's @authenticated with automatic detection
Route::middleware('auth:sanctum')->group(function () {
// These routes are automatically detected as requiring authentication
}); -
Example Data Migration
// Migrate Scribe examples to Factories or Seeders
class UserFactory extends Factory
{
public function definition()
{
return [
'name' => $this->faker->name(),
'email' => $this->faker->unique()->safeEmail(),
];
}
}
🔄 Migrating from API Blueprint
Converting from Blueprint Format
If using API Blueprint (.apib
files):
# Group Users
## User Collection [/users]
### List Users [GET]
+ Response 200 (application/json)
+ Attributes (array[User])
Migration Approach
-
Verify Route Structure
# Match with Laravel routes
php artisan route:list --path=api -
Migrate Data Structures
- Convert Blueprint Data Structures to Laravel Resources
- Convert Attributes to FormRequests
🎯 Phased Migration Strategy
Phase 1: Coexistence Period
// Generate both documentations
"scripts": {
"docs:swagger": "php artisan l5-swagger:generate",
"docs:spectrum": "php artisan spectrum:generate",
"docs:all": "npm run docs:swagger && npm run docs:spectrum"
}
Phase 2: Validation Period
// Custom command to check differences
class CompareDocumentationCommand extends Command
{
public function handle()
{
$swagger = json_decode(file_get_contents('storage/api-docs/api-docs.json'), true);
$spectrum = json_decode(file_get_contents('storage/app/spectrum/openapi.json'), true);
// Compare paths
$swaggerPaths = array_keys($swagger['paths'] ?? []);
$spectrumPaths = array_keys($spectrum['paths'] ?? []);
$missing = array_diff($swaggerPaths, $spectrumPaths);
$extra = array_diff($spectrumPaths, $swaggerPaths);
$this->info('Missing paths: ' . implode(', ', $missing));
$this->info('Extra paths: ' . implode(', ', $extra));
}
}
Phase 3: Switchover
// Control with environment variable
if (env('USE_SPECTRUM_DOCS', false)) {
return redirect('/api/documentation/spectrum');
} else {
return redirect('/api/documentation/swagger');
}
💡 Migration Best Practices
1. Create Backups
# Backup existing documentation
git add .
git commit -m "Backup: Before Laravel Spectrum migration"
git tag pre-spectrum-migration
2. Team Communication
## Documentation Migration Notice
- Migration Period: 2 weeks
- Impact: API documentation auto-generation method changes
- Benefits: No annotations required, reduced maintenance
- Action: Use of FormRequest recommended
3. Update CI/CD
# .github/workflows/api-docs.yml
- name: Generate API Documentation
run: |
# Temporarily generate both
php artisan l5-swagger:generate || true
php artisan spectrum:generate
# Generate comparison report
php artisan docs:compare > docs-comparison.txt
4. Migration Checklist
- Install Laravel Spectrum
- Create configuration file
- Test with sample endpoints
- Gradual migration to FormRequests
- Create API Resources
- Verify documentation comparison
- Team review
- Deploy to production
- Remove old tools
🔍 Troubleshooting
Paths Not Found After Migration
// Check details in debug mode
php artisan spectrum:generate -vvv
// Generate with specific pattern
php artisan spectrum:generate --pattern="api/users/*"
Schema Mismatches
// Add custom mapping
Spectrum::addSchemaMapping(function ($path, $method) {
if ($path === '/api/legacy/endpoint') {
return [
'deprecated' => true,
'x-legacy-schema' => true,
];
}
});
📚 Related Documentation
- Installation and Configuration - Initial setup
- Comparison with Other Tools - Feature comparison table
- FAQ - Frequently asked questions