Vamos a añadir Swagger a nuestro proyecto API REST con Laravel, con Swagger y Swagger UI vamos a poder documentar y exponer de forma organizada los End points de nuestra API.

Swagger es un conjunto de herramientas de código abierto creadas en base a la documentación de OpenAPI que nos ayuda a documentar, crear, definir, y consumir nuestras APIs de una forma sencilla. Como veremos a lo largo de esta publicación Swagger funciona a través de anotaciones en los comentarios proporcionandonos a través de las mismas múltiples funcionalidades para documentar y probar nuestros End Points.

En esta publicación vamos a ver la instalación de Swagger en un proyecto API REST con Laravel, y la utilización de las anotaciones, todo esto lo realizaremos sobre un proyecto ya publicado en esta web.

Instalaremos Swagger en un proyecot que ya creado en Código Xules, y lo haremos cogiendo la parte de la API de Aplicación de microservicios con Laravel y RabbitMQ : 1. EL PROYECTO , para ver como funciona Swagger puedes seguir las explicaciones en tu propio proyecto, si quieres tener como base mi proyecto tendrás que seguir primero las publicaciones siguientes para crear la API de entreprises que será la que documentemos aquí:

Índice

En esta publicación vemos como instalar y configurar Swagger y ver como documentar los primeros end points:

  1. Instalación de Swagger en Laravel
  2. Documentamos con Swagger la API REST de enterprises
    1. Definición del controlador para Swagger
  3. Documentamos uno a uno los métodos de la API con Swagger
    1. Listado de empresas: index()
    2. Consulta de una empresa: show($id)

Software que utilizaremos

En este listado incluimos todo el software utilizado en todo el proyecto, no solo en esta publicación:

  • Linux (Ubuntu 20.04 LTS): sistema operativo Linux con la versión de Ubuntu 20.04 LTS
  • Docker (docker): Docker version 20.10.7, esta guía se ha generado utilizando esta versión de Docker.
  • Docker compose (docker-compose): docker-compose version 1.25.0, , esta guía se ha generado utilizando esta versión
  • Laravel 8: framewok PHP de desarrollo de aplicaciones web, en este caso utilizaremos la versión 8.
  • Mysql 5.7: versión utilizada en la imagen de docker-compose para la base de datos.
  • Nginx: servidor web de aplicaciones
  • PHP 7.4: utilizamos PHP en una versión superior a 7.4 siguiendo las recomendaciones de Laravel, por eso la imagen que utilicemos será de esta versión.
  • Artisan: programa de línea de comandos de Laravel que nos ayuda en la creación de elementos como modelos, controladores, …, y también, nos proporciona otras utilidades como gestión de las migraciones o el listado de las rutas
  • Swagger: conjunto de herramientas de código abierto creadas en base a la documentación de OpenAPI que nos ayuda a documentar, crear, definir, y consumir nuestras APIs de una forma sencilla

1. Instalación de Swagger en Laravel

Para instalar Swagger en Laravel vamos a utilizar el módulo L5-Swagger en GitHub podéis consultar la información sobre la integración desarrollada por DarkaOnLine en Installation & Configuration.

En nuestro caso estamos trabajando con Laravel 8 la instalación que vamos a realizar aquí es válida para versiones de Laravel superiores a 6.X , y es muy sencilla.

Añadimos con Composer el módulo a nuestro proyecto (nuestro proyecto base esta con docker-compose por eso se ejecutan así los comandos, si no usas docker, pudes obviar la parte docker-compose exec admin-app-lpm):

$ docker-compose exec admin-app-lpm composer require "darkaonline/l5-swagger"

Si todo va bien podemos ver la instalación de paquetes que necesita el módulo:

Using version ^8.2 for darkaonline/l5-swagger
 ./composer.json has been updated
 Running composer update darkaonline/l5-swagger
 Loading composer repositories with package information
 Updating dependencies
 Lock file operations: 6 installs, 0 updates, 0 removals
 Locking darkaonline/l5-swagger (8.2.0)
 Locking doctrine/annotations (1.13.2)
 Locking psr/cache (1.0.1)
 Locking swagger-api/swagger-ui (v4.5.0)
 Locking symfony/yaml (v5.4.3)
 Locking zircote/swagger-php (4.2.6)
 Writing lock file
 Installing dependencies from lock file (including require-dev)
 Package operations: 6 installs, 0 updates, 0 removals
 Downloading symfony/yaml (v5.4.3)
 Downloading psr/cache (1.0.1)
 Downloading doctrine/annotations (1.13.2)
 Downloading zircote/swagger-php (4.2.6)
 Downloading swagger-api/swagger-ui (v4.5.0)
 Downloading darkaonline/l5-swagger (8.2.0)
 Installing symfony/yaml (v5.4.3): Extracting archive
 Installing psr/cache (1.0.1): Extracting archive
 Installing doctrine/annotations (1.13.2): Extracting archive
 Installing zircote/swagger-php (4.2.6): Extracting archive
 Installing swagger-api/swagger-ui (v4.5.0): Extracting archive
 Installing darkaonline/l5-swagger (8.2.0): Extracting archive
 Package swiftmailer/swiftmailer is abandoned, you should avoid using it. Use symfony/mailer instead.
 Generating optimized autoload files 
   Illuminate\Foundation\ComposerScripts::postAutoloadDump
   @php artisan package:discover --ansi
   Discovered Package: darkaonline/l5-swagger
   Discovered Package: facade/ignition
   Discovered Package: fruitcake/laravel-cors
   Discovered Package: laravel/sail
   Discovered Package: laravel/sanctum
   Discovered Package: laravel/tinker
   Discovered Package: nesbot/carbon
   Discovered Package: nunomaduro/collision
   Discovered Package: vladimir-yuldashev/laravel-queue-rabbitmq
   Package manifest generated successfully.
   80 packages you are using are looking for funding.
   Use the <code>composer fund</code> command to find out more!
   @php artisan vendor:publish --tag=laravel-assets --ansi --force
   No publishable resources for tag [laravel-assets].
   Publishing complete. 

Una vez instado podemos crear automáticamente la configuración en nuestro proyecto ejecutando:

$ docker-compose exec admin-app-lpm php artisan vendor:publish --provider "L5Swagger\L5SwaggerServiceProvider"

 Copied File [/vendor/darkaonline/l5-swagger/config/l5-swagger.php] To [/config/l5-swagger.php]
 Copied Directory [/vendor/darkaonline/l5-swagger/resources/views] To [/resources/views/vendor/l5-swagger]
 Publishing complete.

Cada vez que queramos actualizar la documentación de nuestro proyecto ejecutaremos la acción generate con el módulo l5-swagger de la siguiente forma:

$ docker-compose exec admin-app-lpm php artisan l5-swagger:generate

Si lo ejecutamos ahora directamente nos dará un error ya que aún no hemos definido ninguna etiqueta y no podrá generar la documentación, ejemplo:

Regenerating docs default
 ErrorException 
 Required @OA\Info() not found
 at vendor/zircote/swagger-php/src/Loggers/DefaultLogger.php:31
      27▕         } else {
      28▕             $error_level = E_USER_WARNING;
      29▕         }
      30▕ 
   ➜  31▕         trigger_error($message, $error_level);
      32▕     }
      33▕ }
      34▕ 
 <code>  +23 vendor frames </code>
 24  artisan:37
       Illuminate\Foundation\Console\Kernel::handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))

Entonces antes de ejecutar ese comando vamos a finalizar la configuración para empezar a documentar la aplicación, y después ejecutar la generación automática.

En nuestro proyecto Swagger se ha configurado automáticamente y puedes ver el fichero de configuración en config/l5-swagger.php , en este proyecto como no hemos definido temas de autentificación no vamos a entrar en detalles en esta parte, centrándonos en la documentación en el siguiente apartado.

Por úlitmos, si queremos fijar la ejecución automática de Swagger definimos en el fichero .env el siguiente parámetro:

L5_SWAGGER_GENERATE_ALWAYS=true

2. Documentamos con Swagger la API REST de enterprises

En nuestra API en la publicación 3.1. Desarrollamos el API REST de ADMIN-APP para empresas hemos desarrollado el API REST de enterprises, la documentación de Swagger la haremos en el controlador CmEnterpriseController veamos como.

2.1. Definición del controlador para Swagger

En nuestra clase CmEnterpriseController vamos a utilizar los comentarios para documentar la aplicación empezaremos utilizando la anotación @OA\Info para definir la información del controlador, empezaremos definiendo la versión, el título y la descripción como se puede ver a continuación:

/**
 * @OA\Info(
 *      version="1.0.0", 
 *      title="L5 OpenApi documentación de Enterprises",
 *      description="L5 Swagger OpenApi description para enterprises.",
 * )
 */
