knuckleswtf/scribe
Scribe generates human-friendly API docs from your Laravel code. It builds a polished single-page HTML site with code samples and “Try It Out”, plus Postman collections and OpenAPI specs. It can infer params from validation and fetch sample responses.
Installation:
composer require knuckleswtf/scribe
Publish the config file:
php artisan vendor:publish --provider="Knuckles\Scribe\ScribeServiceProvider" --tag="scribe-config"
First Run: Generate documentation for all routes:
php artisan scribe:generate
Access the generated HTML at public/docs/index.html (or your configured output path).
Quick Test:
Visit http://your-app.test/docs to see the interactive documentation with "Try It Out" panels.
Document a new API endpoint:
// routes/api.php
Route::get('/users/{id}', [UserController::class, 'show'])
->middleware('auth:sanctum')
->name('users.show');
Scribe automatically detects this route and generates:
routes/ and app/Http/Controllers/.// app/Http/Controllers/UserController.php
use Knuckles\Scribe\Attributes\Deprecated;
use Knuckles\Scribe\Attributes\BodyParam;
#[Deprecated('Use /users/{id}/profile instead')]
public function show(int $id) { ... }
#[BodyParam(name: 'name', type: 'string', description: 'User name')]
public function update(Request $request) { ... }
#[Response] attributes or configure in scribe.php:
'responses' => [
'users.show' => [
'200' => [
'description' => 'Returns a user',
'content' => [
'application/json' => [
'schema' => [
'type' => 'object',
'properties' => [
'id' => ['type' => 'integer'],
'name' => ['type' => 'string']
]
]
]
]
]
]
],
Illuminate\Http\Resources\Json\JsonResource and generates schemas.FormRequest classes:
// app/Http/Requests/StoreUserRequest.php
public function rules(): array
{
return [
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users'
];
}
scribe.php:
'parameters' => [
'users.store' => [
'body' => [
'name' => ['type' => 'string', 'description' => 'User name'],
'email' => ['type' => 'string', 'format' => 'email']
]
]
],
auth:sanctum).scribe.php:
'security' => [
'bearerAuth' => [
'type' => 'http',
'scheme' => 'bearer',
'bearerFormat' => 'JWT'
]
],
Add undocumented routes manually:
'scribe' => [
'endpoints' => [
[
'method' => 'GET',
'uri' => '/admin/dashboard',
'description' => 'Admin dashboard',
'auth' => ['admin']
]
]
],
Add to your deployment script:
php artisan scribe:generate --output=docs
git add docs
git commit -m "Update API docs"
Use Laravel Mix or Vite to serve versioned docs:
// vite.config.js
import { defineConfig } from 'vite';
import { scribePlugin } from 'vite-plugin-scribe';
export default defineConfig({
plugins: [
scribePlugin({
versions: ['v1', 'v2'],
outputDir: 'docs'
})
]
});
Generate specs during deployment:
php artisan scribe:generate --export=postman,openapi
public/docs/openapi.json (OpenAPI 3.0/3.1)public/docs/Postman Collection.jsonAdd to your test suite:
// tests/Feature/ApiDocumentationTest.php
public function test_api_docs_generate()
{
$this->artisan('scribe:generate')
->assertExitCode(0)
->expectsOutput('Generated API documentation');
$this->assertFileExists(public_path('docs/index.html'));
}
Customize the UI by extending the default theme:
vendor/knuckleswtf/scribe/resources/views to resources/views/vendor/scribe.endpoint.blade.php).#[Response] with explicit schemas or configure ignore_relationships in scribe.php:
'responses' => [
'users.show' => [
'200' => [
'ignore_relationships' => ['posts', 'comments']
]
]
],
app/Http/Kernel.php and use #[Deprecated] or #[Auth] attributes:
use Knuckles\Scribe\Attributes\Auth;
#[Auth('api')]
public function sensitiveEndpoint() { ... }
unique:users,email) aren’t documented.'parameters' => [
'users.store' => [
'body' => [
'email' => [
'type' => 'string',
'format' => 'email',
'description' => 'Must be unique in users table'
]
]
]
],
--parallel flag:
php artisan scribe:generate --parallel
scribe.php:
'ignore_routes' => [
'admin.*',
'web.*'
],
#[Response] fails with "Model not found" errors.// config/scribe.php
'factories' => [
App\Models\User::class => App\Models\UserFactory::new()
],
Run with -v for detailed logs:
php artisan scribe:generate -v
Test changes without overwriting:
php artisan scribe:generate --dry-run
Validate the spec using Swagger Editor:
php artisan scribe:generate --export=openapi
open public/docs/openapi.json
| Error | Solution |
|---|---|
Class not found |
Ensure the class is autoloaded or use fully qualified names. |
Route not found |
Check ignore_routes or route registration. |
Validation rule parsing failed |
Define rules manually or extend the validator parser. |
Memory exhausted |
Increase memory_limit or use --parallel. |
Extend Knuckles\Scribe\Strategies\Strategy to add new data sources:
// app/Scribe/CustomStrategy.php
namespace App\Scribe;
use Knuckles\Scribe\Strategies\Strategy;
class CustomStrategy extends Strategy
{
public function getEndpoints(): array
{
return [
[
'method' => 'GET',
'uri' => '/custom',
'description' => 'Custom endpoint'
]
];
}
}
Register in scribe.php:
'strategies' => [
\App\Scribe
How can I help you explore Laravel packages today?