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:

  1. You add annotations (comments) to your controllers or some file.
  2. Specify all parameters, responses in annotations, etc.
  3. Combine into one yaml or json file using command.
  4. 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:

Swagger documentation example
Swagger documentation example

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.

Leave a Reply