Mapear el esquema GraphQL para tu sitio, theme o plugin de WordPress
Así que has decidido empezar a usar GraphQL para tu sitio WordPress existente. ¡Genial! Tanto si se usa para nueva funcionalidad como para funcionalidad existente, GraphQL necesitará interactuar con la capa de datos subyacente, para lo cual necesitarás mapear el modelo de datos de tu aplicación (ya sea código PHP personalizado en tu sitio WordPress, tu theme, o tu plugin) en el esquema GraphQL.
¿Cómo debería hacerse el mapeo? ¿Debe hacerse todo de una vez? ¿Debe ser una réplica exacta del modelo de datos existente? ¿Qué pasa con ajustar algún nombre inapropiado en el proceso? Y en cuanto a la deuda técnica, ¿debe mantenerse o tratarse?
Exploremos algunas estrategias para mapear el modelo de datos de una aplicación WordPress existente en un esquema GraphQL.
Mapea el esquema a tu propio ritmo
Añadir GraphQL a una aplicación no es todo o nada. La misma aplicación podría estar alimentada por varias APIs simultáneamente, en cuyo caso GraphQL vivirá junto a otras APIs, durante todo el tiempo que sea necesario. Por ejemplo, podríamos mantener la funcionalidad existente alimentada por REST, e incorporar GraphQL solo para toda la nueva funcionalidad.
Si quieres hacer una migración completa a GraphQL, no tiene que ocurrir todo de una vez. La funcionalidad existente podría ir migrándose lenta pero firmemente a GraphQL, hasta que un día GraphQL se convierta en la única API de la aplicación.
Por tanto, aunque podrías crear el esquema GraphQL completo el día 1, no tienes por qué: en cualquier momento dado, solo aquellas entidades requeridas por la funcionalidad deben estar presentes en el esquema (mediante sus tipos, campos e interfaces). Puedes mapearlas a medida que pasa el tiempo, de forma progresiva.
No dejes que la interfaz cargue con el peso de la implementación
El servidor GraphQL implementará la lógica para acceder a los datos de la aplicación. Lo hará llamando a funcionalidad de WordPress, como llamar a get_posts para recuperar datos de entradas. En esta capa, hay código PHP para satisfacer los resolvers.
Un esquema GraphQL, sin embargo, es una interfaz: declara los contratos para acceder a datos en la API. No le importan los detalles de implementación: no sabe nada sobre WordPress, ni sobre la función get_posts, la tabla wp_posts de la BD o las consultas SQL.
Como tal, deberíamos evitar filtrar información entre las capas, tanto como sea posible.
Esto es importante, porque el modelo de datos a menudo estará empañado por su implementación. WordPress proporciona un claro ejemplo de esto con el CPT "attachment", para representar archivos multimedia como imágenes.
Como es un Custom Post Type, una imagen se trata como una entrada. Entonces, podemos estar tentados de representar los archivos multimedia utilizando el tipo Post, que contiene estos campos:
type Post {
id: ID!
title: String
content: String
excerpt: String
}Pero esto puede no ser apropiado para la aplicación. El significado del campo "content" es claro para una entrada, pero no lo es para una imagen. Lo más probable es que no debería pertenecer ahí.
Una imagen fue modelada como un CPT en WordPress porque era conveniente, así podía reutilizar la lógica existente, y podía almacenarse en la tabla wp_posts existente.
Sin embargo, conveniente no significa apropiado, y eventualmente puede llevar a deuda técnica (es decir, código deficiente que no puede arreglarse sin producir un cambio disruptivo, por lo que se mantiene en la aplicación durante más tiempo del que debería).
En la medida de lo posible, no queremos mantener deuda técnica en nuestra aplicación. Siempre que se nos dé la oportunidad, deberíamos arreglarla. Mapear el modelo de datos al esquema GraphQL proporciona tal oportunidad, permitiéndonos corregir el problema en la capa de interfaz de datos.
(La deuda técnica seguirá persistiendo a nivel de aplicación, sin embargo, así que no estamos arreglando completamente el problema, pero sí reduciéndolo dentro de nuestras posibilidades.)
Pongamos esta idea en práctica. En lugar de tener un tipo Post que represente archivos multimedia, tiene más sentido tener un tipo Media, que contenga solo aquellas propiedades que sí tienen sentido para una entidad imagen:
type Media {
id: ID!
src: String!
width: Int
height: Int
}Bajo el capó, en el nivel de implementación, el field resolver seguirá ejecutando la función get_posts para resolver entradas de tipo Media, pero eso no es ninguna preocupación para el esquema GraphQL.
Desacopla el esquema GraphQL del diagrama de la BD
WordPress está implementado sobre este diagrama entidad-relación de la BD:

Debemos hacer que el esquema GraphQL se base en el diagrama de la BD, pero no deberíamos intentar crear una réplica 1 a 1. Eso es porque tanto el esquema GraphQL como el diagrama de la BD se construyen con ciertas precondiciones o limitaciones, que no se aplicarán al otro.
La sección anterior muestra un ejemplo, donde la tabla wp_posts almacena los datos del CPT imagen, pero en GraphQL habrá dos tipos distintos, Post y Media.
Consideremos otro ejemplo: categorías. En WordPress, una entrada puede tener una categoría (o más), y cualquier CPT también puede crear su propia categoría. Por ejemplo, un CPT llamado "event" tendrá un "event_category".
Tanto las categorías de entradas como las categorías de eventos se almacenan en la tabla wp_terms. Esto facilita a WordPress obtener filas de uno u otro tipo de categoría, al ejecutar la consulta SQL.
Por tanto, podemos estar tentados de mapear categorías mediante el tipo Category, referenciado tanto por entradas como por eventos:
type Category {
id: ID!
name: String!
}
type Post {
categories: [Category]!
}
type Event {
categories: [Category]!
}Sin embargo, una entrada contendrá siempre categorías de entrada, y un evento contendrá siempre categorías de evento. Los datos de estos dos tipos de categoría pueden estar almacenados en la misma tabla de la BD, pero no se mezclarán a nivel de aplicación. Categoría de entrada y categoría de evento son dos entidades distintas.
GraphQL tiene un sistema de tipos estático. Para aprovechar al máximo GraphQL, las diferentes entidades a nivel de aplicación deben modelarse utilizando diferentes tipos en el esquema GraphQL.
En este caso, al mapear categorías al esquema GraphQL, deberíamos crear un tipo diferente para cada una de ellas: PostCategory y EventCategory. Entonces, el tipo Post solo referenciará PostCategory, y el tipo Event solo referenciará EventCategory:
type PostCategory {
id: ID!
name: String!
}
type Post {
categories: [PostCategory]!
}
type EventCategory {
id: ID!
name: String!
}
type Event {
categories: [EventCategory]!
}Si todavía queremos tener una entidad en el esquema que comprenda todas las categorías, esto puede lograrse mediante una interfaz Category:
interface Category {
name: String!
}
type PostCategory implements Category {
id: ID!
name: String!
}
type EventCategory implements Category {
id: ID!
name: String!
}De este modo, los usuarios que accedan a la API tendrán un claro entendimiento de qué datos se recuperarán, independientemente de cómo se mapea en el diagrama de la BD, y de cómo se almacena en la BD.
Una vez que tenemos el esquema GraphQL final, podemos apreciar que su forma se parecerá algo al diagrama de la BD de WordPress, pero claramente será diferente de él:

