Skip to content

Latest commit

 

History

History
1261 lines (929 loc) · 45.7 KB

File metadata and controls

1261 lines (929 loc) · 45.7 KB

Tesis: GraphQL Go — Implementación y Validación de Compatibilidad

Contenidos

  1. Resumen
  2. Abbreviations
  3. Contexto
  4. Research Questions
  5. Research Framework
  6. Research Methodology
  7. Objetivos
  8. Marco Teórico
  9. Desarrollo: Metodología
  10. Desarrollo: Requerimientos
  11. Desarrollo: Alcance
  12. Desarrollo: Pruebas
  13. Desarrollo: RAD
  14. Results: Type System
  15. Results: Operational Equivalence
  16. Results: Compatibility Validation
  17. Conclusions

Resumen

Esta tesis presenta el desarrollo de una implementación del lenguaje de consulta GraphQL en el lenguaje de programación Go, así como la validación de su compatibilidad con implementaciones de referencia.

GraphQL es un protocolo de comunicación entre clientes y servidores, en el cual los servidores definen sus capacidades mediante esquemas. Este lenguaje permite realizar operaciones como consultas, mutaciones y suscripciones, y fue diseñado para facilitar la interacción entre interfaces de usuario y servidores.

La implementación se fundamentó en dos fuentes principales:

  • La Especificación oficial de GraphQL.
  • La implementación de GraphQL en el lenguaje de programación JavaScript.

El objetivo principal fue desarrollar una implementación compatible con la versión v0.6.0 de graphql-js, la biblioteca de referencia en JavaScript.

Como resultado, se creó el proyecto de código abierto graphql-go/graphql, utilizando GitHub como sistema de control de versiones y plataforma para el ciclo de vida del desarrollo del software.

Se eligió el lenguaje Go debido a su naturaleza moderna, su creciente adopción en nuevos proyectos y su enfoque en la computación en la nube, lo cual se alinea con los beneficios ofrecidos por GraphQL.

El correcto funcionamiento del diseño de la API se confirmó mediante la comparación de aplicaciones de ejemplo desarrolladas en ambos lenguajes. Asimismo, la compatibilidad fue verificada a través de una biblioteca de código abierto denominada graphql-go/compatibility-standard-definitions, que representa una contribución novedosa dentro del contexto de esta investigación.


Abbreviations

Term Description
GraphQL A Query Language
graphql-js GraphQL JavaScript reference implementation
graphql-go GraphQL Go implementation

Contexto

La estrategia tradicional para desarrollar software se basa en herramientas que permiten la comunicación entre clientes y servidores utilizando protocolos tradicionales como: RPC1, SOAP2, REST3.

Estas herramientas centralizan el funcionamiento en el lado del servidor, donde se define la información con la que se puede interactuar. Estas estrategias de comunicación generan diversos problemas que se resuelven bajo demanda con distintas soluciones.

Se crean múltiples endpoints para cubrir diversos casos de uso, lo que genera complejidad en su gestión debido a que en el lado del cliente se agregan soluciones ad-hoc.

Estos diferentes endpoints provocan problemas de sobrecarga o insuficiencia de datos. Como consecuencia, se requieren múltiples solicitudes para consolidar la información, ya que el servidor decide qué datos enviar, generando cargas útiles mayores o menores a las esperadas.

Una de las principales consecuencias de tener múltiples endpoints es la generación de solicitudes anidadas, lo cual conlleva una complejidad conocida como el problema n + 1.

Se produce ineficiencia en el mantenimiento porque cuando se hacen cambios en el servidor, estos impactan en los distintos clientes que dependen de él, volviendo a los clientes más propensos a errores.

Los costos aumentan porque los datos enviados desde los servidores requieren mayor capacidad para atender la demanda, por lo tanto se necesitan más recursos y aprovisionamiento.

La experiencia de desarrollo se ve limitada porque se requieren múltiples herramientas para interactuar con cada uno de los protocolos utilizados por los servidores, lo que impacta negativamente al momento de integrar nuevos miembros al equipo y también afecta la curva de aprendizaje.

Existe complejidad en el lado del cliente de la aplicación porque se requieren múltiples capas de abstracción para coordinar y re-consolidar la información, lo que vuelve al código difícil de mantener debido a la necesidad de múltiples soluciones para abordar los problemas.

Los protocolos tradicionales generan múltiples estrategias para gestionar el versionado de la API, lo que desencadena complejidad en el lado del servidor y provoca una extensibilidad limitada.


Research Questions

RQ1. Is graphql-go implemented according to graphql-js?

This question investigates how the graphql-go implementation aligns with the structure and behavior of graphql-js, including type system definitions, resolver handling, and API design.

RQ2. Is graphql-go compatible with graphql-js?

This question focuses on runtime behavior, internal API consistency, and validation through introspection and execution results.


Research Framework

  1. Justification Framework The structured approach divides justification into three categories—Introduction, Applied, and Social—aligning with the layered reasoning recommended by Hernández‑Sampieri & Mendoza (2018).

  2. Research Strategy Alignment This study follows a mixed-method approach as outlined by Hernández‑Sampieri & Mendoza (2018):

    • Qualitative: Applied to RQ1, focusing on the implementation. This includes analyzing code structures and comparing API design and runtime outputs between graphql-js and graphql-go.
    • Quantitative: Applied to RQ2, focusing on compatibility validation. Using introspection, the internal type systems are programmatically compared, ensuring output equivalence across implementations.
  3. Problem Definition & Validation Mechanisms Their methodology encourages clear research problem statements, guided questions (Covered by RQ1 and RQ2), empirical evidence gathering, and comparative analysis (Reflected by the graphql-go implementation, cross-language validation, and compatibility tool).


