Home » Laravel » How to Use Swagger in Laravel

How to Use Swagger in Laravel

If you need to interact with a third-party development team or your company wants documentation for an REST APIs you’re developing, you can use the OpenAPI Framework, also called Swagger. It is a standard API documentation system used in many companies.

It’s crucial to have standardized, up-to-date documentation so that other developers can easily learn how your API works and you can remember all the details a few months later. In this article, I will explain how to use Swagger in Laravel.


Table of Contents

Preparing a Test Project

Let’s build a simple API interface for viewing user information that unauthorized and authorized users can access to reveal how everything should work. Sanctum will be used for authorization. Although this example is far from reality, it will allow you to learn the most useful Swagger features. Here I will simplify the project structure as much as possible and use two resources and one controller. First, let’s create a resource that will display information about a specific user:

app/Http/Resources/UserResource.php<?php namespace App\Http\Resources; use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; class UserResource extends JsonResource { public function toArray(Request $request): array { return [ 'id' => $this->resource->getKey(), 'name' => $this->resource->name, 'email' => $this->resource->email, ]; } }

Then, create a resource to display information about the current user:

app/Http/Resources/CurrentUserResource.php<?php namespace App\Http\Resources; use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; class CurrentUserResource extends JsonResource { public function toArray(Request $request): array { return [ "id" => $this->resource->getKey(), "name" => $this->resource->name, "email" => $this->resource->email, "details" => [ "badge" => "green", "logo" => "default.png", ], ]; } }

Then, use the following command to create a controller class:

php artisan make:controller UsersController

The controller should contain the following code:

app/Http/Controllers/UsersController.php<?php namespace App\Http\Controllers; use App\Http\Resources\UserResource; use App\Http\Resources\CurrentUserResource; use Illuminate\Support\Facades\Auth; use App\Models\User; use Illuminate\Http\Request; class UsersController extends Controller { public function __construct() { $this->middleware("auth:sanctum", [ "except" => ["login", "list", "get"], ]); } public function login(Request $request) { $request->validate([ "email" => "required|string", "password" => "required|string", ]); $credentials = $request->only("email", "password"); if (Auth::attempt($credentials)) { return response()->json([ "token" => Auth::user()->createToken("ApiToken") ->plainTextToken, "type" => "bearer", ]); } return response()->json([ "message" => "Invalid login or email", ], 401 ); } public function current(Request $request) { return new CurrentUserResource($request->user()); } public function get(string $id) { return new UserResource(User::findOrFail($id)); } public function list() { $users = User::query()->paginate(10); return UserResource::collection($users); } }

Next, you need to register these API routes in routes/api.php:

routes/api.phpuse App\Http\Controllers\UsersController;

routes/api.phpRoute::post('users/login', [UsersController::class, 'login']) ->name('login'); Route::get('users/current', [UsersController::class, 'current']) ->name('current'); Route::get('users', [UsersController::class, 'list']) ->name('list'); Route::get('users/{id}', [UsersController::class, 'get']) ->where('id', '[0-9]+') ->name('get');

And finally, let’s create 10-20 users in the database to be able to make requests to the API and see what it will return. Comment out the following line in the file database/seeders/DatabaseSeeder.php to do this:

database/seeders/DatabaseSeeder.php\App\Models\User::factory(10)->create();

Then, run the seeder:

php artisan db:seed

In this case, 10 users with the password “password” will be created. If you want to change the password or any other user fields before generating testing users, you can do it in the database/factories/UserFactory.php file:

How to install Swagger in Laravel

There is a package of l5-swagger that adds support for generating OpenAPI documentation for Laravel. This package can generate JSON or YAML files with documentation based on PHP annotations and display this API docs in the web interface. Use the following artisan command to install it:

composer require darkaonline/l5-swagger

Then publish the Swagger configuration and view files with this command:

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

The main configuration for the package is located in the config/l5-swagger.php file. For most projects, nothing needs to be changed in the configuration. By default, you can open Swagger UI frontend via the path: api/documentation. By default, the documentation is generated in JSON format, and it searches for annotations in the app folder. If you want to add other directories, open the configuration file, find documentations -> default -> paths -> annotations, and add additional paths here, for example:

Also, if you want to be able to test requests to your API using the Swagger UI, you must set the L5_SWAGGER_CONST_HOST environment variable in your .env file. For localhost it will look like this:

.envL5_SWAGGER_CONST_HOST="http://localhost"

