19. API

Un API es un sistema de comunicación entre lenguajes o equipos. Nos ayudará a recibir JSONs y a generar respuesta filtrando por verbos y enviando cabeceras especiales.

Es esencial en todo backend si se busca procesar peticiones AJAX recibidas desde el frontend. Además que JSON es actualmente el idioma universal para comunicar con cualquier otro lenguaje.

Servir un JSON como un API

Veamos un ejemplo sencillo donde devolvemos un JSON a partir de un diccionario.

<?php
// Información
$relojes = [
[
    'marca' => 'Marea',
    'origen' => 'Spain'
],
[
    'marca' => 'Rolex',
    'origen' => 'Suiza'
],
[
    'marca' => 'Omega',
    'origen' => 'Suiza'
],
[
    'marca' => 'Casio',
    'origen' => 'Japón'
]
];

// Cabecera que indica el tipo de contenido a servir
header('Content-Type: application/json');

// Convirte a JSON y lo imprime
echo json_encode($relojes);

Generando un contenido ideal para alimentar a JavaScript u otros lenguajes.

[
  {
    "marca": "Marea",
    "origen": "Spain"
  },
  {
    "marca": "Rolex",
    "origen": "Suiza"
  },
  {
    "marca": "Omega",
    "origen": "Suiza"
  },
  {
    "marca": "Casio",
    "origen": "Japón"
  }
]

Recoger un JSON de un API

Para obtener el JSON debemos utilizando un cliente HTTP que gestione las cabeceras, verbos y demás elementos técnicos. Dentro del ecosistema PHP es altamente estandarizado invocar a curl. en otras palabras, dejar la responsabilidad fuera del código. En la gran mayoría de las configuraciones de PHP se habilita la extensión que nos permite ejecutarlo.

A continuación puede observar una función de ejemplo que puede ser de ayuda:

/**
 * Obtiene un JSON de una url
 * @param {string} $url
 * @param {string} $method - Por defecto GET.
 * @param {array} $body - Diccionario con elementos a enviar. Por defecto [].
 * @return {array}
 */
function getJSON(string $url, string $method = 'GET', array $body = []): array {
    $curl = curl_init();
    $json = json_encode($body);
    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
    curl_setopt($curl, CURLOPT_POSTFIELDS, $json);
    curl_setopt($curl, CURLOPT_HTTPHEADER, array(
        'Content-Type: application/json',
        'Content-Length: ' . strlen($json))
    );
    $result = curl_exec($curl);
    curl_close($curl);
    return json_decode($result, $assoc = true) ;
}

Aquí puede ver otro ejemplo donde se llama.

$data = getJSON('https://dominio.com/api/v1/dinosaurios/', 'POST', array('email' => 'juana@my.email'));

Control de método

En algun momento habrá que vigilar que método estamos recibiendo (GET, POST, PUT, DELETE, etc). Y en caso contrario devolver un código de error apropiado.

<?php

// Filtramos por el método POST
if ($_SERVER['REQUEST_METHOD'] == 'POST') {

    // Cabecera que indica el tipo de contenido a servir
    header('Content-Type: application/json');
    // Indicamos en la cabecera que tipo de métodos están disponibles
    header("Access-Control-Allow-Methods: POST, OPTIONS");
    // Indicamos el código que devolveremos. 200 en caso que todo sea correcto.
    http_response_code(200);

    // Imprimimos un JSON de respuesta con el código 200
    echo json_encode([
        'status' => 'ok'
    ]);

} else {

    // Indicamos el código que devolveremos. 405: método no permitido
    http_response_code(405);

    // Imprimimos un JSON de respuesta.
    echo json_encode([
        'status' => 'ok'
    ]);

}

Ejemplo formulario de contacto

Usando lo aprendido en la lección E-mails, veamos como podemos crear un API que facilite el envío asíncrono de un formulario de contacto. Un problema muy común donde muchos desarrollares acaban utilizando un servicio por desconocimiento.

Enviaremos la siguiente petición.

curl -XPOST -H "Content-type: application/json" -d '{
    "nombre": "Juana",
    "apelldios": "De Arco",
    "email": "juana@my.email",
    "nacionalidad": "francia",
    "mensaje": "¡Libertad!"
}' http://localhost:8000/api-contacto.php

El API se encargará de:

  1. Capturar el JSON recibido.
  2. Renderizar por cada elemento un <p>.
  3. Enviar un correo con el HTML generado a un servidor SMTP en concreto.
<?php

use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;
require_once('vendor/autoload.php');

