Primeros pasos
Lo primero que tenemos que hacer es abrir la herramienta Swagger Editor y su api de ejemplo. En esta demo podemos observar un ejemplo de definición de API e ir familiarizándonos con los conceptos básicos de la misma. Esta herramienta está divida en dos, por un lado, un editor, donde en formato YAML iremos codificando la descripción de nuestro API, y por otro una imagen previa, donde observar el resultado final de nuestros ajustes. La edición de nuestro archivo YAML se basará en un conjunto de especificaciones y reglas denominado OpenApi.Después de esta breve introducción ha llegado el momento de editar nuestro archivo YAML y conocer los apartados del mismo. Comencemos por la raíz de nuestro documento:También podemos descargarnos el editor de swagger en nuestro propio entorno. Una forma muy sencilla de desplegarlo es descarnos y ejecutar la imagen de docker
Definición básica - Objeto OpenApi
El objeto OpenApi es la raíz de nuestro documento, que a su vez contiene campos básicos. Veamos un ejemplo de alguno de estos campos: En este bloque podemos observar varios campos:- openapi
- Definirá la versión de nuestra especificación de openapi utilizada.
- info
- Contendrá un objeto con información de utilidad sobre nuestro api.
- servers
- Array con las urls de los diferentes entornos donde está alojada nuestra API, por ejemplo:
- paths
- Rutas de nuestra aplicación
- components
- Componentes que reutilizaremos a lo largo de nuestro documento
- security
- Seguridad general aplicada a todos los endpoints de nuestro API
Components - Objetos reusables
En este apartado definiremos aquellos componentes que podremos reutilizar a lo largo de nuestro documento, como por ejemplo, los modelos de nuestra implementación. Podemos observar diferentes secciones.Schemas
En esta sección definiremos las entidades utilizadas en nuestro API, es decir los datos que mostrará o consumirá esta misma. El objetivo es definir la estructura básica de los datos con los que trabajamos (podríamos decir que los modelos de base de datos). Una vez que los hayamos definido en esta sección, podremos asignarlos a las operaciones que los consuman o devuelvan. En este bloque estamos definiendo un objeto User , que se corresponderá con nuestro módelo User en nuestro API. A continuación, enumero las propiedades utilizadas:- required
- Array con los campos que son requeridos a la hora de crear un registro de nuestro modelo
- properties
- Campos del objeto
- type
- tipo de dato (integer, string, object)
- readOnly / writeOnly
- Indica si es un campo que solo se muestra cuando realizamos peticiones de consulta, pero no se necesita enviar cuando insertamos o actualizamos, o al revés.
- format
- Indica diferentes formatos para el tipo string (password, email, uuid, date)
- enum
- Listado de posibles valores que puede tomar la propiedad.
Normalmente los campos id suelen ser un candidato ideal para ser readOnly, ya que cuando creamos nuestro modelo, no necesitamos enviarlo, sino que se genera de forma automática.
Más adelante podremos ver cómo definir estos modelos en línea (sin necesidad de ser globales), no obstante como la mayoría se reutilizan en varios métodos de nuestro API, la forma más óptima es crearlos dentro del objeto global components.schemas.
Parameters
Los parámetros se pueden definir a nivel de operación o path y tambíen de forma global en el objeto components.parameter. Veamos varios ejemplos de esto: Podemos observar varios atributos importantes:- name
- Nombre del parámetro, es decir , el nombre con el que lo capturaremos.
- in
- Indicándonos la localización del parámetro (query, path, header, cookie) , en función de si el parámetro está presente en la url, como parámetro del path, en una cabecera o en una cookie .
- description
- Información descriptiva
- example
- Podemos indicar un valor de ejemplo de este parámetro
- schema.type
- Tipo de dato del parámetro
Algunos ejemplos de parámetros que son el candidato ideal para ser definidos de forma global, son aquellos para realizar paginación u ordenación, ya que irán incluidos en varias de nuestras peticiones.
Al establecer nuestros parámetros como globales, podremos reutilizarlos en varias operaciones, referenciándolos mediante $ref. Esto nos ahorrará mucho trabajo, evitando que tengamos que repetir la definición de los mismos.
Paths y operaciones
En OpenApi los paths son los endpoints, como puede ser /users o /users/:userId y las operaciones, los diferentes tipos de métodos http que les aplicamos (GET, POST, PUT, DELETE, etc..). A continuación veamos un ejemplo completo de la definición de varios path y operaciones:En el anterior código podemos observar la definición de un endpoint /users y la operación get y post sobre si mismo.
Observemos cómo quedaría visualmente esta estructura: Y una de nuestrás operaciones en detalle: A continuación, echemos un vistazo a sus atributos:- security
- En este apartado referenciamos un security scheme (que posteriormente definiremos en el apartado de autorización y autenticación).
- tags
- Las tags sirven para agrupar nuestras operaciones. Usaremos la tag 'user' y así todas las operaciones se visualizarán agrupadas en nuestra interfaz final.
- parameters
- Array de parameters específicos de esta operación . De igual modo también podemos referenciar a aquellos parámetros definidos como globales haciendo uso de $ref.
- responses
- Es necesario que definamos una respuesta por cada operación. Las respuestas se caracterizan por su http status code , el dato retornado y su content type.
Responses
Las respuestas se definen a nivel de operación y nos indican el código de estado http, el dato retornado y el tipo de contenido del mismo. Observemos sus propiedades:- código de estado
- Definido a través de un número (200, 404, 500, etc..)
- description
- Información descriptiva adicional.
- content
- Tipo de contenido, por ejemplo: application/json , application/xml, application/x-www-form-urlencoded, multipart/form-data, text/plain; charset=utf-8, text/html, etc...
- schema
- Aquí referenciaremos a nuestros modelos definidos anterioremente. También podremos definirlos en la propia definición de la response, pero al ser modelos que se reutlizarán en varias operaciones, lo correcto es definirlos de forma global.
RequestBody
Al igual que se definen las respuestas, también tendremos que definir la estructura de entrada de datos para aquellas operaciones que lo permitan, como las que usen PUT Y POST. Podemos observar las siguientes propiedades:- required
- Indicando la obligatoridad del dato.
- content
- Tipo de contenido, por ejemplo: application/json , application/xml, application/x-www-form-urlencoded, multipart/form-data, text/plain; charset=utf-8 ,text/html, etc...
- schema
- Aquí al igual que en las respuestas, referenciaremos a nuestros modelos definidos anterioremente.
Autenticación y autorización
OpenApi usa el término security schema para definir la autenticacíon y autorización. Los diferentes security schemes serán definidos en el objeto components.securitySchemes Podemos observar las siguientes propiedades:- type
- Puede tomar diferentes valores en función del tipo de autenticación ( http, apiKey, oauth2, openIdConnect).
- scheme
- Dentro de cada tipo de autenticación podemos elegir entre diferentes subtipos, por ejemplo, bearer y basic dentro de la autenticación http.
- bearerFormat
- Propiedad arbitraria que indica el tipo de token, en este caso JWT (JSON Web Token).
Veamos un ejemplo de como enlazar un securityScheme para todas las rutas de nuestra API:Una vez que hemos definidos nuestros esquemas de seguridad, podremos aplicarlos en todas las operaciones o sólo en determinados paths y operaciones , usando para esto la propiedad security de nuestro documento raíz o de nuestra operación.