The following command can be used to generate swagger documentation:

php artisan l5-swagger:generate

Swagger tool will generate a JSON file which contains the docs and located in the storage/api-docs folder. However, it generates only the default documentation. If you want to have a few tabs of documentation in the UI, docs for each tab will be placed in a separate file. In this case, you need to add the –all option to re-generate all files:

php artisan l5-swagger:generate --all

But don’t do that right now. You should first add the project information and at least one API endpoint. Otherwise, an error will be thrown.

How to use Swagger in Laravel

Here I list the most commonly used annotations and their descriptions that you can use in your projects:

  • Info – information about a whole project;
  • Tag – create a tag to separate endpoints by sections;
  • Get – describe GET request;
  • Post – describe POST request;
  • Delete – describe DELETE request;
  • Post – describe POST request;
  • Put – describe PUT request;
  • Schema – describe JSON properties or property parameters, which can be reused in other schemas;
  • Response – describe a response for a request;
  • Property – describe a field;
  • Items – describe elements of an array;

1. Adding Information About the Project

You need to add project information to the @Info annotation, before generating your first documentation. I usually use the app/Http/Controllers/Controller.php class. For example.

app/Http/Controllers/Controller.php/** * @OA\Info( * description="This is an example API for users management", * version="1.0.0", * title="User Management API" * ) */ class Controller extends BaseController { use AuthorizesRequests, ValidatesRequests; }

2. Endpoint Description

Let’s see what the annotations should look like to describe any endpoint. Each query description starts with an annotation that describes its HTTP method. It can be: @Get, @Post, @Put, @Delete. In the annotation itself, you can set the values of the following parameters:

  • path – URI path for request;
  • tags – array of tags, to place the request
  • summary – short description;
  • description – full description;
  • operationId – unique identifier for request; if empty, an UUID will be used.

This is the root annotation for each API endpoint documentation. Inside this annotation, you need to describe what parameters the API endpoint accepts and what responses can be returned. The following set of annotations are used for these purposes:

  • @Parameter – describes a parameter, which should be passed in a query string, headers, or as a cookie;
  • @RequestBody – describes request body for PUT, POST and DELETE requests;
  • @Response – describes a response, its contents and status code;
  • @Security – describes endpoint authorization.

This is not a complete list of supported annotations, but only the most commonly used ones.

For example, let’s describe an endpoint that returns a list of users, skipping the description of the data returned in the response. This is /api/users.

By default, all documentation can be in any file in the ./app folder and its subfolders. Therefore, you can place the code below directly into the app/Http/Controllers/UsersController.php class before implementing the list() method. I usually place the description of the API endpoints right in the controller above the methods it describes. It makes it easier to update the documentation quickly when the code is changed.

app/Http/Controllers/UsersController.php /** * @OA\Get( * path="/api/users/", * summary="List users", * operationId="list_users", * description="Returns a list of users that are registered on a server with pagination", * @OA\Response( * response=200, * description="List of users", * ), * @OA\Response( * response=500, * description="Something went wrong", * ) * ) */

Pay attention to the comma at the end of the description line. You should put commas if you plan to add something after this element in the future because then you can forget to add a comma, and the annotation compiler will generate an error, but it can be difficult to find its cause. Now you can re-generate this documentation:

php artisan l5-swagger:generate

The HTML documentation can be found at http://localhost/api/documenation</strong> for a local server:

3. Request Parameters Description

You can describe several entities using the @Parameter annotation:

  • Path – route parameter, which can be parsed from the path, for example /user/{id};
  • Query – parameters, which are added to the query path, for example, /user?id=1;
  • Header – any custom headers;
  • Cookie – any custom cookie.

You can provide the following arguments for this annotation:

  • name – the name of the parameter;
  • in – a type of parameter (path, query, header, or cookie);
  • description – a short description of a parameter;
  • required – determines whether the parameter is required. All path parameters are required;
  • example – example of data that you should pass to this parameter;

This is not a complete list of arguments that can be added. The full list can be found on this page. Let’s look at an example and add the information about the page parameter to the API description for getting the list of users:

app/Http/Controllers/UsersController.php * @OA\Parameter( * name="page", * description="Specify page number", * in="query", * required=false * ),

The re-generated documentation will look like this:

Next, let’s describe the request to get information about a user by their ID, again skipping the information about the response fields:

app/Http/Controllers/UsersController.php /** * @OA\Get( * path="/api/users/{id}", * summary="Get users", * description="Returns information about user by id", * operationId="get_user_by_id", * @OA\Parameter( * name="id", * description="User ID", * in="path", * required=true, * example="1" * ), * @OA\Response( * response=200, * description="User found", * ), * @OA\Response( * response=500, * description="Something went wrong", * ) * ) */

Here, we add the id parameter right in the path using curly braces and then describe the parameter itself in the @Parameter annotation. After that, you can re-generate swagger docs and see the result:

You can also try sending this request to the server and see the response by clicking the Try It Out button:

Here you can change the parameter value and click Execute to get the server’s response:

As you can see, Swagger understands what this parameter is and how to pass it.

4. JSON Description

You can use JSON data in response and request data description. For Laravel, there is no difference between form data and JSON in the request body unless you are sending files. You can describe JSON content using the @JsonContent annotation. This annotation can contain one or more @Property annotations describing each JSON field. The @Property annotation can contain the following parameters:

  • property – the name of the field;
  • type – the type of the field. Supported types: integer, number, string, boolean, object, array;
  • description – a short description of the field;
  • format – additional information about the data. It depends on the type of field;
  • example – the example data for displaying;
  • nullable – determines whether the parameter can contain a null value.

The most common number formats are float and double. String formats: date, date-time, password, binary, byte, email, uuid, uri, hostname, ipv4, ipv6, and others. These are just the most commonly used parameter types. In addition, one @Property annotation can contain other properties, enumerations, arrays, etc. For example, let’s look at how to describe the message field in response to an error message. If debugging is disabled, Laravel will return the following JSON:

{ "message": "Internal Server Error" }

The description of this JSON will look like this:

app/Http/Controllers/UsersController.php * @OA\JsonContent( * @OA\Property( * property="message", * description="An error message", * type="string", * nullable="true", * example="Internal Server Error" * ) * )

5. Response Description

Until now, I have described only status codes for responses, but the documentation should contain detailed information about which fields will be returned. You learned how to describe JSON from the previous paragraph. Now you need to insert this description into the error response description. Just add this @JsonContent after the description parameter:

Now let’s describe an endpoint that allows to get user data by ID that returns JSON with the following data:

{ "id": 1, "name": "Eino Bosco", "email": "ksmitham@example.net" }

This is what the full response description about successful operation with user data will look like:

app/Http/Controllers/UsersController.php * @OA\Response( * response=200, * description="User found", * @OA\JsonContent( * @OA\Property( * property="id", * description="User identifier", * type="integer", * nullable="false", * example="1" * ), * @OA\Property( * property="name", * description="User name", * type="string", * nullable="false", * example="Kellen Boyer" * ), * @OA\Property( * property="email", * description="User E-mail", * type="string", * nullable="false", * example="kellen.boyer@example.com" * ), * ) * ),

For now, our documentation will look like this:

6. Using Schemas

You may have already noticed that when you request information about a specific user and when you request a list of users, the same fields are returned in response. Quite often, you will have a set of fields that are used in multiple endpoints. It would be code duplication to describe them again each time.

Instead, you can describe the documentation for a UserResource once in its file and then import it wherever the resource is used. This can be done using the @Schema annotation. It allows you to describe all the necessary fields or parameters in one place and then use them everywhere.

Each schema must have a schema parameter with the name of the schema. This name will be used to import the schema. For example, the schema for UserResource that is used in the /users/{id} route will look like this:

app/Http/Resources/UserResource.php/** * @OA\Schema( * schema="UserSchema", * @OA\Property( * property="id", * description="User identifier", * type="integer", * nullable="false", * example="1" * ), * @OA\Property( * property="name", * description="User name", * type="string", * nullable="false", * example="Kellen Boyer" * ), * @OA\Property( * property="email", * description="User E-mail", * type="string", * nullable="false", * example="kellen.boyer@example.com" * ), * ) * ) */

Now, you can add this schema to your response using the ref parameter. Use the path “#/components/schemas” plus the name of the schema:

app/Http/Controllers/UsersController.php * @OA\Response( * response=200, * description="User found", * @OA\JsonContent( * ref="#/components/schemas/UserSchema" * ) * ),

On the frontend, it will look like this:

You can go to the Schema tab to see more details:

Also, at the bottom of the page, you’ll see a list of schemas and this one:

The schema can be imported not only into JsonContent but also into other annotations, e.g: Property, Items, etc.

