I don't like writing tons of lines of stupid annotations just to have hope that api documentation will be generated correctly without errors that says nothing. And I am not the only one. More.
This package solves this problem the way I like - by writing PHP code.
This package adds apidocs
method to Laravel routes, where you can define route definitions using code you use every day.
This way you can:
- reuse, extend and modify existing api definitions
- create generic endpoint definitions and just modify them ad-hoc or by creating child classes
- define multiple examples
- define multiple sample responses
- define and reuse parameter definitions
- make your controllers readable again
- Add
johnylemon/laravel-apidocs
repository
composer require johnylemon/laravel-apidocs
-
Register
Johnylemon\Apidocs\Providers\ApidocsServiceProvider
provider if not registered automagically . -
Install package. This command will publish all required assets.
php artisan apidocs:install
- Enjoy!
This package ships with command for rapid route definition generation.
php artisan apidocs:endpoint SampleEndpoint
Brand new SampleEndpoint
class will be placed within app\Apidocs\Endpoints
directory.
Target directory may be changed within your apidocs
config file.
This class contains only one describe
method, where you have to use any of available methods that will describe your endpoint. Like that:
<?php
namespace App\Apidocs\Endpoints;
use Johnylemon\Apidocs\Endpoints\Endpoint;
use Johnylemon\Apidocs\Facades\Param;
class SampleEndpoint extends Endpoint
{
public function describe(): void
{
$this->title('List users')
->desc('Returns paginated list of users');
}
}
As you can see we set title and description as endpoint definition. Every method returns endpoint instance so you can chain them.
- uri
- method
- group
- deprecated
- title
- description
- desc
- query
- params
- body
- header
- headers
- example
- examples
- returns
Set uri. Called under the hood during endpoint mounting
$this->uri('/users');
Set endpoint method. Called under the hood during endpoint mounting
$this->method('POST');
Add endpoint to specific group. Group have to be defined previously.
$this->group('some-group-slug');
Will mark endpoint as deprecated.
$this->deprecated();
Set endpoint title
$this->title('Create user resource');
Set endpoint description
$this->description('This endpoint contains logic for creating user resources based on provided data');
Alias for description
Defines endpoint query params. See: parameters
$this->query([
'page' => Param::type('int')
])
Defines endpoint route params. See: parameters
$this->query([
'page' => Param::type('int')
])
Defines endpoint body params. See: parameters
$this->query([
'page' => Param::type('int')
])
Defines endpoint header
$this->header('x-johnylemon', 'apidocs')
Defines multiple endpoint header at once
$this->headers([
'x-johnylemon' => 'apidocs',
'x-laravel' => 'framework'
])
Defines endpoint example. Optionally you can define example title
$this->example([
'name' => 'johnylemon',
'web' => 'https://johnylemon.dev',
'email' => '[email protected]'
], 'Store user')
Define multiple endpoint examples at once
$this->examples([
[
'name' => 'johny',
'web' => 'https://johnylemon.dev',
'email' => '[email protected]'
],
[
'name' => 'lemon',
'web' => 'https://johnylemon.dev',
'email' => '[email protected]'
]
])
Define sample return value with status code. OPtinally you may define response description.
$this->returns(201, [
'name' => 'johny',
'web' => 'https://johnylemon.dev',
'email' => '[email protected]'
], 'User created')
->returns(401, [
'status' => 'unauthorized',
], 'Auth issue');
Additionally you can use methods like returns201
(or any other status code)
// calling this ...
$this->returns201([
'name' => 'johny',
'web' => 'https://johnylemon.dev',
'email' => '[email protected]'
], 'User created');
// ... is equivalent of this...
$this->returns(201, [
'name' => 'johny',
'web' => 'https://johnylemon.dev',
'email' => '[email protected]'
], 'User created')
Okay, you created your first endpoint definition. Now it's time to use it as some real route definition.
Lets assume you have following routes:
Route::get('api/users', [UsersController::class, 'index']);
If you want to use App\Apidocs\Endpoints\SampleEndpoint
class as definition for first of them you should simply do this:
use App\Apidocs\Endpoints\SampleEndpoint;
Route::get('api/users', [UsersController::class, 'index'])->apidocs(SampleEndpoint::class);
and... yes, thats it!
The only thing you have to do now is to call php artisan apidocs:generate
command and visit /apidocs
route to see it in action!
⚠️ This package must clear route cache to generate apidocs properly. If you are using route caching in your production environment rememeber to callartisan route:cache
afterartisan apidocs:generate
command
Because apidocs
method returns endpoint class, you can chain methods during route definition. For example, you may want to mark your route as deprecated:
use App\Apidocs\Endpoints\SampleEndpoint;
Route::get('api/users', [UsersController::class, 'index'])->apidocs(SampleEndpoint::class)->deprecated();
And because deprecated
method returns endpoint as well, you are allowed to use other endpoint methods.
⚠️ After callingapidocs
method you cannot use route-specific methods, like, say,name
method. Be sure to callapidocs
method after all framework route-specific methods are called.
Sometimes you would like to use resource
or apiResource
methods to create bunch of typical CRUD endpoints. To specify definitions for these endpoints you have to use their names:
Route::resource('posts', PostsController::class)->apidocs([
'posts.index' => PostsIndexEndpoint::class,
'posts.store' => PostStoreEndpoint::class,
]);
As you can see, you may ommit endpoints you dont want to be documented.
Sometimes you may be using resources
or apiResources
methods to create bunch of CRUDs at once. Because Laravel does not provide any handy hook for that, routes defined that way (and any other named routes!) may be documented using apidocs
helper:
//
// your resoures
//
Route::resources([
'users' => UsersController::class,
'posts' => PostsController::class,
]);
//
// defining endpoints
//
apidocs([
'posts.index' => PostsIndexEndpoint::class,
'posts.store' => PostStoreEndpoint::class,
'users.index' => UsersIndexEndpoint::class,
'users.store' => UserStoreEndpoint::class,
'users.destroy' => UserStoreEndpoint::class,
]);
As metioned earlier, you may ommit endpoints you don't want to be documented.
Some routes contains route parameters, like {user}
segment.
Sometimes you also want to use required or optional query parameters.
Routes like POST
, PATCH
, PUT
almost always expects some payload.
You can define them using params, and pass them as array to query
, body
and params
method when describing endpoint.
Lets assume your index
route from previously presented routes expects optional page
parameter.
Your definition should now contain additional query
method call with array of possible parameters. After that your code will look like that:
<?php
namespace App\Apidocs\Endpoints;
use Johnylemon\Apidocs\Endpoints\Endpoint;
use Johnylemon\Apidocs\Facades\Param;
class SampleEndpoint extends Endpoint
{
public function describe(): void
{
$this->title('List users')
->desc('Returns paginated list of users')
->query([
Param::int('page')->example(1)->default(1)->optional()
])
}
}
Note that we did not specify parameter name (page
) by array key. It is not necessary when you define parameter name within class. But of course you can define them in different way:
use Johnylemon\Apidocs\Facades\Param;
$this->query([
// parameter name will be `page`
Param::int('page')->example(1)->default(1)->optional()
// same effect:
'page' => Param::int('page')->example(1)->default(1)->optional()
// same effect:
'page' => Param::type('int')->example(1)->default(1)->optional()
// this parameter will be named `page_number`
'page_number' => Param::int('page')->example(1)->default(1)->optional()
])
As you can see, when parameter name is defined in both, array key and param name, array key will take precedence allowing you to create reusable custom parameters.
Route parameters and request body parameters can be defined same way.
Define parameter type
Param::type('int');
Define parameter name
Param::name('username');
Define parameter description
Param::description('Unique username');
Alias of description
. See description
Mark parameter as required
Param::required();
Mark parameter as optional
Param::optional();
Set parameter possible values
Param::possible([10, 100, 1000]);
Alias for possible
. See possible
Param::enum([10, 100, 1000]);
Set parameter default value.
Param::default(42);
Set parameter example.
Param::example(42);
Alias for example
. See example
Param class makes use of magic __call
method and allow you to define parameter type and name at once by using one of these methods: string
, array
, boolean
, bool
, integer
or int
use Johnylemon\Apidocs\Facades\Param;
$this->query([
Param::string('slug'), // `slug` property, that should have `string` type
Param::int('id'), // id `property`, that should have `int` type
Param::array('roles'), // `roles` property, that should have `array` type
])
It is common case that you may use page
or some other param in different endpoint definitions. So it may be cumbersome to write something like that over and over again:
$this->query([
Param::int('page')->example(1)->default(1)->optional()
]);
To solve that problem you may define PageParam
, which you can reuse as many times as you want without repeated code:
use App\Apidocs\Params\PageParam;
(...)
$this->query([
PageParam::class
]);
Custom parameters may be created by typing
php artisan apidocs:param PageParam
New param class may be defined within __construct
method:
<?php
namespace App\Apidocs\Params;
use Johnylemon\Apidocs\Params\Param;
class PageParam extends Param
{
public function __construct()
{
$this->name('page')->type('int')->default(1)->eg(42)->optional();
}
}
Apidocs endpoints will be groupped. If no group is specified, default non-groupped
group will be used.
You can define your own groups using Johnylemon\Apidocs\Facades\Apidocs
facade:
use Johnylemon\Apidocs\Facades\Apidocs;
Apidocs::defineGroup('users', 'Users', 'Manage users');
Apidocs::defineGroup('tickets', 'Tickets'); // Last parameter is optional
Groups must be defined before route registering. Perfect place for that is the the very beginning of your routes file.
This package ships with some commands that will be used for common tasks:
command | description |
---|---|
apidocs:install |
install package |
apidocs:endpoint {name} |
create endpoint class |
apidocs:param {name} |
create param class |
You can run the tests with:
vendor/bin/phpunit
The MIT License (MIT) Please see LICENSE for details.
Visit me at https://johnylemon.dev
- improve examples
- improve responses
- improve resources
- improve layout
Developed with ❤ by johnylemon.