class CmEnterpriseController extends Controller

Otras anotaciones que podemos añadir por ejemplo son:

  • @OA\Contact: añadimos datos de contacto
  • @OA\License: añadimos información sobre la licencia.

Vamos a añadir estás anotaciones y ver también como podemos añadir un logo (inicialmente incluimos el default):

/**
 * @OA\Info(
 *      version="1.0.0",
 *      title="L5 OpenApi",
 *      description="L5 Swagger OpenApi description",
 *      x={
 *          "logo": {
 *              "url": "https://via.placeholder.com/190x90.png?text=L5-Swagger"
 *          }
 *      },
 *      @OA\Contact(
 *          email="julio.yanez@codigoxules.org"
 *      ),
 *      @OA\License(
 *         name="Apache 2.0",
 *         url="https://www.apache.org/licenses/LICENSE-2.0.html"
 *     )
 * )
 */
class CmEnterpriseController extends Controller

¿Qué pasa si ahora ejecutamos la generación de documentación?

$ docker-compose exec admin-app-lpm php artisan l5-swagger:generate

Pues que nos va a dar un error porque aún no hemos documentado ningún método y nos dirá que no encuentra ningún @OA\PathItem() que es necesario para generar la documentación, lo que ya tenemos es accesible la visualización web, en nuestro caso en http://localhost:28021/api/documentation, se ve así.

Swagger- Visualización inicial definiendo OA\Info()
Swagger- Visualización inicial definiendo OAInfo()

La información de la API en formato JSON se puede consultar en http://localhost:28021/docs/api-docs.json, como hemos dicho con la correspondencia hacia el estándar Open API 3.0:

Información de la documentación de la API con el estándar OPEN API 3.0
Swagger – Información de la documentación de la API con el estándar OPEN API 3.0

Entonces vamos al siguiente punto a documentar los métodos.

3. Documentamos uno a uno los métodos de la API con Swagger

Vamos a ir repasando los métodos desarrollados en CmEnterpriseController y añadiéndoles la documentación. Los métodos implementados y que se corresponden a las operaciones CRUD son:

  • index(): listado de todas las empresas.
  • store(Request $request): creación de una nueva empresa.
  • show($id): consulta de datos de una empresa.
  • update(Request $request, $id): actualización de los datos de una empresa.
  • destroy($id): eliminación de una empresa.

3.1. Listado de empresas: index()

Este método de la API va asociado a un GET que nos devuelve todas las empresas registradas, veamos la implementación del método, y a continuación vamos viendo cómo documentarlo:

    public function index()
    {
      $enterprises = CmEnterprise::all();
      $message = 'Empresas obtenidos correctamente';
      $response = [
          'success' => true,
          'data'    => CmEnterpriseResource::collection($enterprises),
          'message' => $message,
      ];
      return response()->json($response, 200);
    }

Si inspeccionamos con route:list nuestro acceso a la API de listado de empresas (enterprises) vemos que se corresponde con:

  • Domain:
  • Method: GET|HEAD
  • URI: api/enterprises
  • Name: enterprises.index
  • Action: App\Http\Controllers\API\CmEnterpriseController@index
  • Middleware: api

Entonces vamos a ir llevando esa información con anotaciones para que desde la web generada con Swagger podamos ver la información y probar el End Point:

    /**
     * Display a listing of the resource.
     * Mostramos el listado de los regitros solicitados.
     * @return \Illuminate\Http\Response
     *
     * @OA\Get(
     *     path="/api/enterprises",
     *     tags={"enterprises"},
     *     summary="Mostrar el listado de empresas",
     *     @OA\Response(
     *         response=200,
     *         description="Mostrar todas las empresas."
     *     ),
     *     @OA\Response(
     *         response="default",
     *         description="Ha ocurrido un error."
     *     )
     * ) 
     */
    public function index()
     

En negrita os marco lo que se corresponde con la documentación para Swagger, como es un método GET utilizamos la anotación @OA\Get y le vamos añadiendo información del End Point y de las respuestas. También dejamos los comentarios que teníamos anteriormente para que veais como interactúa con ellos Swagger, es sencillo:

  • path=»/api/enterprises»: definimos el path la URL de nuestro End Point
  • tags={«enterprises»}: usamos el tag enterprises para agrupar en él todos los End Point de enterprises.
  • summary=»Mostrar el listado de empresas»: descripción del End Point.
  • @OA\Response: con esta anotación definimos como son cada un de las respuestas del End point desde el Ok con 200 a las de errores.