Objetivos

Objetivo General

  • Implementar GraphQL en el lenguaje de programación Go.
  • Validar la compatibilidad de la implementación de GraphQL Go con graphql-js.

Objetivos Específicos

  • Investigar el contexto y las motivaciones del diseño de GraphQL y su relevancia en los sistemas modernos.
  • Estudiar el estado actual de las implementaciones de GraphQL en Go.
  • Analizar la arquitectura y el proceso de desarrollo del proyecto de código abierto graphql-go/graphql.
  • Documentar las prácticas de ingeniería de software utilizadas en el desarrollo de la biblioteca de GraphQL en Go.
  • Diseñar y desarrollar una biblioteca de validación: graphql-go/compatibility-standard-definitions.
  • Evaluar la compatibilidad con graphql-js mediante introspección y comparaciones de tiempo de ejecución.

Marco Teórico

Trabajos Internacionales Previos

Análisis Basado en Generación de Código

99designs/gqlgen (GitHub) Esta biblioteca implementa GraphQL a partir de un esquema definido en un archivo .graphql, generando automáticamente todos los componentes necesarios del lado del servidor en tiempo de compilación. Entre los elementos generados se incluyen: un servidor HTTPS, punto de acceso GraphQL, pruebas unitarias, y herramientas de desarrollo como GraphQL Playground. Su ventaja principal radica en la centralización de la definición del sistema de tipos y la automatización de su desarrollo.

Limitaciones: La extensibilidad se ve restringida debido a la generación automática de código Go, dificultando las personalizaciones avanzadas sin modificar dependencias internas del proyecto.

Análisis Basado en Definición de Resolutores Imperativos

graph-gophers/graphql-go (GitHub)

Implementa la especificación de GraphQL iniciando desde un archivo de esquema en texto plano, mientras que los resolutores se definen manualmente en código Go. Su ventaja principal radica en la adopción generalizada por parte de la comunidad de Go y su núcleo compartido, que favorece el mantenimiento colaborativo.

Limitaciones: Al definirse el esquema a nivel de texto, se requiere infraestructura adicional para mantener la fuente de verdad del sistema de tipos.

Análisis Declarativo Query-to-SQL

dosco/graphjin (GitHub) Propone una solución declarativa de extremo a extremo que traduce un esquema GraphQL directamente a consultas SQL. Esta biblioteca genera código Go que permite consultas, mutaciones y suscripciones sin necesidad de lógica personalizada.

Limitaciones: Al ser una solución integrada, introduce dependencia en su núcleo, dificultando su extensión sin intervención directa sobre el proyecto principal.

Trabajos Nacionales Previos

Karla Cecilia Reyes Burgos (2023): En su estudio titulado Servicios web con GraphQL, se realiza una revisión sistemática de literatura sobre el uso de GraphQL. Se enfocó en responder las siguientes preguntas:

  • ¿Qué áreas científicas muestran mayor interés en el desarrollo de servicios web con GraphQL?
    La industria médica lidera el interés en la implementación de GraphQL.

  • ¿Qué países presentan mayor interés en el desarrollo de servicios web con GraphQL?
    Estados Unidos encabeza el uso e implementación de GraphQL.

  • ¿Cuál fue el año con mayor número de investigaciones relacionadas al desarrollo de servicios con GraphQL?
    El año 2019 reportó el mayor número de soluciones informáticas basadas en GraphQL.


Variables

Variables Independientes

  • Implementación de GraphQL en Go

    • Paridad de Implementación
    • Corrección de Implementación
  • Validación de Compatibilidad de GraphQL en Go

    • Equivalencia de Validación de Compatibilidad vía Introspección

Variables Dependientes

  • graphql-js (Implementación de Referencia en JavaScript)

    • Actualizaciones de la API
    • Cambios en Componentes Centrales
    • Cambios en el Sistema de Tipos
    • Capacidades de Introspección
  • Especificación de GraphQL

    • Cambios en la Documentación de la Especificación

Metodologías Fundamentales

Desarrollo Rápido de Aplicaciones (RAD)

Enfatiza iteraciones rápidas para desarrollar software.

  • Prototipado: Validar ideas mediante versiones tempranas.
  • Retroalimentación del usuario: Incorporar mejoras basadas en uso real.
  • Desarrollo veloz: Ideal para entornos de desarrollo ágil.

Metodología de Desarrollo de Código Abierto

Se enfoca en colaboración abierta y descentralizada.

  • Revisión por pares: Verificación constante para minimizar errores.
  • Contribución descentralizada: Apoyo global con herramientas compartidas.
  • Código público: Transparencia total que facilita auditoría y colaboración.
  • Prototipado rápido: Permite innovación acelerada por el acceso directo al código fuente.
  • Colaboración: Participación diversa que enriquece el aprendizaje colectivo.

Desarrollo: Metodología

Implementación

Desarrollo Rápido de Aplicaciones (RAD)

