Desarrollo de un Restful API
Un api
es una interfaz que permite exponer de forma sencilla y modular los recursos de una aplicación para ser reutilizados. En resumen es una interfaz diseñada por desarrolladores para desarrolladores de allí que es tan importante ser claros, usar estándares y documentar.
Esta guía pretende ofrecer algunas directrices y recomendaciones a tener en cuenta en la creación de un api
para hacerlo más claros y de fácil integración, independientemente del lenguaje o herramientas de programación.
Nombre de cada recurso
El nombre de cada recurso debe ser entendible y fácil de identificar ya que este debe indicar en una palabra todo lo que nos ofrece.
Al elegir el nombre del recurso es importante preferir nombres sencillos y concretos preferiblemente que definan la entidad.
Por esta razón es una buena practica usar sustantivos en plural en lugar de verbos.
Ejemplo:
El servicio Restful para administrar los usuarios en nuestra aplicación será:
http://app.com/api/v1/users
El servicio Restful para administrar los productos en nuestra aplicación será:
http://app.com/api/v1/products
Métodos CRUD
Por lo genenral necesitamos poder crear, actualizar, consultar y obtener un listado de datos.
Sin Restful tendríamos que crear una ruta para cada método:
http://app.com/api/v1/createUsers
http://app.com/api/v1/getUsers
http://app.com/api/v1/updateUsers
http://app.com/api/v1/deleteUsers
Con Restful, reducimos esto a una sola URL,usando los métodos CRUD de HTTP para cada acción:
http://app.com/api/v1/users
En resumen, solo tendíamos una URL para todas las acciones y la forma de diferenciar cada acción es medinate el método HTTP utilizado.
Ejemplo:
Consultar listado de usuarios:
Método GET
GET: http://app.com/api/v1/users
Consultar detalle de un usuario por ID, en donde 123 es el ID del usuario:
Método GET
GET: http://app.com/api/v1/users/123
Crear un nuevo usuario enviando todo el formulario:
Método POST
POST: http://app.com/api/v1/users
Actualizar el usuario enviando el formulario, en donde 123 es el ID del usuario:
Método PUT
PUT: http://app.com/api/v1/users/123
Eliminar un usuario en donde 123 es el ID del usuario:
Método DELETE
DELETE: http://app.com/api/v1/users/123
Relaciones y Filtros
Es muy fácil confundir una relación con un filtro.
Básicamente una relación se da cuando requerimos una consulta de un recurso que depende de otro recurso.
Un filtro nos permite obtener la información limitada por uno o más criterios como por ejemplo el texto de una búsqueda, o un rango de fechas.
Relación
Usamos una relación en un recurso del api
cuando la relación es obligatoria, es decir que no se puede obtener la información sin diferenciarla por la relación.
Un ejemplo sería el listado de capítulos de un libro, en este caso no podemos obtener el listado de capítulos sin identificar el libro, y no sería muy útil obtener un listado de todos los capítulos sin identificar el libro.
http://app.com/api/v1/books/:id/chapters
Como buena práctica se recomienda no usar más de 2 niveles en la URL de un recurso del api
ya que cada nivel adiciona complejidad al momento de entender y documentar un api
. Cuando requerimos más de 2 niveles se recomienda usar filtros.
Filtros
Los filtros por su parte son opcionales, y permiten limitar la información por criterios más amplios y combinables.
Un ejemplo de esta forma de consulta sería una relación entre estudiantes y cursos en donde por separado podríamos obtener la lista de estudiantes y la lista de cursos.
Si necesitamos consultar los estudiantes registrados en un curso y que están activos podríamos usar algo así:
http://app.com/api/v1/students/?course=10&status=active
Otro ejemplo podría ser obtener un listado de estudiantes matriculados en un rango de fechas:
http://app.com/api/v1/students/?fecha1=20150101&fecha2=20150501
Adicionalmente se puede utilizar filtros para indicar la cantidad de registros a obtener. Por ejemplo en una consulta paginada utilizaríamos los parámetros offset
para indicar el máximo de registros y skip
para indicar el registro inicial.
http://app.com/api/v1/students/?course=10&status=active&offset=20&skip=10
Versiones del api
Un api
es una aplicación como tal, incluso independiente del Frontend, puede ir creciendo y mejorando, de forma independiente y por esto es importante garantizar la compatibilidad con los clientes que consumen los servicios.
La mejor forma de garantizar esto, es utilizando un estándar en la URL que nos indique la versión del api
y así poder tener claro que recibe y que responde.
Ejemplo:
http://api.app.com/v1/users
http://api.app.com/v2/users
Respuesta JSON
Aunque podríamos utilizar como respuesta XML o incluso texto plano, es muy recomendado utilizar objetos JSON, ya que es mas liviano y práctico para el procesamiento en el cliente.
Es importante evitar enviar la respuesta json
en string
, se debe siempre garantizar que el ContentType
sea application/json
y que la codificación de la respuesta esté en UTF-8
, así garantizamos que el fontend va a entender la respuesta y que no vamos a tener problemas con tildes y caracteres especiales.
Otra recomendación importante es siempre usar minúsculas para los nombres de las propiedades del objeto JSON.
Manejo de errores y Encapsulamiento
En toda respuesta es importante informar al cliente lo que ocurrió en el llamado, ya sea que todo estuvo bien o que se presentó algún error.
Se recomienda siempre enviar un código de estado HTTP para indicar si la llamada fué exitosa o se presentó algún incidente.
Adicionalmente, se recomienda encapsular todas las respuestas en un objeto Json estándar que permita informar al cliente con un mensaje mas detallado del suceso.
Por ejemplo, si se presenta un problema de conexión con la base de datos, debemos responder con un código de estado 500, y la respuesta en Json debería indicar que el problema es de conexión a la base de datos.
{
status : 500,
message : "No es posible conectar con la base de datos",
content : ""
}
Otro ejemplo, cuando el usuario si tiene credenciales validas pero no tiene acceso a una consulta en particular es importante informar del suceso mediante un código de estado HTTP 401 y adicional la respuesta en Json detallando el incidente.
{
status : 401,
message : "El usuario no está autorizado a consultar esta data",
content : ""
}
O en caso de que todo esté bien, y para continuar con el estándar retornamos un código de estado HTTP 200 y la respuesta Json encapsulada:
{
status : 200,
message : "",
content : { nombre : "Pedro", apellido : "Perez" }
}
Incluso podríamos crear una serie de códigos internos más específicos dentro de la respuesta de un servicio para informar con mayor detalle, aunque esto no es muy recomendado y solo se debe usar en casos específicos.
Por ejemplo, el cliente no envía toda la información de entrada requerida, entonces respondemos con un código de estado HTTP 412 y la respuesta Json sería:
{
status : 801,
message : "Se requiere que se complete todo el formulario",
content : ""
}
Eso es todo por ahora, básicamente el secreto está en que todo lo que se defina sea pensando en la simplicidad y facilidad y siempre se siga el estándar que se define para evitar problemas incompatibilidades más adelante, además es de importancia documentar correctamente nuestro api, para que así aseguremos que el cliente sigue las indicaciones y que no va a tener problemas al hacer uso de uno de los servicios.