Swagger doc for Laravel API
Содержание
What is Swagger
When writing an API, it’s always a good idea to give the API users an interactive visualization of your complete API documentation. So, Swagger is a tool that can help you.
Basically, Swagger is a language and framework agnostic ecosystem to produce and visualize RESTful APIs.
How to set up Swagger on Laravel
There are a lot of packages for Swagger, I’m going to use this one for Laravel https://github.com/DarkaOnLine/L5-Swagger
In order to add this package to you composer dependencies just run next command:
composer require "darkaonline/l5-swagger"
Let’s take a look how this Swagger package for Laravel works:
- You add annotations (comments) to your controllers or some file.
- Specify all parameters, responses in annotations, etc.
- Combine into one yaml or json file using command.
- Open web page that renders this API documentation.
Example for Laravel
Let’s take a look how to add swagger to Laravel in practice. As we discussed first of all we need to add package:
composer require "darkaonline/l5-swagger"
So, our next step is to publish config file and all view file:
php artisan vendor:publish --provider "L5Swagger\L5SwaggerServiceProvider"
As a result this will create config file config/l5-swagger.php. Part of it you can see below:
<?php return [ 'default' => 'default', 'documentations' => [ 'default' => [ 'api' => [ 'title' => 'L5 Swagger UI', ], 'routes' => [ /* * Route for accessing api documentation interface */ 'api' => 'api/documentation', ], 'paths' => [ /* * Edit to include full URL in ui for assets */ 'use_absolute_path' => env('L5_SWAGGER_USE_ABSOLUTE_PATH', true), /* * File name of the generated json documentation file */ 'docs_json' => 'api-docs.json', /* * File name of the generated YAML documentation file */ 'docs_yaml' => 'pet-store.yaml', /* * Set this to `json` or `yaml` to determine which documentation file to use in UI */ 'format_to_use_for_docs' => env('L5_FORMAT_TO_USE_FOR_DOCS', 'yaml'), /* * Absolute paths to directory containing the swagger annotations are stored. */ 'annotations' => [ base_path('app'), ], ], ], ... ... ...
There are a lot of other parameters, so let’s take a look on some of them:
- ‘api’ => ‘api/documentation’ – url path to you documentation
- ‘docs_json’ => ‘api-docs.json’ – file name with generated documentation. Can be json or yaml format.
- ‘format_to_use_for_docs’ => env(‘L5_FORMAT_TO_USE_FOR_DOCS’, ‘yaml’) – format for doc file. Choose whichever you like more. I prefer json
We added this package to our Laravel project. Let’s run command to generate doc and see what happens
php artisan l5-swagger:generate
After running this command we’ll see the error
Required @OA\Info() not found
Let’s add this annotation block. @OA\Info() is related to whole documentation, not to some specific route. So it’s better to add to some Base Controller.
<?php namespace App\Http\Controllers; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Routing\Controller as BaseController; /** * @OA\Info( * title="Your super ApplicationAPI", * version="1.0.0", * ) */ class Controller extends BaseController { use AuthorizesRequests, DispatchesJobs, ValidatesRequests; }
We still cannot run this command till we add some route. So, I create controller LoginController and put annotations there. Later we go through it and analyse in details.
<?php namespace App\Http\Controllers; /** * @OA\Post( * path="/login", * summary="Log in", * description="Login user into app", * tags={"auth"}, * @OA\RequestBody( * required=true, * description="Pass user credentials", * @OA\JsonContent( * required={"email","password"}, * @OA\Property(property="email", type="string", format="email", example="user@mail.com"), * @OA\Property(property="password", type="string", format="password", example="PassWord12345"), * ), * ), * @OA\Response( * response=200, * description="Success", * @OA\JsonContent( * @OA\Property(property="user", type="object"), * ) * ), * @OA\Response( * response=422, * description="Wrong credentials response", * @OA\JsonContent( * @OA\Property(property="message", type="string", example="Sorry, wrong email address or password. Please try again") * ) * ) * ) */ class LoginController extends Controller { public function __invoke() { // TODO: Implement __invoke() method. } }
Now we can generate documentation file
php artisan l5-swagger:generate
If there is no error open http://127.0.0.1:8000/api/documentation in browser. There might be different port or/and url depending on your set up. You should see this screen:
First of all let’s take a look on annotations we added to controllers:
- @OA — means Open API annotation.
- @OA\Post — means POST request. There are GET, POST, DELETE, etc.
- Path — it’s an URL
- Tags — it will group you API by sections.
- @OA\RequestBody — it’s obvious from the name. It should have JsonContent annotation inside with Property annotations(i.e., field descriptions).
- @OA\Response — you can have as many responses as you want. You should provide all possible success and error responses here.
- When you need to describe an array of objects you can use type=” array” and pass object via @OA\Items
You can see it’s pretty easy to add documentation, just need to check some parameters you might need. As example I’ll add two more controllers:
<?php namespace App\Http\Controllers; /** * @OA\Post( * path="/v1/logout", * summary="Logout", * description="Logout user and invalidate token", * operationId="authLogout", * tags={"auth"}, * security={ {"bearer": {} }}, * @OA\Response( * response=200, * description="Success" * ), * @OA\Response( * response=401, * description="Returns when user is not authenticated", * @OA\JsonContent( * @OA\Property(property="message", type="string", example="Not authorized"), * ) * ) * ) */ class LogoutController extends Controller { public function __invoke() { } }
<?php namespace App\Http\Controllers; /** * @OA\Get( * path="/v1/geo/cities/{cityId}/zip_codes", * summary="List of zip codes by city", * description="Get list of zip codes by city", * operationId="geoZipCodes", * tags={"geo"}, * security={ {"bearer": {} }}, * @OA\Parameter( * description="ID of city", * in="path", * name="cityId", * required=true, * example="1", * @OA\Schema( * type="integer", * format="int64" * ) * ), * * @OA\Response( * response=200, * description="Success", * @OA\JsonContent( * @OA\Property(property="user", type="object"), * ) * ) * ) */ class ZipCodeController extends Controller { }
As you see we added here another parameter security={{“bearer”: {} }}, that tells swagger we need auth for this endpoint.
We added each annotation to separate controller, but it’s also possible to put everything in one file, however I don’t like this approach as it’ll grow up to thousands lines of code.
Other examples
In order to take a look on some examples and try them out you can check this link: https://github.com/zircote/swagger-php/tree/master/Examples
And there is link to swagger documentation you can try and play with parameters: https://petstore.swagger.io/
Issues with using Swagger
Some issues you might face using this package is to write and maintain the swagger annotations.
Such as adding/removing query parameters or change validation rules of an API, the swagger annotations need to be updated to reflect the latest state of the API.