El desarrollo de la biblioteca graphql-go/graphql siguió la metodología de Desarrollo Rápido de Aplicaciones (RAD) para facilitar una implementación ágil e iterativa alineada con el modelo de referencia graphql-js.

Prototipado

La implementación comenzó con componentes funcionales mínimos y se expandió de manera incremental. Los esfuerzos iniciales se centraron en módulos fundamentales, incluidos el procesamiento del texto fuente, el análisis léxico (lexer), la construcción del árbol sintáctico (AST), el análisis del esquema y los mecanismos de resolución de campos.

Retroalimentación del Usuario

A medida que el proyecto evolucionaba, la retroalimentación de los usuarios jugó un papel clave. En lugar de seguir un plan de desarrollo rígido, se priorizó la integración continua de sugerencias recopiladas a través de GitHub mediante Issues y Pull Requests. Esto permitió una respuesta ágil a las necesidades emergentes, manteniendo al mismo tiempo la alineación con el código base de graphql-js, la Especificación de GraphQL y las mejores prácticas del lenguaje Go.

Ciclos Rápidos de Entrega

El proyecto se benefició de ciclos de desarrollo cortos, lo que permitió lanzamientos frecuentes. Esta entrega acelerada fue impulsada por la demanda real de empresas que requerían capacidades funcionales de GraphQL en Go, lo que exigía un ritmo de desarrollo eficiente y adaptable.

Metodología de Desarrollo de Código Abierto

El paradigma de desarrollo de código abierto sustentó el ciclo de vida del proyecto, promoviendo la transparencia, la colaboración y la innovación impulsada por la comunidad. Alojado en GitHub, el proyecto se mantuvo accesible públicamente desde sus primeras etapas y fomentó la participación global.

Revisión por Pares

La revisión por pares impulsada por la comunidad fue esencial para garantizar la calidad del software. Los colaboradores reportaron problemas, sugirieron mejoras, corrigieron errores y aplicaron parches de seguridad críticos, fortaleciendo la confiabilidad de la implementación.

Colaboradores Descentralizados

El desarrollo no estuvo vinculado a una única entidad u organización. Por el contrario, colaboradores de diversas regiones y perfiles profesionales contribuyeron de forma asincrónica utilizando GitHub como plataforma central de coordinación.

Código Público y Licencia

Desde sus primeras versiones, el repositorio fue publicado bajo la licencia permisiva MIT, fomentando el uso libre y la colaboración abierta. Esta decisión se alineó con el modelo de licencia adoptado por la implementación de referencia en JavaScript, graphql-js, que en su versión v0.6.0 utilizaba una licencia BSD de tres cláusulas1. Esta compatibilidad de licencias facilitó tanto el aprendizaje académico como el uso comercial, permitiendo la creación de proyectos derivados mediante forks, integraciones y adaptaciones particulares.

Prototipado Rápido

La plataforma GitHub respaldó la naturaleza ágil del proyecto al proporcionar herramientas esenciales como seguimiento de incidencias, pull requests, etiquetado, ramificación y lanzamientos versionados. Estas capacidades permitieron a los colaboradores iterar rápidamente y gestionar eficazmente los hitos del desarrollo.

Colaboración

La colaboración entre equipos multidisciplinarios fue una piedra angular del proyecto. Las funciones colaborativas de GitHub fomentaron el intercambio de documentación, el seguimiento de decisiones y discusiones transparentes sobre compensaciones de diseño. Esto apoyó la mantenibilidad a largo plazo y redujo la curva de aprendizaje para nuevos colaboradores y usuarios.


Desarrollo: Requerimientos

Funcionales: Implementación

  • La implementación debe estar escrita en el lenguaje de programación Go.
  • Debe implementar GraphQL siguiendo la versión 0.6.0 de graphql-js.
  • La Especificación GraphQL servirá como guía alternativa en caso de ambigüedad o ausencia de detalles en graphql-js.

No Funcionales: Implementación

Portabilidad

  • Compatible con sistemas operativos cruzados.
  • Ejecutable en el entorno de ejecución estándar de Go, evitando dependencias de C (cgo).

Seguridad

  • Debe seguir las mejores prácticas de seguridad recomendadas por la comunidad de Go.

Mantenibilidad

  • Aplicar buenas prácticas de diseño de API y estructuras internas según las convenciones de Go.
  • Incluir comentarios explicativos en el código fuente.
  • Integrar herramientas de cobertura de pruebas.
  • Proveer ejemplos de código para casos de uso comunes.

Confiabilidad

  • El proyecto debe ser de código abierto.
  • Debe seguir flujos de trabajo colaborativos que incluyan revisión de código.

Escalabilidad

  • La aceptación por parte de la comunidad de código abierto servirá como indicador de su capacidad de adopción y escalamiento.

Rendimiento

  • El rendimiento debe ser validado a través de pruebas unitarias que incluyan evaluaciones de desempeño fundamentales.

Reusabilidad

  • La solución debe poder integrarse en otros proyectos de Go mediante interfaz de línea de comandos (CLI).

Flexibilidad

  • Debe mantener una mínima desviación respecto a graphql-js para conservar la paridad de diseño de API.

