Article

Un solo campo ausente tumba todas las páginas que lo piden

GraphQL rechaza una consulta en su totalidad si un solo campo seleccionado ha desaparecido. Renombre un campo de Drupal y todas las páginas que ejecutan esa consulta quedan en blanco a la vez, mientras TypeScript, la compilación y CI siguen en verde. Esta es la verificación que añadimos.
July 15, 2026
Topics:Headless CMSCI/CDContent ModelingDeveloper Experience
Tags:DrupalNext.jsGraphQLTypeScript

Renombre un campo en Drupal. Despliegue. Su CI está en verde, sus tipos compilan, su compilación de Next.js tiene éxito. Todas las páginas que consultan ese campo ahora están en blanco.

Esto ha tumbado nuestro front end dos veces. Las dos veces el despliegue parecía perfecto.

GraphQL es todo o nada, y ese es todo el problema

La gente supone que un front end desacoplado se degrada con elegancia: el CMS elimina un campo, el componente que lo usaba se renderiza vacío, todo lo demás sigue adelante. Así se comporta REST. No es así como se comporta GraphQL.

GraphQL valida una consulta como documento, antes de ejecutar nada. Si un campo seleccionado no existe en el tipo, el servidor no devuelve datos parciales con una advertencia. Rechaza la consulta entera:

Cannot query field "bogusFieldDoesNotExist" on type "NodeResource".

No vuelve ningún dato. No el campo que eliminó: la respuesta completa. Nuestra consulta de nodos es un solo documento, de unos diez mil caracteres, que impulsa todas las páginas de contenido del sitio. Una selección obsoleta en ella no degrada un componente. Deja en blanco o devuelve 500 en todas las páginas que ejecutan esa consulta, a la vez, en producción.

El radio de impacto no es proporcional al error. Eso es lo que hace que merezca una verificación automática en lugar de un hábito de revisión de código.

Por qué nada en la canalización lo detecta

Recorra las capas y verá que cada una informa honestamente sobre otra cosa:

  • TypeScript comprueba la forma que usted afirma que tiene la respuesta. La consulta es una cadena de plantilla. tsc no tiene opinión sobre su contenido.
  • La compilación de Next.js compila código. No habla con Drupal.
  • Las pruebas unitarias simulan el cliente GraphQL, así que verifican fixtures que eran correctos cuando se escribieron.
  • Las propias pruebas de Drupal pasan, porque desde el lado de Drupal nada está roto. Renombrar un campo es algo perfectamente válido.

Nadie miente. La costura entre los dos sistemas simplemente no está representada en ningún sitio, así que nada puede comprobarla. La deriva aditiva —un campo nuevo de Drupal que el front end nunca recoge— es aún más silenciosa: no tiene ningún síntoma, nunca.

Convierta la costura en un artefacto

Un contrato que no puede comparar es un contrato que no tiene. Así que convertimos el esquema en un archivo.

Drupal exporta su SDL de GraphQL y confirmamos el resultado en el repositorio del front end como graphql/schema.graphql: unos 97 KB, 136 tipos. Vive en el front end deliberadamente: el front end declara lo que necesita, y cualquier CMS detrás de la costura tiene que satisfacerlo. Esa es la misma postura que hace que el contrato sea portable a un back end distinto más adelante.

Dos cosas que no construimos, porque ya existían aguas arriba en drupal/graphql:

drush graphql:dump graphql_compose_server
drush graphql:detect-breaking-changes graphql_compose_server contract.sdl

El primero imprime el SDL. El segundo analiza un contrato confirmado, ejecuta el detector de cambios incompatibles y sale con código distinto de cero ante un cambio incompatible mientras guarda silencio ante uno aditivo: exactamente la distinción que importa. Buscar en el rastreador de incidencias del proyecto antes de escribir código es más barato que mantener su propia versión para siempre.

Una creencia que vale la pena corregir, porque casi nos detuvo: supusimos que disable_introspection: true bloquearía la exportación del SDL. No lo hace. Esa bandera instala una regla de validación de consultas. Imprimir el esquema recorre el mapa de tipos en el propio proceso y nunca ejecuta una consulta. La introspección sigue desactivada para el punto de acceso público y la exportación sigue funcionando.

La verificación es una prueba unitaria

Con el contrato confirmado, la comprobación es pequeña. Construya el esquema a partir del archivo una vez, llame a cada función de consulta exportada, capture el texto de la consulta que habría enviado y valídelo:

const errors = validate(schema, parse(document))
if (errors.length === 0) return
throw new Error(
  "Query does not match the committed schema contract:\n" +
    errors.map((e) => `  - ${e.message}`).join("\n")
)

Ya teníamos un punto de prueba que simula el cliente GraphQL, y npm test ya era un paso bloqueante de CI. Así que la verificación no necesitó nueva infraestructura, ningún servicio nuevo ni cambios en CI. Está organizada en tabla para cada configuración regional, menú y bundle, de modo que se valida cada documento alcanzable y no solo la ruta predeterminada.

Lance los mensajes subyacentes tal cual. Nuestra primera versión afirmaba expect(errors).toEqual([]) y fallaba con expected [Array(1)] to deeply equal [], que no le dice nada a un futuro mantenedor. La biblioteca ya produce la frase precisa: el campo, el tipo y a menudo una sugerencia. No la deseche por una aserción más ordenada.

Qué aporta

La deriva incompatible ahora falla en rojo en el momento del pull request, con el nombre del campo en el error. La deriva aditiva aparece como un diff en un archivo confirmado, que una persona puede leer y sobre el que puede decidir. La falla pasó de producción a un pull request, y de silenciosa a específica.

La verificación empezó de inmediato a proteger consultas escritas después de ella, incluido el trabajo de vista previa de borradores que se lanzó la misma semana. Ese es el punto. Una comprobación que solo cubre el código en el que usted pensaba cuando la escribió es un comentario. Una comprobación que cubre el código que otra persona escribirá el mes que viene es un contrato.