Lumen API Tutorial – Documentation using Swagger UI
Introduction
In this tutorial we will use Swagger UI to create documentation for our Lumen or Laravel API. We will create a command that generates the swagger JSON file and a page to render the documentation. Then we will write basic phpdoc blocks and annotations.
Installation
We will use this specific package which provides us the methods we need to create the swagger JSON file.
https://github.com/zircote/swagger-php
To install we just use composer
composer require zircote/swagger-php
Artisan Command
Now we can write an artisan command which scans a specific directory for phpdoc blocks with annotations about our API.
Create a file app/Console/Commands/SwaggerScan.php.
namespace App\Console\Commands; use Illuminate\Console\Command; class SwaggerScan extends Command { protected $signature = 'swg:scan'; public function handle() { $path = dirname(dirname(__DIR__)); $outputPath = dirname(dirname(dirname(__DIR__))) . DIRECTORY_SEPARATOR . 'public/swagger.json'; $this->info('Scanning ' . $path); $openApi = \OpenApi\scan($path); header('Content-Type: application/json'); file_put_contents($outputPath, $openApi->toJson()); $this->info('Output ' . $outputPath); } }
This command will generate a swagger.json file at the public directory.
Swagger UI Page
We need to create a page to view the documentation. On Lumen we create a file public/swagger-ui.html.
If you are using Laravel you can also use the blade templating engine.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>My Api Documentation</title> <link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@3/swagger-ui.css"> </head> <body> <div id="swagger-ui"></div> <script src="//unpkg.com/swagger-ui-dist@3/swagger-ui-standalone-preset.js"></script> <script src="//unpkg.com/swagger-ui-dist@3/swagger-ui-bundle.js"></script> <script> (function () { SwaggerUIBundle({ url: "swagger.json", dom_id: '#swagger-ui', presets: [ SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset ], layout: "StandaloneLayout" }); })(); </script> </body> </html>
The javascript in the bottom parses the swagger.json file and renders it on theĀ div
with the ID “swagger-ui”.
Annotations
Now, what’s left to do is to write some annotations, run the artisan command, and view the documentation on the browser.
Technically we can write the annotations anywhere as long as it is inside the directory that we specify on the artisan command but in this tutorial I will use the controller, view-models, and transformers.
Sometimes request body looks different from the response so we can use view-models or just the eloquent model for the request body. For the response we write it on the response transformers. Then finally for the details of the request like the method (GET, POST, PUT, PATCH, and DELETE) we put it in the controller.
The first annotations we need to write is the basic informationabout our API.
Edit your app/http/Controllers/Controller.php.
/** * Class Controller * @package App\Http\Controllers * @OA\OpenApi( * @OA\Info( * version="1.0.0", * title="Laravel Swagger Tutorial", * @OA\License(name="MIT") * ), * @OA\Server( * description="API server", * url="http://api.laravel-swagger-tutorial.test/", * ), * ) */ class Controller extends BaseController { // omitted for brevity }
Here are some example schemas/formats that we can reference to other annotations.
NOTE: Using view-models is not required.
app/ViewModels/PostViewModel.php
namespace App\ViewModels; /** * Class PostViewModel * @property string $title * @property string $body * @package App\ViewModels * @OA\Schema( * schema="PostRequest", * type="object", * title="PostRequest", * required={"title", "body"}, * properties={ * @OA\Property(property="title", type="string"), * @OA\Property(property="body", type="string") * } * ) */ class PostViewModel extends ViewModel { protected $mappings = [ 'title' => 'title', 'body' => 'body', ]; }
app/Transformers/PostTransformer.php
namespace App\Transformers; /** * @OA\Schema( * schema="PostResponse", * type="object", * title="PostResponse", * properties={ * @OA\Property(property="id", type="integer"), * @OA\Property(property="attributes", type="object", properties={ * @OA\Property(property="title", type="string"), * @OA\Property(property="body", type="string") * }), * @OA\Property(property="relationships", type="array", @OA\Items({ * * })), * } * ) */ class PostTransformer extends Transformer { public $type = 'post'; protected $availableIncludes = ['user']; public function transform($user) { return [ 'id' => $user->id, 'title' => $user->title, 'body' => $user->body, ]; } public function includeUser($post) { return $this->item($post->user, new UserTransformer(), 'user'); } }
app/Transformers/UserTransformer.php
namespace App\Transformers; /** * @OA\Schema( * schema="UserResponse", * type="object", * title="UserResponse", * properties={ * @OA\Property(property="id", type="integer"), * @OA\Property(property="attributes", type="object", properties={ * @OA\Property(property="name", type="string"), * @OA\Property(property="email", type="string") * }), * } * ) */ class UserTransformer extends Transformer { public $type = 'user'; /** * @param \App\Models\User $user * @return array */ public function transform($user) { return [ 'id' => $user->id, 'name' => $user->name, 'email' => $user->email, ]; } }
Here are some examples for API routes.
app/Http/Controllers/PostController.php
namespace App\Http\Controllers; use App\Models\Post; use App\Transformers\PostTransformer; use Illuminate\Http\Request; class PostController extends Controller { /** * @OA\Get( * path="/posts", * summary="List all posts", * operationId="index", * tags={"Post"}, * @OA\Parameter( * name="include", * in="query", * required=false, * @OA\Schema( * type="array", * @OA\Items( * type="string", * enum = {"user"}, * ) * ) * ), * @OA\Response( * response=200, * description="An paged array of posts", * @OA\JsonContent( * type="array", * @OA\Items(ref="#/components/schemas/PostResponse") * ), * ), * @OA\Response( * response="default", * description="unexpected error", * @OA\Schema(ref="#/components/schemas/Error") * ) * ) */ public function index() { return $this->paginate(\App\Models\Post::paginate(5), new PostTransformer()); } /** * @OA\Post( * path="/posts", * summary="New blog post", * operationId="store", * tags={"Post"}, * @OA\RequestBody( * required=true, * description="Post object", * @OA\JsonContent(ref="#/components/schemas/PostRequest") * ), * @OA\Response( * response=200, * description="A post", * @OA\JsonContent(ref="#/components/schemas/PostResponse"), * ), * @OA\Response( * response="default", * description="unexpected error", * @OA\Schema(ref="#/components/schemas/Error") * ) * ) * @param Request $request * @return array */ public function store(Request $request) { $viewModel = new PostViewModel(); $viewModel->mapArray($request->json()->all()); // Transfer this on a service class or do some validation. $post = new Post(); $post->title = $viewModel->title; $post->body = $viewModel->body; $post->save(); return $this->item($post, new PostTransformer()); } }
Viewing on the browser
After editing the annotations you have to run the artisan command.
php artisan swg:scan
To view the documentation on the browser go to <your host>/swagger-ui.html.
That is all. Thanks for reading!
Monaam
April 3, 2020 at 3:28 pm
i need api documentation for routes
Okpala
June 11, 2020 at 4:23 pm
I get this error “Call to undefined function OpenApi\scan()”