Requerimientos Funcionales: Validación de Compatibilidad

  • La herramienta de validación de compatibilidad debe comparar el sistema de tipos de graphql-js y graphql-go usando introspección.
  • Debe ser ejecutable via la interface de linea de commandos (CLI).
  • La herramienta debe ser escrita en Go.
  • De ser necesario usar otras bibliotecas de Go open-source.

No Funcionales: Validación de Compatibilidad

Portabilidad

  • Compatible con sistemas operativos cruzados.
  • Ejecutable en el entorno de ejecución estándar de Go, sin necesidad de cgo.

Seguridad

  • Debe aplicar las mejores prácticas de seguridad reconocidas en la comunidad de desarrollo en Go.

Mantenibilidad

  • Aplicar buenas prácticas de diseño de API y estructuras internas en Go.
  • Incluir comentarios explicativos en el código fuente.
  • Integrar herramientas de cobertura de pruebas.
  • Incluir scripts ejecutables para facilitar la interacción mediante CLI.

Confiabilidad

  • Debe tratarse de un proyecto de código abierto.
  • Requiere procesos colaborativos de revisión de código para garantizar calidad.

Escalabilidad

  • La aceptación y uso por parte de la comunidad será un indicador clave.

Rendimiento

  • La validación de rendimiento debe estar basada en la actividad del repositorio y la eficiencia del procesamiento con caché.

Reusabilidad

  • El sistema debe ser integrable en otros proyectos de Go utilizando la interfaz de línea de comandos (CLI).

Flexibilidad

  • Debe permitir la integración de nuevas funcionalidades dentro del CLI.

Desarrollo: Alcance

Objetivos de Investigación

Esta investigación tiene como objetivo explorar la integración de GraphQL en el ecosistema de programación Go, centrándose tanto en la implementación de la especificación de GraphQL como en su validación de compatibilidad. El estudio establece una base técnica y metodológica para la evolución continua de bibliotecas open-source de GraphQL mediante herramientas automatizadas y entornos colaborativos de contribución.

Aclaraciones sobre el Alcance

Implementación

El alcance de la implementación se encuentra anclado a la implementación de referencia en JavaScript (graphql-js) en su versión 0.6.0. Aunque esta versión constituye la base del desarrollo, el proyecto promueve futuras mejoras mediante contribuciones de la comunidad, alineadas con principios de extensibilidad y compatibilidad retroactiva.

Las mejoras no deben alterar la funcionalidad existente, a menos que sean impulsadas explícitamente por cambios en la implementación de referencia. La estabilidad es un objetivo clave, y la implementación es validada mediante aplicaciones reales en entornos de producción de múltiples organizaciones.

La biblioteca reproduce el modelo imperativo de construcción de esquemas utilizado por graphql-js, lo que permite una interfaz completamente programable para interactuar con el sistema de tipos interno. La solidez del sistema se garantiza al replicar el conjunto original de pruebas unitarias, adaptado idiomáticamente para Go, asegurando paridad de características y coherencia de comportamiento.

Validación de Compatibilidad

La herramienta de validación de compatibilidad está diseñada como un componente externo, intencionalmente desacoplado de la implementación principal. Su arquitectura modular permite integrar futuras implementaciones de GraphQL más allá de las evaluadas en este estudio.

La herramienta verifica la equivalencia en tiempo de ejecución mediante comparaciones basadas en introspección, enfocándose en la compatibilidad estructural entre sistemas de tipos. Además, sienta las bases para la integración con CI/CD, permitiendo que futuros mantenedores automaticen las verificaciones de compatibilidad durante los flujos de trabajo de desarrollo.

Identificación de Posibles Sesgos

La implementación toma como referencia principal el diseño de graphql-js. Sin embargo, se realizan adaptaciones para ajustarse a las prácticas idiomáticas de Go, evitando patrones específicos del lenguaje JavaScript. Este proceso enfatiza la fidelidad conceptual sobre la replicación sintáctica.

Asimismo, la herramienta de validación de compatibilidad se limita inicialmente a la versión 0.6.0 de graphql-js. Aunque esta restricción reduce su alcance inmediato, su diseño modular facilita su ampliación para futuras versiones u otras implementaciones de referencia.

Límites de la Investigación

Esta investigación se limita explícitamente a evaluar y replicar el comportamiento de graphql-js en su versión 0.6.0. Si bien se considera la extensibilidad, los esfuerzos de validación y las conclusiones están limitados a dicha versión histórica.

Ámbitos Fuera del Alcance

Limitaciones de Recursos

Las siguientes iniciativas fueron consideradas conceptualmente para enriquecer la validación de compatibilidad, pero quedaron fuera del alcance de este estudio debido a limitaciones de tiempo y recursos. Se proponen como trabajos futuros:

  • Pruebas de Compatibilidad Unitarias (GitHub): Framework para validar nombres de pruebas unitarias entre graphql-js y graphql-go.
  • Aceptación de Usuario en Compatibilidad (GitHub): Análisis centrado en el usuario para comparar métricas de adopción, patrones de retroalimentación y características de uso entre graphql-js y graphql-go.

Desarrollo: Pruebas

Implementación

La estrategia para la implementación de graphql-go fue crear pruebas unitarias siguiendo la convención de nombres de las pruebas unitarias de la implementación de referencia graphql-js, pero debido a la creación natural de convenciones propias del implementador, la estrategia de mapeo 1 a 1 en los nombres de las pruebas unitarias no se siguió estrictamente. Algunas pruebas unitarias aún mantienen nombres similares a sus equivalentes en graphql-js, verificando componentes análogos.