7. Array Description

Often, APIs return an array of data. You can use the @Property annotation with the type array to describe arrays. In this case, the annotation must contain the @Items annotation, which describes what the array item looks like. For example, this is how the description of an array with a list of users will look like:

app/Http/Controllers/UsersController.php * @OA\JsonContent( * @OA\Property( * property="data", * type="array", * description="List of users", * @OA\Items( * @OA\Property( * property="id", * description="User identifier", * type="integer", * nullable="false", * example="1" * ), * @OA\Property( * property="name", * description="User name", * type="string", * nullable="false", * example="Kellen Boyer" * ), * ) * ) * )

This entry means that each element of the array is an object that contains the id and name fields. But instead of listing the fields manually, we can use the previously described scheme. Then the response with the list of users will look like this:

app/Http/Controllers/UsersController.php * @OA\Response( * response=200, * description="Users List", * @OA\JsonContent( * @OA\Property( * property="data", * type="array", * description="List of users", * @OA\Items( * ref="#/components/schemas/UserSchema" * ) * ) * ) * ),

This is how it will look in swagger UI:

8. Authorization

You may often need to restrict API access. Some requests can only be executed by authorized users. In our example, this is the /api/users/current request, which displays information about an authorized user. In Laravel, you can use different authorization mechanisms, such as Passport with JWT tokens or a simplified Sanctum. Swagger allows you to describe in the documentation what authorization data should be passed to the request. First, you need to describe the securitySchema in the swagger config file l5-swagger.php. For Sanctum, the schema will look like this

config/l5-swagger.php "sanctum" => [ "type" => "apiKey", "description" => "Enter token in format (Bearer <token>)", "name" => "Authorization", "in" => "header", ],

The type field can be set to apiKey, basic or oauth2. In the name field, you need to specify the name of the header in which the key should be passed, and in the in field, it is indicated that the parameter should be passed as a header. Now this scheme can be used in the desired endpoint using the security parameter. For example, let’s start describing the documentation for the api/users/current endpoint:

app/Http/Controllers/UsersController.php/** * @OA\Get( * path="/api/users/current", * summary="Get current user info", * description="Returns information about current user", * security={{"sanctum":{}}}, * operationId="get_current_user", * @OA\Response( * response=200, * description="Everything OK", * ), * @OA\Response( * response=500, * description="Something went wrong", * ) * ) */

As you can see, the header in which the authorization will be passed has already been described, and there is no need to describe it again. This is how it will look in the browser:

Before making a request to the server, you should click on the lock icon, enter the authorization token and click Authorize:

After this, you can perform the request and it will be successful.

9. Enumerations

Enumerations allow you to describe all possible values for the property. Describe the property as usual, and then use the enum parameter to define the possible values. For example, let’s define a badge field that can have three values green, red, and yellow:

/** * @OA\Property( * property="badge", * type="string", * description="User badge color", * nullable=false, * enum={ * "green", * "red", * "yellow" * } * ), */

You can also describe a field with the @Schema annotation and reuse it anywhere. Just describe the schema of the property:

app/Http/Resources/CurrentUserResource.php * @OA\Schema( * schema="BadgeEnum", * type="string", * description="User badge color", * nullable=false, * enum={ * "green", * "red", * "yellow" * } * )

Now you can import this schema into any property without rewriting the same code:

/** * @OA\Property( * property="badge", * type="string", * ref="#/components/schemas/BadgeEnum" * ) */

10. Object Description

If you want to describe any object that contains other properties, such as an array element, you can use the object type and add nested properties to the property with the object type. Let’s describe the schema with a nested details object for response of the current user endpoint:

app/Http/Resources/CurrentUserResource.php/** * @OA\Schema( * schema="CurrentUserSchema", * @OA\Property( * property="id", * type="integer", * description="User ID", * nullable=false, * example="1" * ), * @OA\Property( * property="name", * type="string", * description="User Name", * nullable=false, * example="John" * ), * @OA\Property( * property="details", * type="object", * nullable=false, * description="User details", * @OA\Property( * property="badge", * type="string", * nullable=false, * ref="#/components/schemas/BadgeEnum" * ), * @OA\Property( * property="logo", * type="string", * nullable=false, * description="User logo", * ), * ) * ) */

Well, it remains to add this schema to the description of the users/current query in the controller:

app/Http/Controllers/UsersController.php * @OA\Response( * response=200, * description="Everything OK", * @OA\JsonContent( * ref="#/components/schemas/CurrentUserSchema" * ) * ),

Information about the BadgeEnum can be found at the bottom of the page or on the Schema tab:

11. Combine Schemas

Schemas allow you to do many interesting things. You can combine two or more schemas to describe arrays that can contain multiple different types of objects. Here are the most common directives for combining schemas:

  • anyOf – can contain any of the specified schemas or both;
  • allOf – must contain all specified schemas;
  • oneOf – must contain only one of the specified schemas.

Each of these directives needs an array of schema references. For example, imagine that when you request a list of /users/list users with authorization, your API will return extended data about the authorized user and regular data about everyone else. You can describe such an array using the anyOf parameter:

app/Http/Controllers/UsersController.php * @OA\JsonContent( * @OA\Property( * property="users", * type="array", * description="List of users", * @OA\Items( * anyOf={ * @OA\Schema(ref="#/components/schemas/UserSchema"), * @OA\Schema(ref="#/components/schemas/CurrentUserSchema"), * } * ) * ) * )

12. POST, PUT, DELETE Requests

Now you know everything you need to create project documentation. But so far, we’ve only considered GET requests. Documentation for POST and other types of endpoints looks almost the same as documentation for GET endpoints. The main difference between POST and GET requests is the presence of the request body. This means that you can use the @RequestBody annotation together with the @JsonContent to describe what should be contained there. For example, let’s describe the documentation for a user creation request:

app/Http/Controllers/UsersController.php/** * @OA\Schema( * schema="LoginRequest", * @OA\Property( * property="email", * type="string", * description="User EMail", * nullable=false, * format="email" * ), * @OA\Property( * property="password", * type="string", * description="User Password", * nullable=false, * example="password" * ), * ) * * @OA\Post( * path="/api/users/login", * summary="Authorize user", * description="Authorizes user by its email and password", * operationId="login", * @OA\RequestBody( * @OA\JsonContent(ref="#/components/schemas/LoginRequest") * ), * @OA\Response( * response=201, * description="Authentication successful", * @OA\JsonContent( * @OA\Property( * property="token", * type="string", * description="Sanctum authorization token", * example="1|fSPJ2AR0TU0dLB6aiYgtSGHkPnFTfBdh4ltISiSo", * ), * @OA\Property( * property="type", * type="string", * description="Token type", * example="bearer", * ), * ) * ), * @OA\Response( * response=500, * description="Internal server error" * ) * ) */

I’ve made the LoginRequest schema separate because, in a real project, it’s better to place it in a request class extended from FormRequest and then bind it to the controller, as shown in the code above.

13. Tagging endpoints

You can group documentation for endpoints using tags. For example, you can group all the endpoints for user management using the “Users” tag. First, create a tag in the same place as the main project information:

app/Http/Controllers/Controller.php * @OA\Tag( * name="Users", * description="API Endpoints of User Management" * )

Note that there should not be multiple documentation blocks for one method or class. The data will be read only from the nearest block, so putting everything in one block /** */ is better.

After this, add the tag to the required endpoints using the tags parameter. For example.

app/Http/Controllers/UsersController.php * tags={"Users"},

This parameter can accept multiple tags, so you need to pass an array, not a string. Now it looks nicer:

Swagger Annotations and Subclasses

It’s a little weird, but when you extend a class that has @Schema annotations, your subclass inherits the schema. For example, you have a CurrentUserResource. If you create an AdminResource based on that class and add an empty AdminUserSchema to it, it will inherit all the fields from the CurrentUserSchema:

app/Http/Resources/AdminResource.php<?php namespace App\Http\Resources; use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; /** * @OA\Schema( * schema="AdminUserSchema", * ) */ class AdminResource extends CurrentUserResource { public function toArray(Request $request): array { return [ 'id' => $this->resource->getKey(), 'name' => $this->resource->name, 'email' => $this->resource->email, 'details' => [ 'badge' => 'green', 'logo' => 'default.png' ] ]; } }

This is how this schema will look like in the Swagger web interface:

So be careful when using inheritance with Swagger.

Wrapping Up

In this article, I have explained how to use Swagger in Laravel with the l5-swagger package to create docs for RESTful APIs. As you can see, it can be very convenient. Your documentation will be located as close as possible to your code so that you can update it regularly on time.

Leave a Comment

Exit mobile version