API Documentation in Laravel and Lumen with Swagger
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.
<?phpnamespace 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
<?phpnamespace App\Http\Middleware;use Closure;use Symfony\Component\HttpFoundation\Cookie;class SecureApiDocs{public function handle($request, Closure $next){// for local, dont add any authenticationif (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 variableif ($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:
<?phpreturn ["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