Por ello, inicialmente se tuvo la intención de crear graphql-go/compatibility-unit-tests, lo cual requería refactorizaciones de código con la intención de hacer coincidir los nombres de pruebas unitarias entre graphql-go y graphql-js, pero este proyecto quedó fuera del alcance y se agregó como trabajo futuro.

Aunque la convención de coincidencia en los nombres de pruebas no se mantuvo como estrategia establecida, la implementación contiene una suite de pruebas robusta con herramientas que aseguran la reducción de riesgos de errores mediante regresiones.

El entorno de pruebas incluye, por ejemplo, herramientas de cobertura de código que aseguran que el proyecto no descienda por debajo del estado de calidad deseado. Actualmente, la base de cobertura de código es: 92%.

Validación de Compatibilidad

La herramienta de validación de compatibilidad es una biblioteca basada en CLI que contiene pruebas unitarias para sus componentes principales.

Actualmente no contiene herramientas de integración continua (CI) como la biblioteca de implementación.

Se agregó como trabajo futuro mejorar el entorno de pruebas para soportar los siguientes aspectos:

  • Cobertura de código.
  • Integración continua (CI).
  • Aumento del número de pruebas y cobertura.

Desarrollo: RAD

Implementación

La implementación de la biblioteca graphql-go/graphql adoptó un modelo de Desarrollo Rápido de Aplicaciones (RAD), caracterizado por contribuciones iterativas a través de pull requests en GitHub. En lugar de seguir una hoja de ruta estrictamente predefinida, el desarrollo surgió de manera orgánica mediante interacciones comunitarias, discusiones en issues y necesidades prácticas derivadas del portado de características desde la implementación de referencia, graphql-js.

Cada tarea, representada como un pull request individual, abordó componentes específicos como el ejecutor, resolutor, impresor, analizador sintáctico e introspección. Este enfoque hizo énfasis en la modularidad, mantenibilidad y conformidad con la especificación de GraphQL versión 0.6.0, incorporando frecuentemente mejoras paralelas encontradas en graphql-js.

Este enfoque ascendente, basado en pull requests, permitió una rápida creación de prototipos y pruebas de funcionalidades, donde muchas decisiones de diseño se discutieron y resolvieron abiertamente dentro de los hilos de las pull requests. En consecuencia, el historial de desarrollo constituye una línea de tiempo de soluciones prácticas moldeadas tanto por el conocimiento colectivo como por los requisitos en evolución para garantizar la compatibilidad de GraphQL en Go.

Para facilitar la adopción y uso por parte de proyectos externos, se utilizaron etiquetas de GitHub (Tags) para publicar versiones. Estas etiquetas no siguen necesariamente una relación 1:1 con las versiones correspondientes de graphql-js, sino que representan hitos estables con sus respectivas notas de lanzamiento.

La siguiente sección presenta una lista detallada de las solicitudes de extracción que documentan la evolución de la implementación.

Implementación: Pull Requests

Image

Validación de Compatibilidad

La herramienta de validación de compatibilidad siguió un enfoque de desarrollo estrechamente alineado con la metodología de Desarrollo Rápido de Aplicaciones (RAD), caracterizada por ciclos cortos e incrementales, impulsados principalmente mediante pull requests. A diferencia de metodologías tradicionales basadas en planificación, el desarrollo no comenzó con una hoja de ruta completamente definida. En su lugar, la evolución del proyecto fue guiada por la integración continua de funcionalidades abordadas iterativamente.

A diferencia de la implementación original de graphql-go, este proyecto puso un mayor énfasis en el seguimiento de tareas mediante GitHub Issues. Se mantuvo, en la mayoría de los casos, una relación uno a uno entre issues y sus pull requests asociados, lo que proporcionó trazabilidad y un mecanismo de planificación ligera.

Además, el proyecto experimentó con el uso de herramientas inteligentes, como un agente de inteligencia artificial denominado mentatbot, al cual se le asignaron issues que luego fueron abordados mediante pull requests automatizadas o asistidas. Este enfoque refleja la creciente integración de herramientas inteligentes en los flujos modernos de desarrollo de software.

Todos los pull requests fueron desarrollados en ramas aisladas antes de integrarse a la rama main, que funcionó como la versión estable por defecto del proyecto, dado que no se utilizaron etiquetas de versiones formales. Este flujo de trabajo basado en ramas permitió agilidad y menor sobrecarga, en línea con los principios del RAD.

A continuación, se presenta un listado detallado de los pull requests que documentan la evolución de la herramienta de validación de compatibilidad.

Validación de Compatibilidad: Pull Requests

Image


Results: Type System

To validate the implementation of the core GraphQL type system, we created equivalent fields and types in both the JavaScript and Go example applications using graphql-js and graphql-go, respectively. Both libraries follow an imperative schema definition pattern and maintain a near-identical API surface, faithfully reproducing the GraphQL Specification’s flexibility in schema construction styles while emphasizing programmatic control.

This section presents the implementation details of various GraphQL type system elements. Each type is documented with side-by-side code snippets and concise descriptions to aid in comparative understanding.

