Skip to main content

API Documentation in Laravel and Lumen with Swagger

10 Jul, 20198 min read

Table of Contents

Integrating Swagger into Laravel Application

The following integration is practiced on Laravel version ^5.8.* but integration with older versions should be straight forward. We will start by installing the darkaonline/l5-swagger package which exposes services providers that we can use to quickly integrate it inside our application.

composer require darkaonline/l5-swagger

We will also export the configuration files by executing following command:

php artisan vendor:publish --provider "L5Swagger\L5SwaggerServiceProvider"

This will copy the default configuration files to config/l5-swagger.php. Have a look inside it and modify any details if needed. It works out of the box without any changes. Laravel has recently introduced the package auto-discovery for service providers which this packages exposes. So there is not need for further configuration. But if you are using an older versions of Laravel, you can add the following service provider inside config/app.php's providers array:

L5Swagger\L5SwaggerServiceProvider::class

To check for auto-discovery, if executing composer install prints any messages related to discovery, auto discovery is available otherwise not.

That's all we need to configure the Laravel application to work with Swagger. If you are only looking for integration with Laravel, you can skip the next section and jump to the code annotation section, else follow along with me.

Integrating Swagger into Lumen Application

We will use a different package named darkaonline/swagger-lume to integrate the Swagger with Lumen.

composer require darkaonline/swagger-lume

Next step is to register the service provider. Now, we will update our bootstrap/app.php file

// uncomment the facades
$app->withFacades();
// load the swagger-lume configuration
$app->configure('swagger-lume');
// register the lumen swagger service provider
$app->register(\SwaggerLume\ServiceProvider::class);

And, we will publish the configuration files also by executing

php artisan swagger-lume:publish

This swagger UI package comes with Swagger UI component that it will load when accessed via browser. But, due to some restriction on how the Lumen router works, we will need to copy these assets to our public folder. Copy the assets by executing following command:

cp -a vendor/swagger-api/swagger-ui/dist public/swagger-ui-assets

Now, to make sure that every developer runs this command after installation, we will add a post-install-cmd script inside our composer.json file. This will make sure that the assets are always copied to the public folder.

{
"scripts": {
"post-install-cmd": [
"cp -a vendor/swagger-api/swagger-ui/dist public/swagger-ui-assets"
]
}
}

You may also want to add following auto generated folders to yours .gitignore file:

/resources/views/vendor
/storage/api-docs
/public/swagger-ui-assets

Now that we have integrated Swagger inside our Lumen and Laravel application, our next step is to annotate our code and see the apis docs in actions.

Add API Annotations

In Swagger documentation, we can annotation all the stuff related to API docs e.g. Parameters, Requests, Responses, Security, Schemas etc. But before we add annotation to anything, we need to provided some required annotation to Swagger. Fro Swagger to work, we will add some basic information regarding our apis e.g. title, version, description etc. To do that, we will put the following annotation inside app/Http/Controllers/Controller.php:

/**
* @OA\Info(
* title="Your Awesome Modules's API",
* version="1.0.0",
* @OA\Contact(
* email="[email protected]",
* name="Developer Team"
* )
* )
*/

If you like, you can put this comment anywhere inside your app's directory (swagger-config.path.annotations). The base controller felt the right place for this as this is the entry point for all HTTP requests.

Now let's write our first api annotation for a GET request. Create or use an existing Controller. Assuming UserController with a index method.

<?php
namespace App\Http\Controllers;
use App\User;
use Illuminate\Http\Request;
// We can define the User Scheme here or in our App\User model
/**
* @OA\Schema(
* schema="UserSchema",
* title="User Model"
* description="User model",
* @OA\Property(
* property="id", description="ID of the user",
* @OA\Schema(type="number", example=1)
* ),
* @OA\Property(
* property="name", description="Name of the user",
* @OA\Schema(type="string", example="User Name")
* )
* )
*/
// We can define the request parameter inside the Requests or here
/**
* @OA\Parameter(
* parameter="get_users_request_parameter_limit",
* name="limit",
* description="Limit the number of results",
* in="query",
* @OA\Schema(
* type="number", default=10
* )
* ),
*/
class UserController extends Controller {
/**
* @OA\Get(
* path="/users",
* summary="Return the list of users",
* tags={"Hello"},
* @OA\Parameter(ref="#/components/parameters/get_users_request_parameter_limit"),
* @OA\Response(
* response=200,
* description="List of users",
* @OA\JsonContent(
* @OA\Property(
* property="data",
* description="List of users",
* @OA\Schema(
* type="array,
* @OA\Items(ref="#/components/schemas/UserSchema")
* )
* )
* )
* )
* )
*/
public function index (Request $request) {
$users = User::paginate($request->get("limit", 10));
return ["data" => $users]
}
}

