5 minutos

API REST de WordPress: cómo usar tu sitio como backend para tu app

Guía práctica de la API REST de WordPress: lee entradas, pagina resultados, crea tu propio endpoint en PHP y protégelo con contraseñas de aplicación.

Avatar de David González Blanchard

Desarrollador e instructor en Managua

API REST de WordPress como backend para tu app

Muchos negocios ya tienen un sitio en WordPress y, en algún momento, quieren una app. La primera idea suele ser montar un backend nuevo desde cero, con su base de datos y su panel. A veces es lo correcto, pero muchas veces no hace falta: la API REST de WordPress ya viene incluida y te permite leer y escribir contenido desde una app móvil, una web en Vue o cualquier otro cliente.

En mis proyectos con clientes la uso bastante, sobre todo cuando el equipo del negocio ya sabe publicar en WordPress y no quiere aprender otro panel. En esta guía te muestro cómo leer datos, cómo paginar y cómo crear tu propio endpoint con PHP.

Qué es /wp-json y cómo probarlo ahora mismo

Todo sitio WordPress moderno responde en la ruta /wp-json/. Si abres en tu navegador la dirección de tu sitio seguida de /wp-json/, vas a ver un JSON con la lista de namespaces disponibles. El de WordPress es wp/v2, y los plugins agregan los suyos (WooCommerce, por ejemplo, agrega wc/v3).

Puedes probar con este mismo blog. Desde la terminal:

curl "https://gonzalezblanchard.com/wp-json/wp/v2/posts?per_page=3"

Eso te devuelve las últimas tres entradas con todos sus campos: título, contenido, fecha, autor, categorías y bastante más.

Pide solo lo que necesitas con _fields

La respuesta completa de una entrada es grande. Si tu app solo muestra una lista con títulos, no tiene sentido descargar todo el contenido de cada artículo, y menos cuando tus usuarios están con datos móviles.

Para eso existe el parámetro global _fields, documentado en los parámetros globales de la API:

curl "https://gonzalezblanchard.com/wp-json/wp/v2/posts?per_page=3&_fields=id,title,link"

Ahora cada entrada trae solo el id, el title y el link. La diferencia de tamaño se nota mucho.

Paginación: los encabezados que casi nadie lee

La API no te devuelve todas las entradas de una sola vez. Según la guía de paginación oficial, per_page acepta valores de 1 a 100, y cada respuesta paginada trae dos encabezados HTTP:

  • X-WP-Total: el total de registros.
  • X-WP-TotalPages: el total de páginas.

Con eso sabes si tienes que mostrar un botón de «cargar más». Así se ve en JavaScript, por ejemplo en un proyecto con Vue o en un script de Node 18 o superior:

const url = 'https://gonzalezblanchard.com/wp-json/wp/v2/posts?per_page=3&_fields=id,title,link';

const respuesta = await fetch(url);
const totalPaginas = respuesta.headers.get('X-WP-TotalPages');
const posts = await respuesta.json();

posts.forEach((post) => console.log(post.title.rendered, post.link));
console.log(`Páginas disponibles: ${totalPaginas}`);

Fíjate que el título viene dentro de title.rendered. Es un detalle que confunde la primera vez.

Crear tu propio endpoint en la API REST de WordPress

Las rutas de wp/v2 cubren entradas, páginas, categorías, medios y usuarios. Pero muchas veces tu app necesita un dato específico del negocio. Un ejemplo sencillo: el horario de atención de una clínica o de una tienda, que el dueño quiere cambiar sin tocar la app.

Crea una carpeta horario-negocio-api dentro de wp-content/plugins/ y adentro un archivo horario-negocio-api.php:

<?php
/**
 * Plugin Name: Horario del Negocio API
 * Description: Expone el horario de atención del negocio por la API REST.
 * Version: 1.0.0
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

add_action( 'rest_api_init', 'hna_registrar_rutas' );

function hna_registrar_rutas() {
    register_rest_route( 'negocio/v1', '/horario', array(
        array(
            'methods'             => WP_REST_Server::READABLE,
            'callback'            => 'hna_obtener_horario',
            'permission_callback' => '__return_true',
        ),
        array(
            'methods'             => WP_REST_Server::EDITABLE,
            'callback'            => 'hna_actualizar_horario',
            'permission_callback' => function () {
                return current_user_can( 'manage_options' );
            },
            'args'                => array(
                'horario' => array(
                    'type'              => 'string',
                    'required'          => true,
                    'sanitize_callback' => 'sanitize_text_field',
                ),
            ),
        ),
    ) );
}

function hna_obtener_horario() {
    return rest_ensure_response( array(
        'horario' => get_option( 'hna_horario', 'Lunes a viernes, de 8:00 a.m. a 5:00 p.m.' ),
    ) );
}

function hna_actualizar_horario( WP_REST_Request $request ) {
    update_option( 'hna_horario', $request->get_param( 'horario' ) );

    return rest_ensure_response( array(
        'horario' => get_option( 'hna_horario' ),
    ) );
}

Activa el plugin desde el panel y tu endpoint queda en /wp-json/negocio/v1/horario.

Lo que está pasando:

  • register_rest_route se llama dentro del hook rest_api_init, como indica la guía para agregar endpoints personalizados. El namespace negocio/v1 lleva versión para que, si un día cambias el formato, puedas crear v2 sin romper las apps que ya están instaladas.
  • permission_callback es obligatorio. Desde WordPress 5.5, si no lo pones, WordPress muestra un aviso. Para la lectura uso __return_true porque el horario es público; para la escritura, solo un administrador puede cambiarlo.
  • sanitize_callback limpia el texto antes de guardarlo. Nunca guardes lo que llega del cliente tal cual.
  • rest_ensure_response convierte el arreglo en una respuesta REST correcta. La documentación pide que el callback devuelva datos y no imprima el JSON por su cuenta.

Autenticación con contraseñas de aplicación

Para la parte de escritura necesitas autenticarte. Desde WordPress 5.6 existen las contraseñas de aplicación, que generas en Usuarios > Perfil, al final de la página. Según la documentación de autenticación, se envían con Basic Auth sobre HTTPS. De hecho, WordPress solo las habilita por defecto en sitios con SSL o en entornos locales.

Así actualizas el horario desde la terminal:

curl -X POST "https://tusitio.com/wp-json/negocio/v1/horario" \
  -u "tu_usuario:xxxx xxxx xxxx xxxx xxxx xxxx" \
  -H "Content-Type: application/json" \
  -d '{"horario": "Lunes a sábado, de 7:00 a.m. a 6:00 p.m."}'

Dos recomendaciones que aplico siempre:

  1. Crea un usuario solo para la app, con el rol mínimo que necesite. No uses tu cuenta de administrador.
  2. Nunca pongas esa contraseña dentro de una app móvil que vas a publicar. Cualquiera puede descompilar un APK. Si la app necesita escribir datos, lo correcto es que cada usuario inicie sesión con sus propias credenciales, o que haya un servidor intermedio que guarde la llave.

¿Y desde Android?

Para tu app, este endpoint es una API como cualquier otra. Con Retrofit defines una interfaz con @GET("wp-json/negocio/v1/horario"), una data class con el campo horario y listo. En mi canal de YouTube tengo tutoriales de consumo de APIs y MVVM con Jetpack Compose que puedes aplicar tal cual a este caso.

Cuándo no usar WordPress como backend

No todo debe pasar por WordPress. Si tu app maneja muchas escrituras por segundo, lógica de negocio compleja o datos muy relacionados, como inventarios, pagos o reservas, te conviene un backend dedicado, por ejemplo con Laravel. WordPress brilla cuando el contenido lo administra gente del negocio y la app principalmente lo muestra: noticias, menús, horarios, promociones, catálogos sencillos.

Si quieres ver ejemplos de APIs hechas con Laravel, tengo algunos repositorios públicos en mi GitHub. Y si tienes un sitio en WordPress y estás pensando en una app, cuéntame en los comentarios qué datos te gustaría mostrar en ella.

Avatar de David González Blanchard

Escrito por

Trabajemos juntos

¿Tienes una app en mente?

Desarrollo apps Android y web a la medida para negocios en Nicaragua. Escríbeme y lo platicamos sin compromiso.