Note: In all graphql-go examples, we adhere to Go coding style conventions. For instance, all Resolve functions return two values—data and an error—which is a fundamental idiom in Go for error handling.


1. Scalars

Scalars are the basic leaf values in GraphQL.

Int

graphql-js:

int: {
  type: GraphQLInt,
  resolve() {
    return 20;
  },
},

graphql-go

"int": &graphql.Field{
  Type: graphql.Int,
  Resolve: func(p graphql.ResolveParams) (interface{}, error) {
    return 20, nil
  },
},

Float

graphql-js

float: {
  type: GraphQLFloat,
  resolve() {
    return 20.01;
  },
},

graphql-go

"float": &graphql.Field{
  Type: graphql.Float,
  Resolve: func(p graphql.ResolveParams) (interface{}, error) {
    return 20.01, nil
  },
},

String

graphql-js

string: {
  type: GraphQLString,
  resolve() {
    return "str";
  },
},

graphql-go

"string": &graphql.Field{
  Type: graphql.String,
  Resolve: func(p graphql.ResolveParams) (interface{}, error) {
    return "str", nil
  },
},

Boolean

graphql-js

boolean: {
  type: GraphQLBoolean,
  resolve() {
    return true;
  },
},

graphql-go

"boolean": &graphql.Field{
  Type: graphql.Boolean,
  Resolve: func(p graphql.ResolveParams) (interface{}, error) {
    return true, nil
  },
},

ID

graphql-js

ID: {
  type: GraphQLID,
  resolve() {
    return "d983b9d9-681c-4059-b5a3-5329d1c6f82d";
  },
},

graphql-go

"ID": &graphql.Field{
  Type: graphql.ID,
  Resolve: func(p graphql.ResolveParams) (interface{}, error) {
    return "d983b9d9-681c-4059-b5a3-5329d1c6f82d", nil
  },
},

2. Objects

Defines structured data types with fields.

graphql-js

const objectTypeWithArguments = new GraphQLObjectType({
  name: "objectTypeWithArguments",
  description: "An object with arguments.",
  fields: () => {
    return {
      name: {
        description: "The name of the object.",
        type: GraphQLString,
      },
    };
  },
});

graphql-go

objectTypeWithArguments := graphql.NewObject(graphql.ObjectConfig{
  Name: "objectTypeWithArguments",
  Description: "An object with arguments.",
  Fields: graphql.Fields{
    "name": &graphql.Field{
      Description: "The name of the object.",
      Type: graphql.String,
    },
  },
})

3. Interfaces

Used to abstract fields shared by multiple types.

graphql-js

const nodeInterface = new GraphQLInterfaceType({
  name: "Node",
  description: "An object with an ID.",
  fields: {
    id: {
      type: GraphQLID,
      description: "The ID of the object."
    }
  },
  resolveType(obj) {
    switch (obj.type) {
      case "user":
        return userType;
      case "product":
        return productType;
    }
    return null;
  },
});

graphql-go

nodeInterface = graphql.NewInterface(graphql.InterfaceConfig{
	Name:        "Node",
	Description: "An object with an ID.",
	Fields: graphql.Fields{
		"id": &graphql.Field{
			Type:        graphql.ID,
			Description: "The ID of the object.",
		},
	},
	ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object {
		if obj, ok := p.Value.(map[string]interface{}); ok {
			switch obj["type"] {
			case "user":
				return userType
			case "product":
				return productType
			}
		}
		return nil
	},
})

4. Unions

Allows fields to return one of multiple object types.

graphql-js

const searchResultUnion = new GraphQLUnionType({
  name: "SearchResult",
  description: "A union of User and Product types.",
  types: [userType, productType],
  resolveType: (obj) => {
    switch (obj.type) {
      case "user":
        return userType;
      case "product":
        return productType;
    }
    return null;
  },
});

graphql-go

searchResultUnion = graphql.NewUnion(graphql.UnionConfig{
	Name:        "SearchResult",
	Description: "A union of User and Product types.",
	Types:       []*graphql.Object{userType, productType},
	ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object {
		if obj, ok := p.Value.(map[string]interface{}); ok {
			switch obj["type"] {
			case "user":
				return userType
			case "product":
				return productType
			}
		}
		return nil
	},
})

5. Enums

Defines a fixed set of possible values.

graphql-js

const enumType = new GraphQLEnumType({
  name: "Enum",
  description: "A enum.",
  values: {
    FIRST_ENUM: {
      value: 1,
      description: "First enum value.",
    },
    SECOND_ENUM: {
      value: 2,
      description: "Second enum value.",
    },
  },
});

graphql-go

enumType := graphql.NewEnum(graphql.EnumConfig{
  Name: "Enum",
  Description: "A enum.",
  Values: graphql.EnumValueConfigMap{
    "FIRST_ENUM": &graphql.EnumValueConfig{
      Value: 1,
      Description: "First enum value.",
    },
    "SECOND_ENUM": &graphql.EnumValueConfig{
      Value: 2,
      Description: "Second enum value.",
    },
  },
})

6. Input Objects

Used for passing structured input to queries or mutations.

graphql-js

const userInputType = new GraphQLInputObjectType({
  name: "UserInput",
  description: "Input type for user data.",
  fields: () => {
    return {
      name: {
        type: GraphQLString,
        description: "The name of the user.",
      },
      email: {
        type: GraphQLString,
        description: "The email of the user.",
      },
      age: {
        type: GraphQLInt,
        description: "The age of the user.",
      },
    };
  },
});

