Lección 19: API | Curso PHP 8.x

Lección 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.

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');

19-1 19-2 19-3 19-4

Esta obra está bajo una Licencia Creative Commons Atribución-NoComercial-SinDerivadas 4.0 Internacional.

Atribución/Reconocimiento-NoComercial-SinDerivados 4.0 Internacional

¿Me ayudas?

Comprame un café
Pulsa sobre la imagen

No te sientas obligado a realizar una donación, pero cada aportación mantiene el sitio en activo logrando que continúe existiendo y sea accesible para otras personas. Además me motiva a crear nuevo contenido.

Comentarios

{{ comments.length }} comentarios

Nuevo comentario

Nueva replica  {{ formatEllipsisAuthor(replyComment.author) }}

Acepto la política de Protección de Datos.

Escribe el primer comentario