~/posts/laravel-swagger-tips-examples
date: 2019-02-02 | read_time: 6 min read | topic: web-development

How to write Swagger documentation for Laravel API. Tips & examples

API documentation becomes necessary when you split the team into Backend and Frontend, especially with monorepos or microservices architecture.

Getting Started

Install the L5-Swagger package, which wraps Swagger-php and swagger-ui for Laravel:

composer require "darkaonline/l5-swagger"

Publish configuration and views:

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

Configuration

Key settings in config/l5-swagger.php:

  • routes.api — URL for documentation UI (default: api/documentation, recommend api/docs)
  • generate_always — Set to false to prevent on-the-fly generation for large APIs

Creating Base Documentation

Add @OA\Info() notation to your base controller to generate docs:

/**
 * @OA\Info(
 *    title="Your super Application API",
 *    version="1.0.0",
 * )
 */
class Controller extends BaseController
{
    // ...
}

Generate documentation:

php artisan l5-swagger:generate

Access at http://127.0.0.1:8000/api/docs

Documenting Routes

Example POST endpoint with request body and response:

/**
 * @OA\Post(
 * path="/login",
 * summary="Sign in",
 * description="Login by email, password",
 * operationId="authLogin",
 * tags={"auth"},
 * @OA\RequestBody(
 *    required=true,
 *    description="Pass user credentials",
 *    @OA\JsonContent(
 *       required={"email","password"},
 *       @OA\Property(property="email", type="string", format="email", example="user1@mail.com"),
 *       @OA\Property(property="password", type="string", format="password", example="PassWord12345"),
 *       @OA\Property(property="persistent", type="boolean", example="true"),
 *    ),
 * ),
 * @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")
 *    )
 * )
 * )
 */

Key Annotations Explained

  • @OA — OpenAPI annotation (see OpenAPI specification)
  • @OA\Post|Get|Delete — HTTP methods
  • path — URL endpoint
  • tags — Groups related APIs
  • @OA\RequestBody — Request parameters with JsonContent and Property annotations
  • @OA\Response — All possible responses (success and error codes)

Adding Success Responses

* @OA\Response(
*     response=200,
*     description="Success",
*     @OA\JsonContent(
*        @OA\Property(property="user", type="object", ref="#/components/schemas/User"),
*     )
*  ),

Creating Reusable Schemas

Define a User schema in your Model:

/**
 * @OA\Schema(
 * required={"password"},
 * @OA\Xml(name="User"),
 * @OA\Property(property="id", type="integer", readOnly="true", example="1"),
 * @OA\Property(property="email", type="string", format="email", example="user@gmail.com"),
 * @OA\Property(property="first_name", type="string", maxLength=32, example="John"),
 * @OA\Property(property="last_name", type="string", maxLength=32, example="Doe"),
 * @OA\Property(property="created_at", ref="#/components/schemas/BaseModel/properties/created_at"),
 * @OA\Property(property="updated_at", ref="#/components/schemas/BaseModel/properties/updated_at"),
 * @OA\Property(property="deleted_at", ref="#/components/schemas/BaseModel/properties/deleted_at")
 * )
 * Class User
 */

Creating Abstract BaseModel for Reusable Properties

/**
 * @OA\Schema(
 * @OA\Property(property="created_at", type="string", format="date-time", description="Initial creation timestamp", readOnly="true"),
 * @OA\Property(property="updated_at", type="string", format="date-time", description="Last update timestamp", readOnly="true"),
 * @OA\Property(property="deleted_at", type="string", format="date-time", description="Soft delete timestamp", readOnly="true"),
 * )
 * Class BaseModel
 */
abstract class BaseModel extends Model {}

JWT Bearer Token Authentication

Add security to protected routes:

/**
 * @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"),
 *    )
 * )
 * )
 */

This displays a lock icon in Swagger UI for token entry.

Advanced Property Types

Email with Pattern Validation

@OA\Property(property="email", type="string", pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).+$", format="email", example="user2@gmail.com"),

Array of Validation Errors

* @OA\Response(
*     response=422,
*     description="Validation error",
*     @OA\JsonContent(
*        @OA\Property(property="message", type="string", example="The given data was invalid."),
*        @OA\Property(
*           property="errors",
*           type="object",
*           @OA\Property(
*              property="email",
*              type="array",
*              collectionFormat="multi",
*              @OA\Items(
*                 type="string",
*                 example={"The email field is required.","The email must be a valid email address."},
*              )
*           )
*        )
*     )
*  )

Base64 Image

@OA\Property(property="picture", type="string", format="base64", example="data:image/jpeg;base64, yourSuperLongStringBinary"),

Array of Objects

@OA\Property(property="data", type="array", @OA\Items(ref="#/components/schemas/City"))

Combined Schemas (allOf, anyOf)

/**
 * @OA\Schema(
 *     schema="profileGet",
 * allOf={
 *    @OA\Schema(ref="#/components/schemas/User"),
 *    @OA\Schema(
 *       @OA\Property(property="categories", type="array", @OA\Items(ref="#/components/schemas/OrderCategory")),
 *    ),
 *    @OA\Schema(
 *       @OA\Property(property="locations", type="array", @OA\Items(ref="#/components/schemas/stateCounties")),
 *    ),
 *    @OA\Schema(
 *       @OA\Property(property="avatar", type="object", ref="#/components/schemas/File"),
 *    ),
 *    @OA\Schema(
 *       @OA\Property(property="address", type="object", ref="#/components/schemas/AddressCoordinates"),
 *    )
 * }
 * )
 *
 * @OA\Get(
 * path="/v1/profile",
 * summary="Retrieve profile information",
 * description="Get profile short information",
 * operationId="profileShow",
 * tags={"profile"},
 * security={ {"bearer": {} }},
 * @OA\Response(
 *    response=200,
 *    description="Success",
 *    @OA\JsonContent(
 *       @OA\Property(property="data", type="object", ref="#/components/schemas/profileGet")
 *    )
 * ),
 * @OA\Response(
 *    response=401,
 *    description="User should be authorized to get profile information",
 *    @OA\JsonContent(
 *       @OA\Property(property="message", type="string", example="Not authorized"),
 *    )
 * )
 * )
 */

URL Path Parameters

/**
 * @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"
 *    )
 * )
 * )
 */

Resources

tags: php laravel openapi swagger jwt