graphql-go

userInputType := graphql.NewInputObject(graphql.InputObjectConfig{
	Name:        "UserInput",
	Description: "Input type for user data.",
	Fields: graphql.InputObjectConfigFieldMap{
		"name": &graphql.InputObjectFieldConfig{
			Type:        graphql.String,
			Description: "The name of the user.",
		},
		"email": &graphql.InputObjectFieldConfig{
			Type:        graphql.String,
			Description: "The email of the user.",
		},
		"age": &graphql.InputObjectFieldConfig{
			Type:        graphql.Int,
			Description: "The age of the user.",
		},
	},
})

7. Lists

Represents arrays of values or objects.

graphql-js

stringList: {
  type: new GraphQLList(GraphQLString),
  resolve() {
    return ["first string", "second string", "third string"];
  },
},

objectList: {
  type: new GraphQLList(objectType),
  resolve() {
    return [
      { name: "First object in list" },
      { name: "Second object in list" },
      { name: "Third object in list" },
    ];
  },
},

graphql-go

"stringList": &graphql.Field{
  Type: graphql.NewList(graphql.String),
  Resolve: func(p graphql.ResolveParams) (interface{}, error) {
    return []string{"first string", "second string", "third string"}, nil
  },
},

"objectList": &graphql.Field{
  Type: graphql.NewList(objectType),
  Resolve: func(p graphql.ResolveParams) (interface{}, error) {
    return []interface{}{
      map[string]interface{}{ "name": "First object in list" },
      map[string]interface{}{ "name": "Second object in list" },
      map[string]interface{}{ "name": "Third object in list" },
    }, nil
  },
},

8. Non Null

Represents a declaration that a type disallows null.

graphql-js

const userTypeNonNull = new GraphQLObjectType({
  name: "UserNonNull",
  description: "A user with non-null fields.",
  fields: () => {
    return {
      id: {
        type: new GraphQLNonNull(GraphQLID),
        description: "The non-null ID of the user.",
      },
      name: {
        type: new GraphQLNonNull(GraphQLString),
        description: "The non-null name of the user.",
      },
    };
  },
});
userNonNull: {
  type: userTypeNonNull,
  resolve() {
    return {
      id: "user-non-null-1",
      name: "John Doe Non-Null",
    };
  },
},

graphql-go

userTypeNonNull := graphql.NewObject(graphql.ObjectConfig{
	Name:        "UserNonNull",
	Description: "A user with non-null fields.",
	Fields: graphql.Fields{
		"id": &graphql.Field{
			Type:        graphql.NewNonNull(graphql.ID),
			Description: "The non-null ID of the user.",
		},
		"name": &graphql.Field{
			Type:        graphql.NewNonNull(graphql.String),
			Description: "The non-null name of the user.",
		},
	},
})
"userNonNull": &graphql.Field{
	Type: userTypeNonNull,
	Resolve: func(p graphql.ResolveParams) (interface{}, error) {
		return map[string]interface{}{
			"id":   "user-non-null-1",
			"name": "John Doe Non-Null",
		}, nil
	},
},

9. Directives @skip

Allows for conditional exclusion during execution.

graphql-js

query ExampleQuery($skipUserName: Boolean!, $skipProductPrice: Boolean!, ...) {
 // ...
}

graphql-go

query ExampleQuery($skipUserName: Boolean!, $skipProductPrice: Boolean!, ...) {
 // ...
}

10. Directives @include

Allows for conditional inclusion during execution.

graphql-js

query ExampleQuery(... , $includeUserName: Boolean!, $includeProductPrice: Boolean!) {
 // ...
}

graphql-go

query ExampleQuery(... , $includeUserName: Boolean!, $includeProductPrice: Boolean!) {
 // ...
}

11. Mutations

Represents an operation to mutate data.

graphql-js

mutation: new GraphQLObjectType({
  name: "RootMutationType",
  fields: {
    createUser: {
      type: userType,
      args: {
        input: {
          description: "input for creating a user",
          type: userInputType,
        },
      },
      resolve(root, { input }) {
        return {
          type: "user",
          id: `user-${Date.now()}`,
          name: input.name,
        };
      },
    },
  },
}),

graphql-go

mutationType := graphql.NewObject(graphql.ObjectConfig{
	Name: "Mutation",
	Fields: graphql.Fields{
		"createUser": &graphql.Field{
			Type: userType,
			Args: graphql.FieldConfigArgument{
				"input": &graphql.ArgumentConfig{
					Description: "input for creating a user",
					Type:        userInputType,
				},
			},
			Resolve: func(p graphql.ResolveParams) (interface{}, error) {
				input := p.Args["input"].(map[string]interface{})
				return map[string]interface{}{
					"type": "user",
					"id":   fmt.Sprintf("user-%d", time.Now().Unix()),
					"name": input["name"],
				}, nil
			},
		},
	},
})

12. Subscriptions

Represents an operation for subscribing to data.

graphql-js

subscription: new GraphQLObjectType({
  name: "RootSubscriptionType",
  fields: {
    userAdded: {
      type: userType,
      resolve() {
        return {
          type: "user",
          id: `user-${Date.now()}`,
          name: "New User Added",
        };
      },
    },
  },
}),