Cómo hemos marcado la actualización en automático de Swagger si vamos ahora directamente a la web ya podemos consultar el End point y ver el resultado, veámoslo paso a paso:

Paso 1: ahora ya nos aparece el métedo GET para /api/empresas dentro de enterprises

Swagger - Listado de empresas api/nterprises
Swagger – Listado de empresas api/nterprises

Paso 2: si desplegamos el endpoint vemos la información añadida y la opción Try it out para probar el endpoint.

Swagger - Documentación del método GET de api/enterprises
Swagger – Documentación del método GET de api/enterprises

Paso 3: al pulsar Try it out se genera automáticamente la opción de ejecutar la llamada a la API:

Swagger - Generamos la llamada para probar el método
Swagger – Generamos la llamada para probar el método

Paso 4: como nuestra consulta ha tenido éxito se muestra el código de respuesta 200 y la respuesta, además podemos ver la llamada a realizar con curl y la URL de la llamada (Request URL):

Swagger - Resultado de la llamada al método GET de api/enterprises
Swagger – Resultado de la llamada al método GET de api/enterprises

3.2. Consulta de una empresa: show($id)

Este método se genera con un método GET en la llamada con el id de la empresa tal que así: api/enterprises/1. Veamos el método desarrollado que sería el siguiente y nos devuelve los datos de la empresa consultada:

    public function show($id)
    {
      $enterprise = CmEnterprise::find($id);
      if (is_null($enterprise)) {
          return response()->json(
                    $response = [
                      'success' => false,
                      'message' => 'No se ha encontrado la empresa.'
                    ],
                    404);
      }
      $message = 'Empresa encontrada.';
      $response = [
          'success' => true,
          'data'    => new CmEnterpriseResource($enterprise),
          'message' => $message,
      ];
      return response()->json($response, 200);
    }  

Ahora lo documentamos utilizando la anotación

    /**
     * Display the specified resource.
     * Muestra el registro solicitado
     * @param  int  $id
     * @return \Illuminate\Http\Response
     * @OA\Get(
     *     path="/api/enterprises/{enterprise}",
     *     tags={"enterprises"},
     *     summary="Mostrar info de una empresa",
     *     @OA\Parameter(
     *         description="Parámetro necesario para la consulta de datos de una empresa",
     *         in="path",
     *         name="enterprise",
     *         required=true,
     *         @OA\Schema(type="string"),
     *         @OA\Examples(example="int", value="1", summary="Introduce un número de id de empresa.")
     *     ),
     *     @OA\Response(
     *         response=200,
     *         description="Mostrar info de una empresa."
     *     ),
     *     @OA\Response(
     *         response=404,
     *         description="No se ha encontrado la empresa."
     *     ),
     *     @OA\Response(
     *         response="default",
     *         description="Ha ocurrido un error."
     *     )
     * ) 
     */
    public function show($id)  

Las anotaciones utilizadas y explicadas anteriormente ya no las desarrollamos porque se entienden por si mismas, en este caso definimos en la documentación que es obligatorio el paso del parámetro del id de empresa con la anotación @OA\Parameter :

  • description: descripción del parámetro necesario
  • required=true: indicamos que es obligatorio
  • @OA\Schema(type=»string»),
  • @OA\Examples(example=»int», value=»1″, summary=»Introduce un número de id de empresa.»): indicamos como sería un ejemplo para consultar la empresa con id = 1 que veremos como lo utilizar en la presentación web para lanzar la llamada al endpoint.
Swagger - Nuevo método añado para la consulta de una empresa
Swagger – Nuevo método añado para la consulta de una empresa

Cómo hicimos antes pulsamos Try it out para generar la llamada al End point con la diferencia que ahora podemos introducir los datos de la empresa que queremos consultar, como podemos ver en las siguientes imágenes:

Swagger - Lanzamos  la llamada al endpoint con la empresa por defecto
Swagger – Lanzamos la llamada al endpoint con la empresa por defecto
Swagger - Resultado de la llamada al método GET de api/enterprises con la empresa 1
Swagger – Resultado de la llamada al método GET de api/enterprises con la empresa 1

Hasta aquí esta primera parte continuaremos con los métodos que faltan en una próxima publicación.