if ($_SERVER['REQUEST_METHOD'] == 'POST') {

    function getJSON() {
        $inputJSON = file_get_contents('php://input');
        return json_decode($inputJSON, true);
    }

    function getHTMLMessage($data) {
        return array_reduce(array_keys($data), function ($carry, $key) use ($data) {
            $carry .= "<p><strong>$key:</strong> $data[$key]</p>";
            return $carry;
        });
    }

    function responseJSON($data, $code=200) {
        header('Content-Type: application/json');
        header("Access-Control-Allow-Methods: POST, OPTIONS");
        http_response_code($code);
        echo json_encode($data);
    }

    // Crea objeto
    $mail = new PHPMailer();

    try {

        // Configuración servidor SMTP
        $mail->isSMTP();
        $mail->Host       = 'smtp.correo.com';
        $mail->SMTPAuth   = true;
        $mail->Username   = 'usuario@correo.com';
        $mail->Password   = 'contraseña';
        $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
        $mail->Port       = 587;

        $mail->setFrom('emisor@correo.es', 'Nombre emisor');
        // Quien lo recibe
        $mail->addAddress('receptor@correo.es', 'Nombre receptor');

        // Contenido
        $mail->isHTML(true);
        $mail->Subject = 'Mi asunto';
        $mail->Body    = getHTMLMessage(getJSON());

        // Enviar
        $mail->send();
    } catch (Exception $e) {
        responseJSON([
            'status' => 'ko'
        ], 500);
    }

    // Respuesta correcta con el código 200
    responseJSON([
        'status' => 'ok'
    ]);

} else {

    // Respuesta de error con el código 405
    responseJSON([
        'status' => 'ko'
    ], 405);
}

Estructura de rutas

Antes de realizar tu API debes dar una estructura lógica para que los desarrolladores asimilen rápidamente como hacer llamadas. En la siguiente tabla puedes ver un ejemplo de una supuesta biblioteca con rutas usando un formato ampliamente extendido llamado API REST.

{: .table .table-responsive .text-center} | Ruta | Método | Funcionalidad | |------|--------|---------------| | /signup | POST | Registro | | /auth/login | POST | Inicio de sesión | | /auth/logout | GET | Cerrar sesión | | /biblioteca | GET | Lista todas las bibliotecas | | /biblioteca | POST | Crea una nueva biblioteca | | /biblioteca/45 | GET | Obtiene la biblioteca | | /biblioteca/45 | PUT | Actualiza la biblioteca | | /biblioteca/45 | DELETE | Borra la biblioteca | | /biblioteca/45/libros | GET | Lista todos los libros de la biblioteca | | /biblioteca/45/libros/21 | GET | Obtiene el libro de la biblioteca | | /biblioteca/45/libros/21 | PUT | Actualiza el libro de la biblioteca | | /biblioteca/45/libros/21 | DELETE | Borra el libro de la biblioteca |

Cabeceras interesantes

Enviar contenido JSON

header('Content-Type: application/json');

Habilitar CORS

header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
header('Access-Control-Allow-Credentials: true');

Indicar caché

Medida en segundos. Ejemplo de un día.

header('Access-Control-Max-Age: 86400');

Métodos disponibles

header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
Actividad 1

Debes enviar por GET la siguiente información.

  • Peso corporal.
  • Altura.

Devolverás un JSON con el calculo del indice ademas de algunos datos extra como si el peso es adecuado.

Pro: Genera un testing para el Endpoint que acabas de crear.

Actividad 2

Diseña una base de datos para que los corredores de una carrera se puedan inscribir.

Necesitarás los siguientes Endpoints.

  • Inscribirse.
  • Obtener información del corredor.
  • Obtener todos los corredores.

Pro: Genera un testing para el Endpoint que acabas de crear.

Actividad 3

Busca en internet un CSV con toda la información referente a los episodios de los Simpson.

1. Lee el archivo desde PHP y genera un Endpoint que devulva un JSON.

2. Genera una tabla HTML desde JavaScript consumiendo el Endpoint.

3. Modifica el Endpoint para permitir una paginación.

4. Crea un nuevo Endpoint para filtrar por nombre.

5. Modifica JavaScript para que use el paginador, añadiendo botones para pasar de página, y un campo para buscar por nombre consumiendo el Endpoint del filtro.

Actividad 4

Crea un API, con sus correspondientes Endpoints para crear un feed de mensajes similar a Slack pero orientado a pingüinos.

  • El HTML será generado por JavaScript por medio de AJAX.
  • El scroll de mensajes será infinito.
  • Se podrá añadir un mensaje.
  • Se podrá borrar tus propios mensajes.

This work is under a Attribution-NonCommercial-NoDerivatives 4.0 International license.

Will you buy me a coffee?