Información general

Herramientas de desarrollo
Para la creación de una API según esta guía de desarrollo, hay varias herramientas que pueden sernos útiles. La mayoría de las que proponemos son herramientas online pero también hay algunas herramientas para su instalación en entorno local.
En general, tanto para crear las especificaciones, la documentación adicional y los ficheros de configuración, cualquier editor de texto nos puede servir. No obstante, indicamos algunas herramientas que nos pueden ayudar, tanto para validar lo que vamos definiendo como para ver el resultado.
Herramientas para la creación de una API síncrona
Para generar la especificación de una API síncrona, proponemos algunos editores online que, conforme vamos definiendo la API, nos van mostrando el resultado y los posibles errores de definición.
- Nuevo editor swagger.io: es una herramienta online que, en función de lo que se indique en la primera línea, validará el tipo de especificación. En este caso, tendremos que indicar openapi: ‘3.1.0’ para que valide esta especificación síncrona.
- OpenAPI-GUI: es una herramienta online para editar especificaciones OpenAPI a través de una interfaz gráfica de usuario.
Herramientas para crear la especificación de una API asíncrona
Para generar la especificación de una API asíncrona, proponemos los siguientes editores que actúan de forma similar a los anteriores.
- AsyncAPI Studio: herramienta online que permite diseñar, editar y visualizar especificaciones AsyncAPI.
- Intellij IDEA: es un IDE de programación que, mediante el plugin AsyncAPI, valida especificaciones AsyncApi 3.0.0. Hay que instalarlo en entorno local.
NOTA: En el momento de la redacción de esta guía, el editor de swagger.io solo soporta versiones 2.x de AsyncApi; por lo que no se indica como opción ya que la explicación de la estructura está realizada según la versión 3.0.0.
Herramientas para la generación de documentación adicional
Para la generación de documentación adicional para las APIs en formato Markdown, se propone la siguiente herramienta online:
- Stackedit: herramienta online que permite ver el resultado de la documentación generada conforme se va escribiendo.
- Además, la mayoría de IDEs comúnmente usados tienen plugins que permiten la edición en formato Markdown.
Herramientas para la creación de una colección de pruebas
Por otro lado, para la realización de las colecciones de pruebas, si es necesario la instalación de herramientas específicas. En este caso, proponemos el uso de Postman, como herramienta ampliamente aceptada por la comunidad y que, además, mediante plugins permite a las herramientas de CI/CD lanzar los casos de prueba generados, ya que permite exportarlos en formato JSON.
Desarrollar una API
A continuación, se podrán ver los pasos que hay que seguir para desarrollar una API para que pueda ser publicada en el API Manager.
Generar el repositorio de código de la API
Cuando se desarrolla una API es obligatorio que el código esté alojado en un repositorio de código remoto que permita el alojamiento de proyectos, la colaboración en equipo, el control de versiones, la automatización de ciertas tareas y la integración con herramientas externas.
Existe un Repositorio de Código corporativo de la Junta de Andalucía de uso obligatorio para todos los proyectos. Además, el correcto uso de este repositorio se encuentra gobernado por una serie de normas que definen la Estrategia de Ramificación del mismo.
El primer paso para desarrollar una API, será generar este repositorio de código donde alojar todos los ficheros necesario de la API. Dicho repositorio deberá seguir la plantilla publicada por la Oficina de Arquitectura, que indica la estructura de carpetas y ficheros necesarios para su desarrollo.
En esta plantilla se encontrarán algunas configuraciones por defecto y parametrización que se tendrá que modificar para adecuar a la API.
La estructura de carpetas y ficheros para la creación y publicación de una API que encontraremos en la plantilla es la siguiente:
Donde
- api.yaml: contiene toda la información básica necesaria para importar la API
- README.md: documentación explicativa sobre la API (estado, cambios, …) en lenguaje Markdown.
- Definitions: contiene el archivo de definición asociado a la API.
- Docs: contiene documentación adicional de la API. Cada documento tendrá una carpeta separada con su nombre.
- Image: imagen en miniatura de la API, con tamaño máximo de 1MB.
- UnitTest: contiene el fichero json con la colección de pruebas.
Crear el fichero de especificación
La especificación de una API es un documento técnico que define de manera detallada su funcionamiento. Es esencialmente el contrato que describe la API, cómo deben ser las peticiones y las respuestas, los métodos y endpoints disponibles, etc. Sirve como una guía para los desarrolladores que desean consumir a través de una aplicación el API.
La especificación de una API debe incluir una serie de componentes y aspectos que se describen a continuación:
- Endpoints: son las URL específicas a las que se puede acceder para interactuar con la API.
- Métodos HTTP: son los métodos HTTP que se pueden usar en cada uno de los endpoint (GET, POST, PUT, DELETE, etc).
- Parámetros de petición: son los distintos parámetros que se pueden o deben enviar en la solicitud, tanto en la ruta, el cuerpo, la cabecera, como en la cadena de consulta.
- Estructura de respuesta: es la forma en que los datos se devolverán, generalmente en formato JSON.
- Códigos de estado HTTP: son los códigos de respuesta HTTP que la API puede devolver, como 200 (OK), 404 (No Encontrado), 500 (Error Interno del Servidor), etc.
- Autenticación y autorización: son los métodos que la API utiliza para garantizar que solo los usuarios autorizados puedan acceder a los recursos, como OAuth, API keys, tokens JWT, etc.
- Errores: aquí se incluirá una descripción de los posibles errores que pueden ocurrir.
- Ejemplos de uso: ejemplos concretos de cómo realizar solicitudes a la API y las respuestas esperadas.
- Versionado: información sobre las versiones de la API y cualquier cambio significativo entre versiones.
El público objetivo de la especificación de la API son los desarrolladores y arquitectos, ya que proporciona un marco claro para que comprendan cómo interactuar con la API y qué esperar de ella, facilitando la integración y el uso eficiente de los servicios ofrecidos.
Si la API es síncrona, la especificación se escribirá siguiendo el estándar OpenApi, mientras que, si es asíncrona, se escribirá siguiendo la iniciativa AsyncApi para la escritura de especificaciones de APIs asíncronas.
Obligatoriedad de campos:
- Los campos sombreados en rojo son campos obligatorios de la especificación. Ejemplo: title
- Los campos sombreados en naranja son campos que, aun no siendo obligatorios en la especificación, en el documento de normas y estándares se indican como obligatorios. Ejemplo: description
- Los campos sombreados en azul son campos que desde la Oficina de Arquitectura se recomienda que se rellenen para facilitar la comprensión de la especificación. Ejemplo: tag
A continuación, se explicarán las distintas partes de las especificaciones según la diferenciación anterior y que definirán esos componentes y aspectos anteriormente nombrados. A la vez que se van explicando las partes, se irá ilustrando con ejemplos de una tienda de mascotas online que permite consultar, dar de alta nuevas mascotas, generar código de chip, etc…
Especificación API síncrona
Una API síncrona es aquella en la que el cliente envía una solicitud al servidor y espera una respuesta inmediata antes de continuar con otras tareas. La comunicación entre el cliente y el servidor ocurre en tiempo real, y el cliente queda bloqueado hasta que el servidor completa la operación solicitada y devuelve una respuesta.
Un ejemplo, iniciando la tienda de mascotas, es cuando un cliente realiza una solicitud para verificar la disponibilidad de un producto específico, como una bolsa de comida para perros. Con una API síncrona, el cliente envía la solicitud al servidor, y la aplicación espera la respuesta del servidor, que confirma si el producto está disponible o no, antes de permitir que el cliente realice el siguiente paso, como añadir el producto al carrito de compra. Durante este proceso, el cliente no puede realizar otras acciones hasta que reciba la respuesta del servidor.
La especificación OpenAPI define una interfaz estándar para las APIs HTTP que permite que tanto los humanos como las computadoras descubran y comprendan las capacidades del servicio sin acceso al código fuente.
Las especificaciones OpenAPI serán escritas en formato yaml, debe contener, al menos, un campo path o un campo components y se componen de varios campos “raíz” que detallan la estructura y el comportamiento de la API, que son:
- openapi: indica la versión de OpenAPI utilizada.
- info: proporciona una descripción completa de la API, ayudando a los desarrolladores, arquitectos y otras partes interesadas para comprender rápidamente su propósito y capacidades. Como elemento obligatorio de la especificación de OpenAPI, a menudo sirve como punto de referencia inicial para los usuarios que navegan por la documentación de la API
- servers: array de objetos de tipo server que proporcionan información de conectividad a un servidor de destino. Si no se proporciona la propiedad servers o es un array vacío, el valor predeterminado sería un objeto de tipo server con un valor de URL de /.
- paths: las rutas y operaciones disponibles para la API.
- webhooks: define los webhooks que una API puede enviar. Permiten a un servidor enviar eventos en tiempo real a otro sistema. En lugar de que el cliente consulte regularmente la API para obtener actualizaciones, el servidor puede notificar al cliente directamente cuando ocurra un evento específico. Al ser muy específico, no se explicará este campo. En las referencias hay enlaces a la documentación.
- security: array de mecanismos de seguridad que se pueden utilizar en la API. El array incluye objetos de requisitos de seguridad alternativos que se pueden utilizar, es decir, solo se debe cumplir uno de para autorizar una solicitud. La configuración de seguridad de las operaciones individuales puede anular esta definición. Para que la seguridad sea opcional, se puede incluir un requisito de seguridad vacío ({}) en el array.
- tags: lista de etiquetas utilizadas por el documento con metadatos adicionales. El orden de las etiquetas se puede utilizar para reflejar el orden en que las herramientas de análisis las ordenan. No todas las etiquetas que se utilizan en los campos de operaciones deben estar declaradas. Las etiquetas que no están declaradas pueden estar organizadas de forma aleatoria o según la lógica de las herramientas. Cada nombre de etiqueta de la lista debe ser único.
- externalDocs: documentación externa adicional.
- components: define y centraliza elementos reutilizables de una API, como esquemas, respuestas, parámetros, ejemplos, requestBodies, cabeceras, y enlaces. Este campo ayuda a evitar redundancias al permitir que estos componentes se definan una vez y luego se referencien en múltiples lugares a lo largo de la especificación.
A continuación, pasaremos a ver los campos que contienen cada uno de estos campos raíz, su definición y posibles valores. Para facilitar la comprensión de cada uno de estos campos “raíz”, se mostrará un ejemplo de cada uno de ellos por separado.
openapi
Contiene el número de versión de OpenAPI que se está usando en la especificación de la API síncrona.
openapi: 3.1.0
info
Proporciona metadatos e información general sobre la API que ayuda a su entendimiento además de a conocer el origen de la API.
Los campos que contiene son:
- title: título o nombre de la API.
- summary: breve resumen de la API.
- description: descripción de la API indicando el propósito y características de la API. Se puede usar formato Markdown.
- termsOfService: URL o documento especificando los términos de uso de la API.
- contact: información de contacto del propietario de la API o persona que la mantenga (name, email, y url).
- license: información de la licencia de la API, incluyendo los campos name y url.
- version: versión de la API para la que se realiza la especificación.
info:
title: Ejemplo de App de tienda de mascotas
summary: Un gestor de tiendas de mascotas.
description: Este es un servidor de ejemplo para tiendas de mascotas.
termsOfService: https://example.com/terms/
contact:
name: Soporte APP
url: https://www.example.com/support
email: support@example.com
license:
name: Apache 2.0
url: https://www.apache.org/licenses/LICENSE-2.0.html
version: 1.0.1
servers
Define los distintos servidores donde se despliega la API.
Cada objeto del array contiene los siguientes campos:
- url: URL al host de destino. Soporta el uso de variables de sustitución nombradas entre {llaves}.
- description: descripción del servidor definido. Se puede usar formato Markdown.
- variables: Map de pares clave-valor que se usan para sustituir en el campo url. El campo default es obligatorio.
servers:
- url: https://development.gigantic-server.com/v1
description: Servidor de desarrollo
- url: https://{username}.gigantic-server.com:{port}/{basePath}
description: Servidor de producción
variables:
username:
# nota! Sin enum significa que permite un valor abierto
default: demo
description: este valor es asignado por el proveedor de servicio in el ejemplo `gigantic-server.com`
port:
enum:
- '8443'
- '443'
default: '8443'
basePath:
default: v2
paths
Contiene las rutas relativas a los endpoints individuales y sus operaciones. La ruta se agrega a la URL del objeto de servidor para construir la URL completa.
Normas de nomenclatura: es importante revisar la documentación de normativa para definir este campo de forma correcta.
El patrón para definir paths es /{path}.
Si coinciden URL, las rutas concretas (sin plantillas entre llaves) se compararán antes que las que tienen plantillas. Las rutas con plantillas con la misma jerarquía, pero con diferentes nombres de plantilla no deben existir, ya que son idénticas. En caso de coincidencia ambigua, la herramienta decidirá cuál usar. Para aclarar este párrafo, veremos una serie de ejemplos
En el siguiente ejemplo, por el primer caso entrarán aquellas peticiones cuya id no sea ‘mias’. Esto es necesario tenerlo en cuenta si necesitamos crear paths de este tipo:
/mascotas/{id}
/mascotas/mias
En el siguiente ejemplo, ambos paths se consideran idénticos, se usará uno de los dos, sin saber cuál; por lo tanto, es obligatorio evitar este tipo de paths con plantillas en el mismo nivel de jerarquía porque no se sabe cuál se usará.
/mascotas/{id}
/mascotas/{nombre}
El siguiente ejemplo puede dar lugar a una resolución ambigua si lanzáramos una petición a /mascotas/mias; por lo que, en general, deberíamos evitar este tipo de paths, a no ser que sean casos muy controlados y completamente seguros de que no se puede dar la ambigüedad:
/{entidad}/mias
/mascotas/{id}
El campo path está compuesto de objetos item path que describen las operaciones disponibles para ese path concreto. Estos objetos contienen los siguientes campos:
- $ref: URL al host de destino. Soporta el uso de variables de sustitución nombradas entre {llaves}.
- summary: resumen que se aplicará a todas las operaciones de ese path
- description: descripción que se aplicará a todas las operaciones de ese path. Se puede usar formato Markdown.
- servers: array alternativo de servidores que dan servicio a todas las operaciones de ese path.
- parameters: lista de parámetros que son aplicables a todas las operaciones de ese path. Estos parámetros se pueden sobrescribir a nivel de operación, pero no se pueden eliminar. No debe incluir parámetros duplicados. Puede hacer referencia a parámetros definidos en el objeto components.
- [operaciones]: cada una de las operaciones que se pueden realizar en ese path (get, put, post, delete, options, head, patch).
paths:
/mascotas/:
get:
……………
/mascotas/{id}:
get:
……………
put:
……………
parameters:
- name: id
in: path
description: ID de la mascota a actualizar
required: true
schema:
type: string
format: ^\d{6}-[A-Z]{2}-\d{5}
examples:
- 999999-FR-11111
Este último campo operaciones es el que define qué se puede hacer en el API, así que procederemos a revisar los campos que puede tener:
- tags: lista de etiquetas para el control de la documentación de la API. Se pueden usar para agrupar de manera lógica.
- summary: corto resumen de lo que la operación hace
- description: explicación detallada del comportamiento de la operación. Se puede usar formato Markdown.
- externalDocs: documentación externa adicional para la operación.
- operationId: cadena única para identificar a la operación. Debe seguir formato case-sensitive y seguir las convenciones comunes de nombrado facilitando su legibilidad.
- parameters: lista de parámetros que son aplicables a la operación. Si se define uno ya definido a nivel de path, lo sobrescribe.
- servers: array alternativo de servidores que dan servicio a la operación. Sobrescribe a los definidos a nivel path o raíz.
- deprecated: declara la operación como deprecada. Los consumidores deberían de abstenerse de usarla. El valor por defecto es false.
- security: array de mecanismos de seguridad que se pueden utilizar en la operación. Sobrescribe los definidos a nivel general en la API. Para eliminar la declaración de seguridad del nivel raíz, hay que declarar un array vacío.
- requestBody: cuerpo de la petición que se aplica a la operación.
- responses: lista de las posibles respuestas que pueden ser devueltas al ejecutar la operación.
tags:
- mascota
summary: Actualiza una mascota en la tienda con los datos recibidos a partir de su id
operationId: actualizaMascotaPorId
parameters:
- name: id
in: path
description: ID de la mascota a actualizar
required: true
schema:
type: string
format: ^\d{6}-[A-Z]{2}-\d{5}
examples:
- 999999-FR-11111
requestBody:
……………
responses:
……………
En el código anterior, se puede ver un ejemplo de la operación put del path mascotas/{id}, y se puede ver que el parámetro id se ha vuelto a definir dentro de la operación cuando ya estaba definido a nivel de path. Aunque no es incorrecto, tampoco es necesario ponerlo si se ha definido a nivel de path.
Los campos requestBody y responses son los más importantes dentro de la definición de una API puesto que indican el contenido de la petición y las posibles respuestas que la operación puede devolver, tanto su código http como el contenido. Es por ello que veremos un ejemplo amplio de cada uno de estos campos.
requestBody:
content:
application/json:
schema:
title: Objeto mascota
required:
- nombre
properties:
nombre:
type: string
maxLength: 35
description: Nombre de la mascota
examples:
- Hulk
vacunada:
type: boolean
description: Indica si la mascota está vacunada
examples:
- true
enfermedades:
type: array
items:
type: object
properties:
nombre:
type: string
description: Nombre de la enfermedad
examples:
- Leishmania
criticidad:
type: number
minimum: 1
maximum: 5
description: Criticidad de la enfermdad (1 - Baja, 5 - Muy alta)
examples:
- 2
Código anterior: Ejemplo del contenido del campo requestBody para la petición de actualización de una mascota
responses:
'200':
description: OK Los datos actualizados de la mascota serán devueltos
content:
application/json:
schema:
title: Objeto mascota
required:
- id
- tipo
properties:
id:
type: number
description: Identificador de la mascota
format: ^\d{6}-[A-Z]{2}-\d{5}
examples:
- 999999-FR-11111
nombre:
type: string
maxLength: 35
description: Nombre de la mascota
examples:
- Hulk
sexo:
type: string
enum: [M,H]
description: Sexo de la mascota ([M]acho o [H]embra)
examples:
- M
vacunada:
type: boolean
description: Indica si la mascota está vacunada
examples:
- true
edad:
type: number
minimum: 0
maximum: 50
description: Edad en años de la mascota
examples:
- 6
nacimiento:
type: string
format: date
description: Fecha de nacimiento
examples:
- 2018-10-18
enfermedades:
type: array
items:
type: object
properties:
nombre:
type: string
description: Nombre de la enfermedad
examples:
- Leishmania
criticidad:
type: number
minimum: 1
maximum: 5
description: Criticidad de la enfermdad (1 - Baja, 5 - Muy alta)
examples:
- 2
'400':
description: ERROR Bad Request
content:
application/json:
schema:
title: Objeto error devuelto para códigos 4XX
required:
- codigo
- mensaje
properties:
mensaje:
type: string
description: Mensaje de error.
examples:
- Bad Request
descripcion:
type: string
description: Descripción detallada del error.
examples:
- La petición realizada no es correcta
codigo:
type: integer
format: int64
examples:
- 400
Código anterior: Ejemplo del contenido del campo responses para la petición de actualización de la mascota
security
Lista de los distintos mecanismos de seguridad que pueden ser usados en la API.
Para las APIs que se publican en el API Manager, se dejará la siguiente configuración ya que, será configurada en el fichero de parametrización.
security:
-
default: []
En el caso de las APIs que no se tengan que publicar en el API Manager, es necesario configurar la seguridad que van a tener. Para ello, es necesario definir los esquemas de seguridad que vamos a necesitar, en la sección components. Los posibles tipos son http (para autenticación básica y bearer), apikey, oauth2 y openIdConnect.
components:
securitySchemes:
BasicAuth:
type: http
scheme: basic
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
ApiKeyAuth:
type: apiKey
in: header
name: X-API-Key
OpenID:
type: openIdConnect
openIdConnectUrl: https://example.com/.well-known/openid-configuration
OAuth2:
type: oauth2
flows:
clientCredential:
tokenUrl: https://example.com/oauth/token
scopes:
read: Concede acceso de lectura
write: Concede acceso de escritura
admin: Concede acceso de admin
A continuación, aplicaremos el esquema o esquemas que necesitemos. Esto se puede hacer a nivel global de la API y a nivel de operación. Si se aplica a nivel general, se aplicará la misma configuración a todas las operaciones a menos que se sobrescriba.
security:
- ApiKeyAuth: []
- OAuth2:
- read
- write
paths:
/pets/{id}:
get:
summary: Obtiene toda la información de mascotas
security:
- OAuth2: [admin] # Usa OAuth con un scope distinto al global
/ping:
get:
summary: Comprueba si el servidor está funcionando
security: [] # Sin seguridad
Es posible usar múltiples tipos de autenticación combinándolos con AND y OR.
security: # ApiKeyAuth OR BasicAuth
- ApiKeyAuth
- BasicAuth
security: # ApiKeyAuth AND BasicAuth
- ApiKeyAuth
BasicAuth
Como hemos visto en la definición de los esquemas de seguridad y en los ejemplos, OAuth2 contiene un parámetro llamado 'scopes'. OpenIdConnect también lo tiene pero no hay que definirlo en el esquema.
Estos scopes o ámbitos sirven para controlar los permisos de acceso a los recursos. Por ejemplo, para hacer una operación GET, podríamos necesitar el scope 'read', mientras que para un PUT o POST, necesitaríamos el scope 'write'.
paths:
/pets:
get:
summary: Obtiene la lista de mascotas
security:
- OAuth2: [read] # <------
...
post:
summary: Añade una mascota
security:
- OAuth2: [write] # <------
tags
Contiene la definición de las etiquetas que se pueden usar en las operaciones para agruparlas de manera lógica. No es obligatorio el uso de etiquetas, pero si muy recomendable para facilitar la lectura de la especificación ya que, la mayoría de los editores y visualizadores de OpenAPI agrupan las operaciones según la etiqueta que tengan definida.
Dentro de las operaciones se puede hacer uso de etiquetas que no se hayan definido a nivel global; pero se recomienda siempre tenerlas definidas todas a nivel global.
Un objeto etiqueta se compone de los siguientes campos:
- name: nombre de la etiqueta.
- description: descripción de la etiqueta definida. Se puede usar formato Markdown.
- externalDocs: documentación externa adicional para la etiqueta.
tags:
- name: mascota
description: Operaciones sobre mascotas
externalDocs
Permite referenciar recursos externos para documentación extendida.
Contiene los siguientes campos:
- url: URL donde se encuentra la documentación.
- description: descripción de la documentación referenciada. Se puede usar formato Markdown.
externalDocs:
url: https://www.example.com
description: Documentación extendida de la API
components
Contiene un conjunto de objetos reutilizables para diferentes aspectos de la especificación. Los objetos definidos dentro del objeto de componentes no tendrán efecto en la API a menos que se haga referencia a ellos explícitamente desde propiedades fuera del objeto de componentes.
Pueden definir cualquiera de los siguientes objetos: schemas, responses, parameters, examples, requestBodies, headers, securitySchemes, links, callbacks o pathItems.
Para ver cómo este campo en concreto nos va a ayudar a la hora de reutilización de objetos, vamos a ver un ejemplo muy claro relacionado con todos los anteriores.
Si recordamos el campo requestBody, teníamos un objeto mascota con una serie de parámetros; alguno de ellos como enfermedades, era un array de un objeto complejo con sus propios parámetros. Al ver el campo response, vemos que en el caso del '200' se nos devolvía un objeto mascota con los mismos campos que había en el RequestBody.
Si pensamos en la API completa con todas sus operaciones, este objeto mascota se podría estar repitiendo decenas de veces, con lo que ello implica de posibilidades de fallos a la hora de definirlo como, aún más, si hay necesidad de realizar alguna modificación posterior.
Para evitar este problema, definiremos el objeto Mascota dentro del campo components de la siguiente manera:
components:
schemas:
Mascota:
title: Objeto mascota
required:
- nombre
properties:
nombre:
type: string
maxLength: 35
description: Nombre de la mascota
examples:
- Hulk
vacunada:
type: boolean
description: Indica si la mascota está vacunada
examples:
- true
enfermedades:
type: array
items:
$ref: '#/components/schemas/Enfermedad'
Enfermedad:
title: Objeto enfermedad
properties:
nombre:
type: string
description: Nombre de la enfermedad
examples:
- Leishmania
criticidad:
type: number
minimum: 1
maximum: 5
description: Criticidad de la enfermdad (1 - Baja, 5 - Muy alta)
examples:
- 2
Como vemos en el código anterior, no solo hemos creado un objeto Mascota, si no que también hemos creado un objeto Enfermedad al que se hace referencia desde el parámetro 'enfermedades'. De esta forma, tanto el objeto Mascota como el objeto Enfermedad, se puede referenciar desde cualquier parte de la especificación.
Volviendo a la requestBody y la response, quedaría de la siguiente forma:
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Mascota'
responses:
'200':
description: OK Los datos actualizados de la mascota serán devueltos
content:
application/json:
schema:
$ref: '#/components/schemas/Mascota'
Queda claro que la definición de componentes reutilizables es una ventaja para evitar muchos errores además de para tener una especificación mucho más clara y organizada.
Además, existen algunas claves como anyOf, oneOf, allOf que permite combinar el uso de esquemas para crear esquemas más complejos o validar contra múltiples criterios.
Extensiones de la especificación
Si bien la especificación OpenAPI intenta adaptarse a la mayoría de los casos de uso, se pueden agregar datos adicionales para ampliar la especificación en ciertos puntos. Se pueden definir prácticamente a nivel de cualquier campo.
Las propiedades de extensión se implementan como campos con patrones que siempre tienen como prefijo "x-". Su valor puede ser null, un primitivo, un array o un objeto.
Aquellos campos que se inician con x-wso2- son campos que el propio API Manager usa para ampliar configuraciones de la API. Aunque se pueden añadir a la especificación, también se pueden añadir a un fichero de parametrización que explicaremos más adelante; por lo que no se añadirán a nivel de especificación.
Un caso muy concreto de uso de extensiones, y que nos va a resultar realmente útil, es el caso de querer definir mocks de las operaciones; para ello, definiremos el campo x-mediation-script dentro de cada operación.
Este campo contiene un script en el que se puede escribir una respuesta tanto en json como xml para cada uno de los códigos de respuesta establecidos. A continuación, se muestra un ejemplo para una operación GET.
x-mediation-script: |-
var accept = mc.getProperty('AcceptHeader');
var responseCode = mc.getProperty('query.param.responseCode');
var responseCodeSC;
var responses = [];
if (!responses[200]) {
responses[200] = [];
}
responses[200]["application/json"] = {"code" : 200, "description" : " Success "};
responses[501] = [];
responses[501]["application/json"] = {"code" : 501, "description" : "Not Implemented"}
if (responseCode == null) {
responseCode = 200;
}
if (!responses[responseCode]) {
if (responses["default"]) {
responseCode = "default"
} else {
responseCode = 501;
}
}
if (responseCode === "default") {
responseCodeSC = mc.getProperty('query.param.responseCode');
} else {
responseCodeSC = responseCode;
}
if (accept == null || !responses[responseCode][accept]) { accept = "application/json"; }
if (accept == "application/json") {
mc.setProperty('CONTENT_TYPE', 'application/json');
mc.setProperty('HTTP_SC', responseCodeSC + "");
mc.setPayloadJSON(responses[responseCode]["application/json"]);
} else if (accept == "application/xml") {
mc.setProperty('CONTENT_TYPE', 'application/xml');
mc.setProperty('HTTP_SC', responseCodeSC + "");
mc.setPayloadXML(responses[responseCode]["application/xml"]);
}
Especificación API asíncrona
Una API asíncrona permite que las operaciones se realicen de manera independiente, sin requerir que el cliente espere a que el servidor complete la tarea antes de continuar. En lugar de recibir una respuesta inmediata, el cliente puede recibir una confirmación de que la solicitud ha sido aceptada y luego recibir el resultado de la operación más tarde, una vez que esté listo.
Siguiendo el ejemplo de la tienda de mascotas, cuando un usuario encarga la preparación de un pedido complejo, como por ejemplo con productos personalizados (nombre de la mascota, teléfono del dueño, etc), con una API asíncrona, cuando el cliente realiza el pedido, recibe una confirmación de que su solicitud ha sido aceptada. Mientras se procesa el pedido (preparando los productos, empaquetándolos, etc.), el cliente puede continuar navegando o salir de la web o de la aplicación y realizar otras acciones. Una vez que el pedido esté listo, la API puede notificar al cliente (por ejemplo, enviando un correo electrónico o actualizando el estado del pedido en la aplicación) de que su pedido ha sido completado y está listo para el envío.
Otro ejemplo, que será el que se use para explicar los distintos campos, sería que esta tienda también tuviera mascotas para adoptar y avisará a los potenciales clientes según sus preferencias, cuando una nueva mascota está disponible para su adopción.
Como recordatorio, la especificación AsyncAPI está diseñada para describir cómo deben comportarse las APIs basadas en eventos, especialmente en arquitecturas de microservicios y sistemas distribuidos.
Las especificaciones AsyncAPI serán escritas en formato yaml y se componen de varios campos “raíz” que detallan la estructura y el comportamiento de los eventos y mensajes, que son:
- asyncapi: indica la versión de AsyncAPI utilizada. (3.0.0)
- id: identificador de la aplicación para la que se está definiendo el documento AsyncAPI.
- info: proporciona una descripción completa de la API, ayudando a los desarrolladores, arquitectos y otras partes interesadas para comprender rápidamente su propósito y capacidades. Como elemento obligatorio de la especificación de Asyncapi, a menudo sirve como punto de referencia inicial para los usuarios que navegan por la documentación de la API
- defaultContentType: content type por defecto a usar cuando se codifica/decodifica el payload de un mensaje.
- servers: permite detallar una lista de servidores, describiendo los endpoints o los brokers de mensajería a los que las aplicaciones pueden conectarse. Ese campo incluye información vital sobra la conexión como protocolo, host, puerto y otras opciones, facilitando la conectividad en varios entornos, como producción, validación o desarrollo.
- channels: sirve para proporcionar un mapa de los diferentes canales con los que se comunica la aplicación durante el tiempo de ejecución. Los canales representan las vías de comunicación a través de las cuales se intercambian los mensajes. Puede especificar su propósito, dirección y los formatos de mensaje esperados para la comunicación. Los consumidores de la API específica pueden comprender las interacciones basadas en mensajes compatibles y los modelos de datos correspondientes.
- operations: se utiliza para describir de manera integral las diversas operaciones realizadas por la aplicación. Ofrece una descripción clara y estructurada, que detalla si la aplicación envía o recibe mensajes y el propósito específico de cada operación.
- components: permite la definición de estructuras o definiciones reutilizables aplicables en varias secciones de la especificación. Los elementos detallados dentro de los componentes solo se convierten en parte de la API cuando se hace referencia explícitamente por las propiedades externas a este campo. Se usa para evitar la repetición y mejorar la capacidad de mantenimiento de la especificación.
- A continuación, se detallaránr los campos que contienen cada uno de estos campos raíz, su definición y posibles valores. Para facilitar la comprensión de cada uno de estos campos “raíz”, se mostrará un ejemplo de cada uno de ellos por separado.
asyncapi
Contiene el número de versión de AsyncAPI que se está usando en la especificación de la API asíncrona.
Actualmente, la última versión es la 3.0.0.
asyncapi: 3.0.0
id
Representa un identificador único de la aplicación para la que se está definiendo la especificación.
Se recomienda utilizar una URN para identificar de forma global y única la aplicación durante largos períodos de tiempo, incluso después de que no esté disponible o deje de existir.
id: 'https://github.com/petshop/petshop-shopping-cart-server'
info
Define la información general sobre la API para ayudar a un entendimiento generalizado de la misma.
Contiene los siguientes campos:
- title: título o nombre de la API.
- version: versión de la API para la que se realiza la especificación.
- description: breve descripción indicando el propósito y características de la API. Se puede usar formato Markdown.
- termsOfService: URL o documento especificando los términos de uso de la API.
- contact: información de contacto del propietario de la API o persona que la mantenga (name, email, y url).
- license: información de la licencia de la API, incluyendo los campos name y url.
- tags: listado de etiquetas para categorizar y organizar la documentación de la API. También se usa como agrupación lógica de aplicaciones.
- externalDocs: enlaces documentación externa relacionada con la API.
info:
title: API Asíncrona de la tienda de mascotas
version: 1.0.0
description: Esta API permite a los usuarios recibir actualizaciones cuando hay nuevas mascotas disponibles para adopción.
termsOfService: https://example.com/terms-of-service
contact:
name: Rohit
email: rohitwashere@asyncapi.com
license:
name: Apache 2.0
url: https://www.apache.org/licenses/LICENSE-2.0.html
tags:
- name: adoptar
description: APIs relacionadas con adopciones
- name: mascota
description: APIs relacionadas con mascotas
externalDocs:
description: Documentación adicional
url: https://example.com/docs
defaultContentType
Cadena que representa el tipo de contenido predeterminado que se utilizará al codificar o decodificar el payload de un mensaje. El valor debe ser un media type específico (por ejemplo, aplicación/json). Los analizadores de esquema deben utilizar este valor cuando se omite la propiedad contentType.
defaultContentType: 'application/json'
servers
Define los diferentes servidores donde se despliega la API, con detalles como protocolo de conexión, esquema de seguridad, nombre del host, …
El primer elemento de la especificación de cada uno de los servidores es el nombre del servidor en la especificación (es un nombre descriptivo, no el propio nombre del host), que contiene los siguientes campos:
- host: nombre del host. Puede incluir el puerto, separado por dos puntos.
- protocol: protocolo o protocolo de mensajería usado por el servidor (AMQP, MQTT, WebSocket, …).
- protocolVersion: version del protocol usado para la conexión.
- pathname: path al recurso en el host.
- description: descripción del servidor.
- title: nombre descriptivo del servidor. Por ejemplo, si el servidor es production, el title puede ser Servidor de Producción.
- summary: resumen o explicación del uso del servidor.
- variables: mapa de variables utilizadas en la URL del servidor. Cada variable es definida con su valor por defecto y una posible lista de valores para la sustitución.
- security: declaración de qué esquemas de seguridad pueden ser usado con este servidor.
- tags: listado de tags para agrupar de manera lógica o categorizar a los servidores.
- externalDocs: documentación externa adicional.
- bindings: listado de campos que definen configuraciones específicas del protocolo para cada servidor. Estas configuraciones proporcionan detalles adicionales y específicos del protocolo que no se pueden cubrir con los campos estándar de AsyncAPI.
servers:
produccion:
host: kafka.petstore.com:{port}
pathname: /v1
protocol: kafka
protocolVersion: "1.0"
description: Servidor Kafka de producción.
title: Servidor Kafka Producción
summary: Servidor para el entorno de producción
variables:
port:
default: "8092"
description: Puerto para conectarse
enum:
- "8092"
security:
- type: http
scheme: bearer
tags:
- name: env:prod
description: Este entorno está diseñado como entorno productivo
externalDocs:
description: Documentación adicional para el servidor de producción
url: https://example.com/docs/production
bindings:
kafka:
exchange: mi-exchange
queue: mi-cola
channels
Define los diferentes canales a través de los cuales se envían y reciben mensajes. Cada canal representa un endpoint de comunicación al que una aplicación puede suscribirse o publicar mensajes.
De forma similar al campo servers, el campo channels define una lista de canales; por lo que el primer elemento será el nombre del canal y contendrá los siguientes campos:
- address: cadena de texto que representa la dirección del canal.
- messages: conjunto de los mensajes que se podrán enviar al canal desde cualquier aplicación en cualquier momento.
- title: título legible para el canal
- summary: resumen corto pero breve del canal.
- description: descripción del canal, que proporciona contexto adicional y detalles del mensaje.
- servers: array de punteros $ref a la definición de los servidores en los que el canal está disponible. Si este campo no se define o está vacío, implica que el canal está disponible en todos los servidores definidos.
- parameters: conjunto de parámetros incluidos en la dirección del canal.
- tags: listado de etiquetas para la agrupación lógica de canales.
- externalDocs: documentación externa adicional para el canal.
- bindings: listado de campos que definen configuraciones específicas del protocolo para cada canal. Estas configuraciones proporcionan detalles adicionales y específicos del canal que no se pueden cubrir con los campos estándar de AsyncAPI.
channels:
disponibilidadMascota:
address: mascota/disponibilidad/{especie}
title: Canal de envío de notificaciones
description: Canal donde se envían notificaciones sobre nuevas mascotas disponibles para adopción.
messages:
recibirNuevaMascota.mensaje:
$ref: '#/components/messages/recibirNuevaMascota'
parameters:
specie:
$ref: '#/components/parameters/especie'
servers:
- $ref: '#/servers/produccion'
tags:
- name: mascota
description: Mensajes relacionados con mascotas
externalDocs:
description: 'Más información'
url: 'https://example.com'
El campo messages puede incluir referencias o definir directamente el objeto, por ejemplo:
recibirNuevaMascota.mensaje:
contentType: application/json
payload:
type: object
properties:
id:
type: string
description: Identificador único de la mascota.
nombre:
type: string
description: Nombre de la mascota.
especie:
type: string
description: 'Especie de mascota (perro, gato, etc.).'
raza:
type: string
description: Raza de la mascota.
edad:
type: integer
description: Edad de la mascota en años.
example:
id: '1234'
nombre: Hulk
especie: perro
raza: Malinoise
edad: 3
No obstante, por cuestiones de mantenibilidad y reutilización, es recomendable definir los componentes en la sección indicada para ellos y referenciarlos donde se usen.
operations
Definen las acciones (operaciones) de envío o recepción que se pueden realizar en un canal específico.
De forma similar a los campos anteriores, el campo operations define una lista de operaciones; por lo que el primer elemento será el nombre de la operación y contendrá los siguientes campos:
- action: los posibles valores son send y receive. Usa send cuando se espera que la aplicación envíe un mensaje al canal indicado, y receive cuando la aplicación deba esperar a recibir un mensaje del canal indicado.
- channel: puntero $ref a la definición del canal en el que se realiza esta operación.
- title: título amigable para la operación.
- summary: breve resumen de lo que trata la operación.
- description: explicación detallada del funcionamiento.
- security: declaración de qué esquemas de seguridad se asocian a la operación.
- tags: lista de etiquetas para agrupación lógica y categorización de operaciones.
- externalDocs: documentación externa adicional para la operación.
- bindings listado de campos que definen configuraciones específicas del protocolo para cada operación. Estas configuraciones proporcionan detalles adicionales y específicos del canal que no se pueden cubrir con los campos estándar de AsyncAPI.
- traits: lista de características que se aplicarán al objeto de operación.
- messages: lista de punteros $ref a los objetos de mensaje admitidos que pueden procesarse mediante esta operación. También se puede definir el mensaje en este campo, pero no se recomienda por cuestiones de mantenibilidad.
- reply: definición de la respuesta en una operación de respuesta/solicitud. La respuesta puede llegar por un canal y/o por una dirección. También se puede añadir la definición del mensaje de respuesta.
operations:
recibirNuevaMascota:
action: send
title: Recepción de mascota
summary: Envía una notificación cuando se recibe una nueva mascota en la tienda
description: Envío de notificaciones de adopción
channel:
$ref: '#/channels/disponibilidadMascota'
tags:
- name: mascota
- name: adopcion
bindings:
kafka:
ack: false
traits:
- $ref: '#/components/operationTraits/kafka'
messages:
- $ref: '#/channels/disponibilidadMascota/messages/recibirNuevaMascota.mensaje'
components
Define objetos reutilizables que pueden ser referenciados en otros lugares de la especificación, permitiendo centralizar definiciones comunes y evitar duplicaciones, facilitando el mantenimiento y la consistencia de la API. Los objetos definidos dentro del objeto de componentes no tendrán efecto en la API a menos que se haga referencia a ellos explícitamente desde propiedades fuera del objeto de componentes.
Pueden definir cualquiera de los siguientes objetos: schemas, servers, channels, operations, messages, securitySchemes, serverVariables, parameters, correlationIds, replies, replyAddresses, externalDocs, tags, operationTraits, messageTraits, serverBindings, channelBindings, operationBindings o messageBindings.
Extensiones de la especificación
Si bien la especificación AsyncAPI intenta adaptarse a la mayoría de los casos de uso, se pueden agregar datos adicionales para ampliar la especificación en ciertos puntos. Se pueden definir prácticamente a nivel de cualquier campo.
Las propiedades de extensión se implementan como campos con patrones que siempre tienen como prefijo "x-". Su valor puede ser
x-addedInVersion: '2'
x-groupName: Transfers
x-methodName: transferFunds
x-sortIndex: 1
Generar documentación adicional
Por otro lado, aunque la propia especificación de la API ya es una documentación de esta, la inclusión de otros tipos de documentación ayuda a los desarrolladores a entender y usar la API de manera clara y efectiva. La documentación puede incluir guías prácticas, tutoriales, ejemplos de uso más detallados, foros de discusión y enlaces a web.
La documentación de la API puede tener el siguiente contenido:
- Guías de inicio rápido.
- Tutoriales paso a paso.
- Casos de uso y ejemplos prácticos.
- Preguntas frecuentes (FAQs).
- Buenas prácticas y recomendaciones.
- Información sobre el ciclo de vida de las versiones.
- Recursos adicionales como enlaces a foros, comunidades de usuarios, etc.
La documentación suele estar escrita en un formato menos técnico para que cualquier persona pueda entender claramente el objetivo de la API y sus distintas funciones y así comprobar si es válida para su negocio, aunque en algunos casos incluya códigos de ejemplo para el desarrollador.
Los tipos de fuentes soportados por la plataforma de gestión de APIs son:
- Inline: documentación con formato de etiquetas similar a HTML que permite ser editada a través de la interfaz de usuario del API Publisher.
- Markdown: documentación en lenguaje markdown que permite ser editada a través de la interfaz de usuario del API Publisher.
- URL: enlaces de foros públicos o de soporte, webs de documentación o referencias de archivos (URL) de un sistema de gestión de configuración externo.
- File: documentos propios en distintos formatos. Los tipos de ficheros que permite ser cargados se definen en el fichero de configuración deployment.toml. Por defecto, los tipos permitidos son: pdf, txt, doc, docx, xls, xlsx, odt, ods, json, yaml y md
En el caso de crear documentación con formato (Inline y Markdown), se recomienda el uso de Markdown porque, en general, la sintaxis para estos tipos de documentación es fácil de aprender y tiene una documentación más amplia con ejemplos. Además, cualquier herramienta online de edición, muestra el texto resultado de forma online.
A continuación, mediante una tabla, se indica los distintos tipos de documentos que se pueden añadir y qué tipos de fuente de las anteriores se pueden usar.
Tipo de documento | Fuentes soportadas |
---|---|
How To | Inline, Markdown, URL, File |
Sample and SDK | Inline, Markdown, URL, File |
Public Forum | URL |
Support Forum | URL |
Other (hay que indicar qué tipo de documentación será) | Inline, Markdown, URL, File |
Se recomienda añadir al menos documentación de tipo How To y Samples and SDK, ya sea en formato Markdown o a través de documentos pdf. Se deja al creador de la API la responsabilidad de añadir alguna otra documentación que pueda ayudar a entender mejor la API o aporte información adicional, o enlaces a foros y webs.
NOTA: Para añadir esta documentación, habrá que crear una estructura de ficheros que se explicará más adelante. (Ver Más)
A continuación, se indicará qué debe contener un documento de tipo How To y un documento de tipo Sample and SDK y se mostrará un ejemplo con lenguaje Markdown para poner de manifiesto su sencillez y legibilidad.
Documento How To
Un documento de tipo "how to" debe ser claro, conciso y bien estructurado para guiar a los usuarios a través de los pasos necesarios para interactuar con la API. A continuación, se describen los apartados esenciales que debe incluir:
- Introducción
- Descripción: Breve descripción de la API y su propósito.
- Requisitos previos: Cualquier información preliminar que los usuarios necesiten conocer o tener (como una API key, herramientas específicas, etc.).
- Autenticación
- Método de autenticación: Descripción de cómo autenticar las solicitudes (por ejemplo, usando OAuth2, API keys, etc.).
- Ejemplos de encabezados de autenticación: Mostrar ejemplos de cómo incluir la información de autenticación en las solicitudes.
- Descripción de Endpoints
- Lista de Endpoints: Lista de todos los endpoints disponibles con una breve descripción de cada uno.
- Formato de URLs: Ejemplos de cómo estructurar las URLs para los diferentes endpoints.
- Métodos HTTP
- Métodos soportados: Explicar qué métodos HTTP son soportados por cada endpoint (GET, POST, PUT, DELETE, etc.).
- Ejemplos de Solicitudes y Respuestas
- Solicitudes: Ejemplos de solicitudes HTTP para cada endpoint, incluyendo encabezados, parámetros de URL, y cuerpos de solicitud si son necesarios.
- Respuestas: Ejemplos de respuestas HTTP esperadas, incluyendo códigos de estado y cuerpos de respuesta.
- Manejo de Errores
- Códigos de estado de errores: Lista de códigos de estado de errores comunes y su significado.
- Mensajes de error: Ejemplos de mensajes de error y cómo interpretarlos.
- Casos de Uso Comunes
- Escenarios típicos: Describir escenarios comunes en los que se utilizaría la API y cómo implementarlos.
- Ejemplos prácticos: Proveer ejemplos prácticos y detallados de solicitudes y respuestas para estos casos de uso.
- Buenas Prácticas
- Recomendaciones: Consejos y mejores prácticas para el uso de la API, incluyendo consideraciones de seguridad y optimización de rendimiento.
- Referencias y Recursos Adicionales
- Documentación adicional: Enlaces a documentación más detallada, tutoriales, y otros recursos útiles.
- Contacto de soporte: Información de contacto para soporte técnico o preguntas adicionales.
A continuación, se muestra un ejemplo de documentación How To
## Introducción
Bienvenido a la API de Gestión de Tiendas de Mascotas. Esta guía le mostrará cómo interactuar con la API para gestionar tiendas de mascotas.
### Requisitos previos
- API Key
- Herramienta para realizar solicitudes HTTP (cURL, Postman, etc.)
## Autenticación
Use el siguiente encabezado para autenticar sus solicitudes:
Authorization: Bearer YOUR_API_KEY
## Endpoints
### Crear una mascota
**POST /mascotas**
#### Solicitud
```json
{
"nombre": "hulk",
"especie": "dog",
"raza": "malinoise",
"sexo": "male"
}
```
#### Respuesta
```json
{
"id": "abc123",
"nombre": "hulk",
"fecha_creada": "2024-01-01T12:00:00Z"
}
```
### Obtener una Mascota por ID
**GET /mascotas/{id}**
#### Solicitud
GET /mascotas/abc123
#### Respuesta
```json
{
"id": "abc123",
"nombre": "hulk",
"especie": "dog",
"sexo": "male",
"fecha_creacion": "2024-01-01T12:00:00Z"
}
```
## Manejo de Errores
### Códigos de estado comunes
- `400 Bad Request`: La solicitud no pudo ser entendida o faltan parámetros.
- `401 Unauthorized`: Autenticación fallida.
- `404 Not Found`: Recurso no encontrado.
- `500 Internal Server Error`: Error en el servidor.
## Buenas Prácticas
- **Seguridad**: Asegúrese de mantener su API Key segura y no compartirla.
- **Optimización**: Evite hacer solicitudes innecesarias para mejorar el rendimiento.
## Referencias y Recursos Adicionales
- Documentación Completa
- Soporte: support@example.com
Este documento cubre todos los elementos necesarios para guiar a los usuarios en la utilización de la API, asegurando que puedan entender y aprovechar al máximo sus funcionalidades.
Documentación de ejemplos y SDK
Un documento de tipo "Sample and SDK" para el uso de una API debe proporcionar ejemplos prácticos y código que los desarrolladores puedan usar directamente para integrar y utilizar la API en sus aplicaciones. Este documento debe ser lo suficientemente detallado y claro para permitir que los desarrolladores comprendan rápidamente cómo funciona la API y cómo pueden implementarla en sus proyectos. Aquí están los componentes esenciales que debe incluir:
- Introducción
- Descripción: Breve descripción de la API y su propósito.
- Objetivo del documento: Explicación de lo que los usuarios encontrarán en el documento y cómo les será útil.
- Configuración Inicial
- Requisitos previos: Herramientas y dependencias necesarias para usar la API (por ejemplo, SDK específicos, lenguajes de programación, entornos de desarrollo).
- Instalación: Instrucciones paso a paso sobre cómo instalar el SDK o cualquier otra herramienta necesaria.
- Autenticación
- Métodos de autenticación: Descripción de cómo autenticar las solicitudes (por ejemplo, usando API keys, OAuth, JWT, etc.).
- Ejemplos de autenticación: Código de muestra para configurar la autenticación.
- Ejemplos de Uso (Samples)
- Ejemplos básicos: Código de muestra para realizar operaciones básicas con la API.
- Ejemplos avanzados: Código de muestra para operaciones más complejas.
- Explicación del código: Descripción detallada de cada fragmento de código, explicando qué hace y por qué es importante.
- Documentación del SDK
- Métodos y funciones disponibles: Lista y descripción de los métodos y funciones proporcionados por el SDK.
- Parámetros: Descripción de los parámetros que se deben pasar a cada método.
- Ejemplos de respuestas: Ejemplos de las respuestas que se pueden esperar de la API.
- Manejo de Errores
- Errores comunes: Lista de errores comunes que los desarrolladores pueden encontrar y cómo resolverlos.
- Manejo de excepciones: Ejemplos de cómo manejar errores y excepciones en el código.
- Prácticas Recomendadas
- Consejos y trucos: Mejores prácticas para utilizar la API de manera eficiente y segura.
- Consideraciones de rendimiento: Sugerencias para optimizar el uso de la API.
- Referencias y Recursos Adicionales
- Enlaces útiles: Enlaces a documentación adicional, foros, y otros recursos de soporte.
- Contacto de soporte: Información de contacto para asistencia técnica.
A continuación, se muestra un ejemplo de Ejemplos y SDK
# API de Gestión de Tienda de Mascotas – Guía How To
# API de Gestión de Tiendas de Mascotas – Guía Sample & SDK
## Introducción
Este documento proporciona ejemplos prácticos y el SDK necesario para integrar la API de gestión de tiendas de mascotas en su aplicación.
### Objetivo del documento
Proveer ejemplos y herramientas para ayudar a los desarrolladores a integrar y utilizar la API rápidamente.
## Configuración Inicial
### Requisitos previos
- API Key
- Node.js instalado
- Biblioteca Axios para realizar solicitudes HTTP
### Instalación
Instale Axios ejecutando el siguiente comando:
```bash
npm install axios
```
## Autenticación
Use el siguiente código para autenticar sus solicitudes:
```
const axios = require('axios');
const instance = axios.create({
baseURL: 'https://api.example.com/',
headers: {'Authorization': 'Bearer YOUR_API_KEY'}
});
```
## Ejemplos de Uso (Samples)
### Crear una mascota
```
instance.post('/mascotas', {
nombre: 'hulk',
especie: 'dog',
sexo: 'male'
})
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error(error);
});
```
### Obtener una Mascota por ID
```
instance.get('/mascotas/abc123')
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error(error);
});
```
## Documentación del SDK
### Métodos disponibles
- `addMascota(data)`: Añade una nueva mascota.
- `getMascotaById(id)`: Obtiene una mascota por su ID.
### Parámetros
- `addMascota(data)`: `data` es un objeto que contiene `nombre`, `edad`, y `sexo`.
- `getMascotaById(id)`: `id` es el identificador de la mascota.
### Ejemplos de respuestas
```json
{
"id": "abc123",
"nombre": "hulk",
"especie": "dog",
"sexo": "male",
"fecha_creacion": "2024-01-01T12:00:00Z"
}
```
## Manejo de Errores
### Errores comunes
- `400 Bad Request`: Solicitud malformada.
- `401 Unauthorized`: Fallo de autenticación.
- `404 Not Found`: Mascota no encontrada.
### Manejo de excepciones
```
instance.post('/mascotas', { /* data */ })
.catch(error => {
if (error.response) {
console.error('Error:', error.response.data);
} else {
console.error('Error:', error.message);
}
});
```
## Prácticas Recomendadas
- **Seguridad**: No exponga su API Key en el código fuente.
- **Optimización**: Use la paginación para manejar grandes volúmenes de datos.
## Referencias y Recursos Adicionales
- Documentación Completa
- Soporte: support@example.com
Crear una colección de pruebas
De cara a la validación de la especificación realizada, se debe realizar una colección de pruebas que validen todas las rutas y operaciones definidas.
En el momento de creación de la especificación, estas pruebas se pueden usar para validar la estructura y configuración de los campos, así como códigos de respuesta. Pero, una vez implementado el código, se pueden actualizar añadiendo validaciones más reales en función de la lógica del backend.
Lo más importante de estas pruebas es que se podrán configurar para lanzar de manera automática cada vez que haya un cambio en la especificación. De esta forma, podremos ver si, ante un cambio realizado, podemos estar afectando a clientes consumidores de la API.
Los pasos para generar una colección de pruebas en Postman son los siguientes:
- Crear la especificación de la API
- Especificación creada anteriormente
- Crear la colección de Postman
- Importar la especificación anterior
- Revisar las peticiones para ver que corresponden todas con la especificación
- Añadir pruebas a las peticiones de Postman
- Para cada petición, añade pruebas en la pestaña "Tests" en Postman. Aquí hay algunos ejemplos de pruebas para algunas peticiones:
GET /mascotas
pm.test("La respuesta tiene un estado 200", function () {
pm.response.to.have.status(200);
});
pm.test("La respuesta es un array", function () {
pm.expect(pm.response.json()).to.be.an('array');
});
POST /mascotas
pm.test("La respuesta tiene un estado 201", function () {
pm.response.to.have.status(201);
});
pm.test("El nombre de la mascota es Hulk", function () {
var jsonData = pm.response.json();
pm.expect(jsonData.nombre).to.eql("Hulk");
});
GET /mascotas/{id}
pm.test("La respuesta tiene un estado 200", function () {
pm.response.to.have.status(200);
});
pm.test("La mascota tiene el ID correcto", function () {
var jsonData = pm.response.json();
pm.expect(jsonData.id).to.eql(pm.variables.get("id"));
});
DELETE /mascotas/{id}
pm.test("La respuesta tiene un estado 200", function () {
pm.response.to.have.status(200);
});
pm.test("El cuerpo de la respuesta está vacío", function () {
pm.expect(pm.response.text()).to.be.empty;
});
- Exportar la colección de Postman
- Exportar la colección en formato json y guardarla en la carpeta UnitTest
Publicar una API
Para publicar una API, es necesario realizar una serie de configuraciones.
Por un lado, y solo en el caso de haber generado documentación adicional, será necesario añadir dicha documentación a la estructura de carpetas de la forma adecuada y crear un fichero de configuración por cada una de ellas.
Por otro lado, y de manera obligatoria, será necesario modificar el fichero de parametrización api.yaml que contiene configuraciones de la API a nivel de la plataforma API Manager WSO2.
A continuación, se explicará cómo realizar cada una de estas configuraciones.
Configuración de los ficheros de documentación
Los ficheros de configuración de documentos sirven para añadir la documentación que se haya generado en el apartado Documentación a la API en el momento de su publicación.
Para añadirlos, dentro de la carpeta Docs del proyecto de la API, hay que crear una carpeta por cada uno de los documentos, cuyo nombre no podrá contener espacios ni caracteres especiales (API_Forum, HowTo, Samples, …).
Dentro de cada una de estas carpetas, habrá que crear un fichero llamado document.yaml, que contendrá la configuración y el documento que se vaya a añadir.
En función de la tipología de documento y el tipo de fuente, varía el contenido del documento y el nombre que le tenemos que dar al documento. A continuación, veremos varios ejemplos en los que se muestran distintos tipos de documentación a adjuntar.
Doc1: tipo SAMPLE con pdf externo como fuente | Doc2: tipo SUPPORT FORUM con url como fuente |
type: document data: name: API_SDK type: SAMPLES sourceType: FILE fileName: file.pdf summary: Ejemplos avanzados de uso de la API visibility: API_LEVEL | type: document data: name: Api_forum type: SUPPORT_FORUM sourceType: URL sourceUrl: http://forum.exampl.com summary: Foro de la API visibility: API_LEVEL |
Doc3: tipo OTHER a partir de Markdown | Doc4: tipo HOWTO a partir de Inline |
type: document data: name: API_Overview type: OTHER otherTypeName: Overview sourceType: MARKDOWN summary: Visión general de la API visibility: API_LEVEL | type: document data: name: GettingStarted type: HOWTO summary: Como usar la API sourceType: INLINE visibility: API_LEVEL |
Detalle de los campos:
- name: El nombre con el que se va a mostrar la documentación (sin espacios ni caracteres especiales).
- type: El tipo de documentación. Puede ser HOWTO, SAMPLES, PUBLIC_FORUM, SUPPORT_FORUM o OTHER.
- otherTypeName: Si type es OTHER, hay que indicar el tipo de documentación que se va a cargar (texto libre).
- summary: Un resumen breve de lo que contiene la documentación.
- sourceType: La fuente del contenido. Puede ser FILE, INLINE, MARKDOWN, o URL.
- sourceUrl: La URL si el sourceType es URL.
- fileName: El nombre del fichero si el sourceType es FILE.
- visibility: Quién puede ver el documento:
- API_LEVEL. Cualquiera que pueda acceder a la API
- PRIVATE. Solo usuarios autenticados que puedan acceder a la API
- OWNER_ONLY. Solo para el propietario de la API
Según los ejemplos mostrados en la tabla anterior, con cuatro tipos de documentación añadidos, la estructura de la carpeta Docs quedaría de la siguiente forma:
Si nos fijamos en los nombres de los documentos cargados, cuando el tipo es Inline o es Markdown, el nombre del documento y el de la carpeta deben coincidir con el atributo name. Mientras que si el tipo es File, hay que indicar en el atributo fileName el nombre del documento a añadir, que se incluye en la misma carpeta que el fichero de configuración.
Configuración del fichero de parametrización
El fichero de parametrización se utiliza, para para definir y configurar las distintas características de la API que se va a publicar en el API Publisher, así como para indicar su comportamiento y otras características.
A continuación, se describirán y detallarán los parámetros principales que se pueden definir en el fichero, así como los posibles subparámetros asociados a cada uno de ellos.
Obligatoriedad de campos:
- Los parámetros sombreados en rojo deben estar presentes y rellenos. Ejemplo: type
- Los parámetros sombreados en naranja, aun no siendo obligatorios para la publicación, en el documento de normas y estándares se indican como obligatorios. Ejemplo: businessInformation
- los parámetros sombreados en azul son recomendados desde la Oficina de Arquitectura para realizar una publicación más completa. Ejemplo: summary
Parámetros raíz
- type: tipo de recurso que se está describiendo en el archivo de configuración (api)
- version: versión del WSO2 API Manager utilizada para esta definición. (v4.3.0)
- data: contiene todos los parámetros que sirven para publicar la API.
A continuación, se mostrarán todos los parámetros que el parámetro raíz data contiene, de manera organizada. En primer lugar, se mostrarán los parámetros necesarios para publicar la API. En segundo lugar, se mostrarán el resto de los parámetros. Para hacer más intuitiva su organización, se hará uso de la nomenclatura de secciones del API Publisher, ya que todos estos parámetros se ven reflejados en las distintas pantallas de configuración.
Parámetros generales
- name: nombre de la API para identificarla dentro del API Manager. Es una cadena de caracteres alfanuméricos, no se permitirá el uso de caracteres especiales ni espacios.
- context: ruta base de la API que se utiliza para construir la URL bajo la cual la API estará disponible. Es una cadena de caracteres alfanuméricos, se debe evitar el uso de caracteres especiales y no se permite el uso de espacios.
- version: versión específica de la API según el formato SemVer (X.Y.Z), que permite la gestión de múltiples versiones.
- provider: proveedor o creador de la API, un usuario responsable de su gestión. Este usuario no tiene por qué estar definido dentro de nuestro API Manager. Cadena de caracteres, evitar el uso de caracteres numéricos y no se permitirá el uso de caracteres especiales.
- organizationId: identificador de la organización en el entorno de WSO2 (carbon.super).
- type: Tipo de API que se publica (HTTP/WS/GRPC).
type: API
version: v4.3.0
data:
name: MascotasAPI
context: /mascotas
version: 1.0.0
provider: ADA
organizationId: carbon.super
type: HTTP
Configuraciones del portal
Información básica
- description: descripción detallada de la API, proporcionando información adicional sobre su propósito y uso.
- hasThumbnail: Indica si la API tiene una miniatura asociada. (true/false). Si se indica true, es necesario añadir el archivo de imagen a la carpeta Image (Ver estructura de API).
- accessControl: control de acceso aplicado a la API dentro del API Publisher. (NONE / RESTRICTED)
- accessControlRoles: roles que deben tener los usuarios que acceder para poder ver la API en el API Publisher. Solo se rellena si el parámetro anterior es RESTRICTED.
- visibility: visibilidad de la API en el API Portal. (PUBLIC/PRIVATE)
- visibleRoles: roles que tienen visibilidad sobre la API en el API Portal. Estos roles se aplicarán solo en el caso de que nuestra API no sea pública. Si la visibilidad es PRIVATE y no se definen roles, por defecto solo se permite ver a los usuarios del mismo dominio del owner de la API.
- tags: lista de etiquetas asociadas a la API, utilizadas para categorizar y buscar la API.
- categories: lista de categorías asociadas a la API para su organización. Solo se pueden usar categorías creadas.
- additionalProperties: en el caso de poder añadir una url del proyecto en git o slack, es necesario configurar este parámetro y el siguiente añadiendo las urls que se vayan a configurar (en el ejemplo podrá ver la configuración con urls de git y de slack).
- additionalPropertiesMap: igual que el anterior.
- advertiseInfo: indica si la API a publicar pertenece a un tercero.
- advertised: true o false. Si es true, la parametrización referente a Runtime, Endpoints y Policies no se rellenará puesto que estarán definidos en el proveedor.
- apiExternalProductionEndpoint: endpoint de producción de la API (obligatorio si advertised es true).
- apiExternalSandboxEndpoint: endpoint de test de la API (opcional si advertised es true)
- originalDevPortalUrl: url del portal del desarrollador original (opcional si advertised es true)
- apiOwner: dueño de la API
- vendor: proveedor
description: Esto es una API de ejemplo
hasThumbnail: false
organizationId: carbon.super
accessControl: RESTRICTED
accessControlRoles:
- internal/publisher
visibility: PRIVATE
visibleRoles: []
tags:
- mascotas
- gold
- silver
categories:
- Public
- ADA
additionalProperties:
-
name: slack_url
value: https://slack.com/MascotasApi
display: true
-
name: github_repo
value: https://github.com/MascotasApi
display: true
additionalPropertiesMap:
slack_url__display:
name: slack_url
value: https://slack.com/MascotasApi
display: false
github_repo__display:
name: github_repo
value: https://github.com/MascotasApi
display: false
advertiseInfo:
advertised: false
apiOwner: admin
vendor: WSO2
Información del negocio
- businessInformation: información de contacto que aparecerá en el API Portal
- bussinesOwner: Nombre del propietario.
- bussinesOwnerEmail: Email del propietario.
- technicalOwner: Nombre del responsable técnico.
- technicalOwnerEmail: Email del responsable técnico.
businessInformation:
businessOwner: Agencia Digital Andaluza
businessOwnerEmail: ada@juntadeandalucia.es
technicalOwner: Ada Technician
technicalOwnerEmail: technician.ada@juntadeandalucia.es
Suscripciones
- policies: lista de políticas de limitación de uso aplicadas a la API. Deben ser creadas previamente por un administrador.
policies:
- Silver
- Unlimited
Configuraciones de la API
Runtime
- transport: Capa de seguridad utilizada en los protocolos de transporte soportados por la API. Esta propiedad permite utilizar cualquier protocolo para dirigir información hacia nuestra API (http/https). La API se expondrá en los gateways según las opciones seleccionadas. Si se selecciona la opción https y el securityScheme mutualssl, se debe presentar un certificado de cliente confiable para acceder a la API.
- securityScheme: lista no excluyente de esquemas de seguridad aplicados a la API (oauth2, api_key)
- keyManagers: gestores de claves asociados a la API, que se pueden configurar dentro del Admin Portal, en caso de esto ser así podremos definir los key managers que serán utilizados en nuestra API utilizando el identificador de este. Por defecto: all.
- authorizationHeader: cabecera utilizada en las solicitudes donde se incluye la información de la autorización. Por defecto: Authorization.
- corsConfiguration: configuración CORS (Cross-Origin Resource Sharing) para la API.
- corsConfigurationEnabled: Indica si CORS está habilitado. (true/false)
- accessControlAllowOrigins: lista de orígenes permitidos para solicitudes CORS (‘*’).
- accessControlAllowCredentials: Indica si se permiten credenciales en solicitudes CORS (true/false)
- accessControlAllowHeaders: Encabezados permitidos en solicitudes CORS (authorization, Access-Control-Allow-Origin, Content-Type, SOAPAction, apikey, Internal-Key
- accessControlAllowMethods: Métodos HTTP permitidos en solicitudes CORS (GET, PUT, POST, DELETE, PATCH, OPTIONS).
- enableSchemaValidation: Indica si la validación de esquemas está habilitada para las solicitudes y respuestas de la API. (true/false)
- responseCachingEnabled: Indica si la caché de respuestas está habilitada. (true / false)
- cacheTimeout: tiempo de expiración de la caché, especificado en segundos. Si se activa la opción anterior, se aplicará el valor de este parámetro. Por defecto: 300.
- maxTps: limita la cantidad total de llamadas que el API Manager puede realizar al backend.
- production: cantidad de llamadas al endpoint de producción
- sandbox: cantidad de llamadas al endpoint de sandbox
transport:
- http
- https
securityScheme:
- oauth2
keyManagers:
- all
authorizationHeader: Authorization
corsConfiguration:
corsConfigurationEnabled: true
accessControlAllowOrigins:
- '*'
accessControlAllowCredentials: true
accessControlAllowHeaders:
- Authorization
- Access-Control-Allow-Origin
- Content-Type
accessControlAllowMethods:
- GET
- PUT
- OPTIONS
enableSchemaValidation: false
responseCachingEnabled: false
cacheTimeout: 300
maxTps:
production: 200
sandbox: 250
NOTA: Para que CORS se habilite correctamente tiene que estar previamente configurado dentro de la configuración de entorno del API Manager, concretamente en el archivo deployment.toml, en caso de que no esté configurado, se debe evitar utilizar estas propiedades ya que pueden generar fallos dentro de nuestro sistema.
Recursos
Operaciones disponibles para la API. Estas operaciones deben coincidir con las de la definición. Permiten configurar aspectos como políticas de limitación de peticiones (a nivel API u operación) y los scopes que se aplicarán a cada operación para controlar los permisos.
Si no se definen estos parámetros, el API Publisher asignará valores por defecto basados en la especificación. Por lo que si quisiéramos personalizar estos valores debemos asegurarnos de revisar y ajustarlos según sus necesidades para garantizar que la API funcione correctamente y cumpla con los requisitos específicos de su aplicación.
- apiThrottlingPolicy: política de throttling definida a nivel API. Invalida la que se defina a nivel operación.
- operations: listado de conjunto de operaciones
- target: path de la operación (el mismo que la especificación)
- verb: método HTTP utilizado para la operación (el mismo que la especificación)
- throttlingPolicy: política de throttling definida para la operación. Solo es efectiva si no está definido el parámetro apiThrottlingPolicy
- authType: habilita/deshabilita el nivel de seguridad definido en la sección Runtime. Posibles valores “Application & Application User” o None.
- scopes: lista de scopes que debe tener el usuario en el token para poder hacer uso de la operación. Solo se puede definir si está habilitado authType.
apiThrottlingPolicy: 20kPerMin
operations:
-
target: /mascotas/{id}
verb: GET
authType: Application & Application User
throttlingPolicy: Unlimited
scopes:
- petDeveloper
Endpoints
Para definir los endpoints, se pueden seguir dos enfoques. El primero consiste en conectar a un backend (tanto ya implementado como un creado mediante alguna herramienta online, como MockAPI). Alternativamente, es posible realizar un mock en el propio API Publisher a partir de este fichero de propiedades (también se puede configurar desde la versión web).
- endpointConfig: tipo de configuración de los endpoints de la API
- endpoint_type: Tipo de endpoint (http para rest y mock o address para soap).
- production_endpoints: Configuración para el entorno de producción.
- endpoint_type: address (solo si endpoint_type es address)
- template_not_supported: true/false (solo si endpoint_type es address)
- url: URL del endpoint de producción.
- sandbox_endpoints: Configuración para el entorno de pruebas.
- endpoint_type: address (solo si endpoint_type es address)
- template_not_supported: true/false (solo si endpoint_type es address)
- url: URL del endpoint.
- implementation_status: prototyped
- endpointImplementationtype: Definición del tipo de implementación del endpoint:
- ENDPOINT: apunta a una url de backend
- INLINE: configuración de un mock. Solo se puede poner este valor si está el parámetro implementation_status: prototyped. El mock se define en la especificación de la API mediante el campo x-mediation-script. Esta configuración la puede ver en la sección 'Extensiones de la especificación'.
Ejemplo backend REST | Ejemplo backend Mock |
---|---|
endpointConfig: endpoint_type: http sandbox_endpoints: url: https://localhost:9443/mascotasapi/v1/api production_endpoints: url: https://localhost:9143/mascotasapi/v1/api/ endpointImplementationType: ENDPOINT | endpointConfig: endpoint_type: http sandbox_endpoints: url: http://localhost production_endpoints: url: http://localhost implementation_status: prototyped endpointImplementationType: INLINE |
Local scopes
En esta sección, se mostrará la definición que se puede hacer de scopes a nivel local de la API. Esto sirve para crear scopes que solo podrán ser usados en esta API, no en las demás. Los usuarios que posean este scope podrán usar la API.
- scopes: listado de scopes a nivel local. Cada uno de ellos tendrán los siguientes parámetros:
- name: nombre del scope (sin espacios)
- displayName: nombre que se mostrará
- description: descripción del scope
- bindings: roles de usuarios que pueden obtener tokens con este scope.
- shared: indica que si es local (false) o global (true)
scopes:
-
scope:
name: petOwner
displayName: petOwner
description: Dueño de mascotas
bindings:
- internal/publisher
shared: false
Policies
Dentro del parámetro de definición de operaciones, existe otro para configurar las políticas relativas a la petición, a la respuesta o a los fallos.
Cada uno de estos parámetros es una lista de políticas definidas
- operationPolicies: políticas relativas a una operación.
- request: listado de políticas relativas a la petición
- response: listado de políticas relativas a la respuesta
- fault: listado de políticas en caso de excepción
operations:
-
target: /mascotas/{id}
verb: GET
operationPolicies:
request:
-
policyName: addHeader
policyVersion: v2
parameters:
headerName: example
headerValue: auth
response: []
fault: []
Despliegue
- isDefaultVersion: Indica si esta es la versión predeterminada de la API. Si se invoca una API sin especificar una versión, se dirigirá la solicitud a la versión predeterminada de la API. (true/false)
- enableSubscriberVerification: Indica si la verificación de suscriptores está habilitada. Usuarios anónimos no podrán subscribirse a la API si esta propiedad se encuentra activada.(true/false)
isDefaultVersion: true
enableSubscriberVerification: false
Lifecycle
- lifeCycleStatus: estado con el que se publicará la API:
- CREATED (por defecto): La API se encuentra en estado creada. No apta para uso externo.
- PUBLISHED: La API se encuentra en estado publicada. Apta para uso externo
- PRE-RELEASED: La API se encuentra en estado de prelanzamiento. Utilizada para hacer promoción y pruebas por parte de los consumidores.
- BLOCKED: La API se encuentra en estado Bloqueado. Todas las llamadas del entorno hacia esta API se encuentran bloqueadas, no se mostrará en el portal del desarrollador.
- DEPRECATED: La API se encuentra en estado obsoleto. Las nuevas subscripciones se desactivan, la API sigue desplegada en la gateway y sigue disponible para subscriptores activos.
- RETIRED: La API se encuentra en estado retirado. Se "despublica" la API para posteriormente ser eliminada del portal del desarrollador.
lifeCycleStatus: PUBLISHED
Publicación automática
En el caso de los proyectos que están almacenado en el Repositorio de Código asociado a la Plataforma de Pre-Cloud, este estaría integrado en la Plataforma de CI/CD, que tendrá un Pipeline asociado al proyecto que se encargará de realizar todo el proceso, desde la validación y pruebas hasta la publicación en la plataforma de destino.
Referencias
- Gitlab
- Jenkins
- Documentación completa AyncAPI
- Editor online AsyncAPI Studio
- Documentación completa OpenAPI
- Editor online Swagger.io
- Editor online OpenAPI-GUI
- WSO2 - Añadir documentación adicional
- Sintaxis básica Markdown
- Editor online Markdown
- Test API con Postman
Anexo I: formato de documentación INLINE
El formato de documentación para documentación inline sigue el formato de marcado html. No requiere las etiquetas raíz html, header y body pero si hace uso del resto de las etiquetas para formatear texto (strong, p, a, li, …).
Este formato tiene gran riqueza de etiquetas de formato, pero suele ser algo complicado formatearlo si no se tienen conocimientos avanzados.