Annotation can be written anywhere irrespective of the code but we should annotate as close as possible to our code. Most of the components (schemas, parameters, responses) can be references and so the docs can be written in a composite reuse principle way.

Viewing Swagger UI for API documentation

Now before viewing our docs, we will need to generate docs. One way to generate docs is to run php artisan l5-swagger:generate (Laravel) or php artisan swagger-lume:generate(Lumen) every time we change the docs Or we can set SWAGGER_GENERATE_ALWAYS=true inside our .env to auto generate docs when we refresh the page. After doing one of these, start the server php artisan serve (Laravel) or php -S localhost:800 -t public and visit http://localhost:8000/api/docs to view the generated docs. These is JSON format that can be viewed by visiting http://localhost:8000/docs.

Visit swagger-php examples or OA 3.0 Spec for more information on how to write annotation.

Securing Docs

For private/internal projects, docs should not be publicly accessible. To secure docs, instead of building a full authentication system (we can), we will implement a key based authentication. API Developer can define a key for his/her docs and consumer developer will need to pass the key to request the api docs. darkaonline/l5-swagger and darkaonline/swagger-lume

Authentication Middleware

<?php
namespace App\Http\Middleware;
use Closure;
use Symfony\Component\HttpFoundation\Cookie;
class SecureApiDocs
{
public function handle($request, Closure $next)
{
// for local, dont add any authentication
if (env('APP_ENV') === 'local') {
return $next($request);
}
$token = $request->get('token');
if (!$token) {
// try to load the token from referer
$query = array();
parse_str(
parse_url($request->header('referer'), PHP_URL_QUERY),
$query
);
if (isset($query['token'])) {
$token = $query['token'];
}
}
// we will match it against the `SWAGGER_DOCS_TOKEN` environment variable
if ($token === env('SWAGGER_DOCS_TOKEN')) {
return $next($request);
} else {
return abort(403);
}
}
}

Registering and applying middleware

Now, let's add this middleware into our application. For Laravel, we need to add update the app/Http/Kernel.php and put it inside routeMiddleware as follows:

protected $routeMiddleware = [
'docs' => \App\Http\Middleware\SecureApiDocs::class,
];

And for Lumen, we need to update the bootstrap/app.php and add it to the routeMiddleware's array as follows:

$app->routeMiddleware([
'docs' => App\Http\Middleware\SecureApiDocs::class,
]);

Now, we just need to modify the .env file to include SWAGGER_DOCS_TOKEN=<some_strong_token> and update the swagger configuration file config/l5-swagger.php by putting the docs middleware in the routes's key as follows:

<?php
return [
"routes" => [
'middleware' => [
'api' => ['docs'],
'asset' => [],
'docs' => ['docs'],
'oauth2_callback' => [],
],
]
];

And we have secured our docs. Only users with a valid token will be able to access the /api/docs and /docs routes.

To test if it is working, try changing the APP_ENV from local to production and set an string to SWAGGER_GENERATE_ALWAYS. Now, start the development server and visit http://localhost:8000/api/docs. You should get a 403 error response.

Where to Annotation ?

Like Tests, Documentation doesn't have to be modular. It should be more readable. But modularity doesn't hurt if it's readable as well. And so, we should write modular and readable annotations. Laravel/Lumen has already separated modules based on what they concerns, we can utilise it to write module annotation. Following step can be followed:

  • Use Request (php artisan make:request) to make the Parameter components and reference them
  • Use Resource (php artisan make:resource) to make the Schema and Response components and reference them
  • Use Controller (php artisan make:controller) to annotate the api end points
  • Use Middleware (php artisan make:middleware) to annotate any security docs

Thanks for reading. I would love to hear your thoughts on twitter!

Subscribe to the telegram channel (sudkumar) or add the RSS to your feed aggregator to get updates when a new article is published.