graphql-go

subscriptionType := graphql.NewObject(graphql.ObjectConfig{
	Name: "Subscription",
	Fields: graphql.Fields{
		"userAdded": &graphql.Field{
			Type: userType,
			Resolve: func(p graphql.ResolveParams) (interface{}, error) {
				return map[string]interface{}{
					"type": "user",
					"id":   fmt.Sprintf("user-%d", time.Now().Unix()),
					"name": "New User Added",
				}, nil
			},
		},
	},
})

This detailed breakdown forms the foundation for the Conclusions section, where we compare design patterns and resolver behaviors across implementations.

This comparison demonstrates that the Go implementation faithfully reproduces the API design of the JavaScript reference implementation. The imperative construction of the type system remains consistent, aligning with the GraphQL Specification’s flexibility in schema definition styles while emphasizing programmatic control.


Results: Operational Equivalence

To assess whether both graphql-js and graphql-go yield the same runtime behavior, we executed equivalent GraphQL operations—Query, Mutation, and Subscription—against each implementation.

Below are the outputs returned by each library. Identical structures and values across both implementations demonstrate operational equivalence, reinforcing that the Go implementation faithfully replicates the behavior of the JavaScript reference.


🔍 Output Comparison Table

graphql-js graphql-go
Identical query, mutation, and subscription outputs Identical query, mutation, and subscription outputs

✅ Query Results

Identical outputs were observed across both implementations.

{
  "data": {
    "ID": "d983b9d9-681c-4059-b5a3-5329d1c6f82d",
    "boolean": true,
    "enum": "SECOND_ENUM",
    "float": 20.01,
    "int": 20,
    "node": {
      "id": "user-1",
      "name": "John Doe"
    },
    "object": {
      "name": "Name of the object instance."
    },
    "objectList": [
      { "name": "First object in list" },
      { "name": "Second object in list" },
      { "name": "Third object in list" }
    ],
    "objectWithArguments": {
      "name": "Name of the object with arguments instance, id: 1"
    },
    "product": {
      "id": "product-1",
      "name": "GraphQL Book"
    },
    "productNonNull": {
      "id": "product-non-null-1",
      "name": "GraphQL Book Non-Null"
    },
    "searchResult": {
      "id": "user-1",
      "name": "John Doe"
    },
    "string": "str",
    "stringList": [
      "first string",
      "second string",
      "third string"
    ],
    "user": {
      "id": "user-1",
      "name": "John Doe"
    },
    "userNonNull": {
      "id": "user-non-null-1",
      "name": "John Doe Non-Null"
    }
  }
}

✅ Mutation Results

Identical outputs were observed across both implementations.

{
  "data": {
    "createProduct": {
      "id": "product-1",
      "name": "GraphQL Guide",
      "price": 49.99
    },
    "createUser": {
      "id": "user-1",
      "name": "Alice"
    },
    "deleteProduct": "Product with id: product-2 deleted successfully",
    "deleteUser": "User with id: user-2 deleted successfully",
    "updateProduct": {
      "id": "product-1",
      "name": "GraphQL Guide Updated",
      "price": 59.99
    },
    "updateUser": {
      "id": "user-1",
      "name": "Alice Updated"
    }
  }
}

✅ Subscription Results

Both implementations emit the same payloads for real-time events.

{
  "data": {
    "productAdded": {
      "id": "product-1",
      "name": "New Product Added",
      "price": 0
    },
    "userAdded": {
      "id": "user-1",
      "name": "New User Added"
    }
  }
}

✅ Conclusion

The exact match in structure and values for all GraphQL operations confirms the operational equivalence between the JavaScript and Go implementations. This not only validates the correctness of the graphql-go implementation but also supports its use as a reliable alternative to the reference library when building production-grade GraphQL APIs in Go.


Results: Compatibility Validation

To further validate the compatibility between graphql-js and graphql-go, we developed an open-source utility: graphql-go/compatibility-standard-definitions.

This library leverages the GraphQL specification’s built-in introspection system to programmatically extract and compare the internal type systems of both implementations. By querying each server's schema metadata (via __schema and __type fields), we can confirm that the registered type definitions—such as objects, interfaces, enums, unions, inputs, and scalars—match precisely between the two.

This approach allows us to automatically assert structural and semantic alignment between the JavaScript reference implementation and the Go alternative, even in deeply nested or polymorphic types.


✅ Conclusion

This compatibility validation framework serves as conclusive evidence that graphql-go conforms to the same type system semantics as graphql-js. It answers one of the central research questions of this thesis:

“Is graphql-go compatible with the GraphQL type system as defined and implemented by graphql-js?”

The answer, supported by introspection-based comparisons, is yes.


Conclusions

The research affirms that graphql-go implements and exposes a programmatic API that closely reproduces the structure, naming conventions, and behaviors of graphql-js. While not all internal names match, due to typical open-source divergence and idiomatic Go conventions, the key observable features do align.

In operational testing, identical outputs for execution and introspection validate the compatibility claim. The introspection-powered validation tool contributes novel proof of runtime schema fidelity, strengthening confidence in using graphql-go as a GraphQL implementation.


Footnotes

  1. graphql-js v0.6.0 BSD License: https://github.com/graphql/graphql-js/blob/v0.6.0/LICENSE