Adapta la nomenclatura de los campos, siguiendo el tipado estático
Los campos deberían, en la medida de lo posible, respetar la misma nomenclatura que tienen en la aplicación.
Por ejemplo, podemos crear una entrada con la función wp_insert_post, y la entrada tiene las propiedades "title" y "content". Estos nombres también son buenos para el esquema GraphQL (aunque puedan necesitar ligeras modificaciones), así que deberíamos mantenerlos:
type MutationRoot {
insertPost(title: String, content: String): Post
}
type Post {
id: ID!
title: String
content: String
}Pero ese no es necesariamente el caso todo el tiempo. Como vimos antes, las entradas personalizadas deben desacoplarse en sus propias entidades. Entonces, mientras que la función get_posts recupera una lista de cualquier CPT, un campo equivalente posts en el tipo raíz del esquema solo recuperará entidades de tipo Post, pero no Page (que también es un CPT):
type QueryRoot {
posts: [Post]!
}Entonces, ¿cómo obtenemos la lista de todas las entradas y páginas? Mediante otro campo más, customPosts, que recupera las entidades de cualquier CPT mapeado bajo el tipo unión CustomPostUnion:
union CustomPostUnion = Post | Page
type QueryRoot {
customPosts: [CustomPostUnion]!
}La lección importante es esta: la nomenclatura que elijamos para el esquema GraphQL debe adaptarse al tipo de la entidad recuperada. Y debido a los tipos fuertes de GraphQL, tal tipo puede ser diferente en las capas de aplicación y de API.
En este caso, mientras que en WordPress un "post" podría significar cualquier "custom post type", en GraphQL un "post" es necesariamente un Post. Si un campo recupera entradas personalizadas, entonces el campo en el esquema GraphQL debe llamarse customPosts, no posts. De forma similar, si un input recibe un ID para una entrada personalizada, debe llamarse customPostID, no postID.

Esta lección se aplica a los comentarios, por ejemplo. Un comentario puede añadirse a cualquier CPT, no solo a entradas. Entonces, el tipo Comment debe dejar esto claro, conteniendo el campo customPost (y no post):
type Comment {
id: ID!
customPost: CustomPostUnion!
}Convierte valores de cadena predefinidos a enums, usando mayúsculas si es posible
Los tipos de enumeración se definen, por convención, en mayúsculas. Por ejemplo, la documentación de graphql.org proporciona este ejemplo:
enum Episode {
NEWHOPE
EMPIRE
JEDI
}Siempre que tengamos que crear un nuevo tipo enum, deberíamos usar mayúsculas para sus constantes definidas. Sin embargo, al migrar el modelo de datos desde la aplicación, podemos encontrar ciertos conjuntos de valores predefinidos que podemos mapear mediante un enum, pero cuyos valores son cadenas en minúsculas.
Para proporcionar un ejemplo, las entradas en WordPress tienen una propiedad "status", que contiene uno de los siguientes valores:
"publish""pending""draft""trash"
Al mapear esta propiedad en el esquema, el campo Post.status podría devolver un String, así:
type Post {
status: String!
}Sin embargo, como el estado necesariamente será uno de esos valores predefinidos, y ningún otro, preferiríamos mapearlo como un enum:
enum Status {
PUBLISH
DRAFT
PENDING
TRASH
}
type Post {
status: Status!
}Ahora, podemos tener un problema: el enum PUBLISH se convertirá al valor de cadena "PUBLISH" en la aplicación, y no "publish".
Usando un valor en mayúsculas, en lugar del esperado en minúsculas, la lógica en la aplicación puede verse interrumpida. De hecho, ejecutar el siguiente código en WordPress no funciona:
// Esto recuperará todas las entradas, no solo las publicadas
$published_posts = get_posts([
"post_status" => "PUBLISH",
]);En este caso, podemos considerar cambiar convención por conveniencia, todavía usando un enum para mapear las constantes, pero en minúsculas:
enum Status {
publish
draft
pending
trash
}En otras palabras, podemos tener un punto medio entre ser apropiados y ser prácticos. Deberíamos usar buenas prácticas al construir el esquema GraphQL, pero permitirnos desviarnos de ellas siempre que tenga sentido.