- 1. Introduction
- 2. Problem Statement
- 3. Theory Framework
- 4. Development
- 5. Results
- 6. Conclusions
- 7. Recomendations
- 8. Acknowledgments
- 9. References
This thesis developed the implementation of the GraphQL standard in the Go programming language and validated its compatibility.
GraphQL is a communication protocol between clients and servers, where capacities are delegated to servers defined via schemas. It is the principal interface for making different operations such as: queries, mutations and subscriptions, and was created for communicating between servers and clients that are user interface oriented.
The implementation had two main sources of development:
- The GraphQL specification.
- The GraphQL implementation in the JavaScript programming language.
The main objective of the implementation was to have compatibility with the JavaScript implementation up to the v0.6.0 version.
The implementation created a GitHub project, which was adopted as a code version control system and software development life cycle tool.
The implementation chose the Go programming language because it is modern and is constantly adopted by new projects, it focuses on cloud computing therefore leverages all the benefits of GraphQL.
The implementation compatibility was verified by various validation frameworks such as:
- Compatibility unit tests.
- Functional tests.
- User interface oriented tests.
The result of the implementation is an open source software library.
The success of the implementation was confirmed by identifying the open source projects and companies that depend on the library and wire it into their own software.
The traditional strategy for building software is by using tools that allow communication between clients and servers using traditional protocols such as: RPC[1], SOAP[2], REST[3].
These tools centralize the functioning on the server side, where the data is defined so it can be interacted with. These strategies of communication generate various problems which are solved on demand with different solutions.
Multiple endpoints are created in order to cover multiple use cases which creates complexity to manage because the end-to-end requests are on the client side.
These different endpoints cause over-fetching and under-fetching of data. The consequence is that it requires multiple requests in order to consolidate the information, because the server decides which data to send therefore the payloads are greater or lower than expected.
One of the principal consequences of having multiple endpoints is that it creates nested requests causing complexity named: n + 1 problem.
Inefficiency at the maintainability side occurs because when changes are made on the server side the impact cascades to the different clients which depend on the server therefore the clients are more error prone.
There are scalability problems on the server side causing more bandwidth usage.
Costs increase because the data sent from the servers requires more capacity in order to attend the demand so more provisioning and resources are required.
Development experience is limited because it requires multiple tools in order to interact with each of the protocols that the servers use, causing negative impact when integrating new ones. Slow development experience occurs because multiple tools are required to interact with the different protocols which creates difficulties at the moment of integrating new team members to the projects and also on the learning curve side.
Complexity at the application client side exists because it requires multiple layers of abstraction to coordinate and re-consolidate the information so the code is difficult to maintain because there are multiple solutions needed in order to address the problems.
The traditional protocols cause multiple strategies to manage the APIs versioning which triggers complexity on the server side that causes limited extensibility.
Did the implementation reach the defined state in order to fulfill the GraphQL features that were defined by the version v0.6.0?
Have the compatibility results shown that the quality standards were accomplished to meet the defined validations?
The first question aims to investigate the implementation life cycle of the GraphQL standard.
The second question aims to verify the compatibility of the implementation by leveraging multiple validation frameworks.
Which other GraphQL implementations exist?
Which Go best practices were decided to use?
Why was it decided to build the implementation as an open source project?
How was the compatibility verification measured?
Which companies and open source projects use the software library?
Which framework of maintenance was set up for the software library?
This aims to describe what other implementations exist in order to decide which best practices to follow and have them as reference for this implementation.
This aims to describe the decision of leveraging the software development strategy of open source so the future maintenance is ensured for keeping the project alive.
This aims to describe the different strategies to verify the compatibility of the implementation.
This aims to describe which companies and open source projects leverage all the benefits of the implementation in their own software.
This aims to describe the different tooling around the software library that was set up in order to build and maintain the implementation.
Hernández and Mendoza (2018): The justification of arguments is needed for responding to the reasonings of the study development, the importance of the problem and who it affects.
Consequently the justifications of the study are elaborated.
Hernández and Mendoza (2018): Argue that value is created when justification of a theory is made, the result can be used by new researchers and to create new concepts.
It was observed that at the time of the release of the GraphQL specification in the year 2015 the implementations were limited only to JavaScript[10].
The implementation result can be used by other researchers because it was decided to maintain it as an open source project.
It does create new concepts because at the time the implementation was released there were no other Go implementations so it created a base and foundational reference for upcoming new implementations.
Hernández and Mendoza (2018): Argue that the justification of the practice creates value when solving one or more real problems through innovation and technologies for the quality of the processes.
The investigation is justified because it creates value through the library and the quality of the implementation is guaranteed through multiple validation frameworks.
Hernández and Mendoza (2018) argue that the social justification impacts the society and the end users of the investigation.
The investigation has social impact because it has benefits for open source and companies because the implementation end result is open source and the impact is at full transparency because all the development is centralized in GitHub and due to the license the cost of usage is zero, and cascades infinite improvements because many contributors support the library creating changes that allow the library to be future proof that avoids error regressions and security fixes.
GraphQL implementation and compatibility verification.
-
Investigate the GraphQL standard implementation life cycle.
-
Investigate the compatibility verification leveraging various frameworks.
-
Investigate current state of alternative implementations.
-
Investigate the decision making for opting-in for open-source model.
-
Investigate which companies use the end result software library.
-
Investigate the tooling around the project.
99designs/gqlgen (2016/10/12): Implements a schema based GraphQL which generates all the components on the server side at build time. The key value is that all the parts are described in the GraphQL file which contains all the definitions of a type system and from there via using the command line interface commands it generates all the Go code which include: An HTTPS server, GraphQL endpoint and unit tests, and related development tools such as GraphQL Playground.
The drawbacks are that the Go code is auto-generated, which limits the extensibility, since the code is locked-in and making changes is difficult because it requires core dependency from the upstream project.
graph-gophers/graphql-go (2016/10/12): Implements the GraphQL standard by having as entry-points the schema as a file, and the types resolvers are defined via code. It is used as a library, with the main advantage being the adoption, also the core is shared across other implementations therefore the benefits are increased by the contributions from the open-source community.
The drawbacks are that the schema is defined at text level which has limitations in terms of extensibility because it requires extra tooling to maintain the schema source of truth.
dosco/graphjin (2019/03/24): Implements the GraphQL standard, with main features being the ability to generate the API from a schema that generates the SQL queries, so it is an end-to-end solution similar to an object-relational-mapping. It supports multiple databases, allows end users to just focus on the GraphQL schema side so the Go code is generated, using the API enables performing queries, mutations and subscriptions.
The drawbacks are that the end-to-end solution locks-in the implementation which is hard to extend, therefore the dependency on having internal hooks exposed and requires strong dependency on the maintainers.
(Karla Cecilia Reyes Burgos, 2023) Created a research titled: Web services with GraphQL. This is a systematic literature that details the recompilation of diverse resources which investigate the GraphQL usage, and has as a principal aim to answer the following questions:
-
Which areas of science show greater interest in the development of web services using GraphQL?
-
The medicine industry is the greater area of science interested that applies a GraphQL implementation.
-
Which countries had shown greater interest in the development of web services using GraphQL?
-
The country with greater interest in applying a GraphQL implementation is USA.
-
In which year do we find the greater quantity of investigations with respect to the development of web services using GraphQL?
-
The year which had more informatic solutions was 2019.
Rapid Application Development (RAD): A methodology that emphasizes and prioritizes rapid iterations of software development.
Some of the key aspects being: Prototyping: Leverages testing ideas and collects feedback before creating the final software version. User Feedback: Focuses on user feedback and continuous iteration of small working parts instead of a strict plan. Fast turnaround: Benefits the turnaround of the development of the software to pivot making it appealing for developers that are into highly fast paced working environments.
Open-source Development Methodology: A methodology that focuses on collaboration across different numbers of contributors, it is decentralized and in openness development.
Some of the key aspects being: Peer Review: Continue development of parts that are improved by the community so the risk of errors are reduced as much as possible.
Decentralized Contributors: Development as a decentralized iterations via different tools that the community can leverage for synchronizing all the development workflows without single point of control.
Public Codebase: The source of the project is at a public repository so can be accessed by any contributor to leverage all the benefits that are tied to the license, and the key feature is that security fixes can be addressed.
Rapid Prototyping: Creates the right environment for rapid prototyping of new features, which enables the participation of all the community via the tooling of the source of the code.
Collaboration: Encourages all types of participation which can be from different experiences so the community of contributors are able to participate and learn in different ways.
Rapid Application Development (RAD): We used this methodology in order to implement the GraphQL Go implementation rapidly and iteratively following the graphql-js reference:
Prototyping: The GraphQL Go implementation was created from very basic initial interfaces that started from the text source, parser, lexer, AST, collectors and resolver.
User Feedback: As we made progress we received constant feedback that was documented mainly in GitHub; we did not follow a strict plan.
Fast turnaround: We did quick iterations in the implementation because it enabled us to constantly release multiple versions so in order to do so the contributors worked in a fast paced working environment, and also to cover the need of a working version for different companies that leveraged the initial versions.
Open-source development methodology: The development of the project was done by leveraging the different characteristics of the open-source methodology to have multiple contributors adding value to the project. It is decentralized so the project can be kept forever due to the dependency on GitHub which guarantees durability and also the development was continuously iterated in the open so any person could benefit from the project from learning, up to proposing changes.
Peer Review: The project was constantly peer-reviewed by different persons across the planet, and the key part of this besides proposing improvements was that they reported and sent security fixes to keep the project trustable and safe from vulnerabilities.
Decentralized Contributors: The project was iterated and leveraged GitHub as a central control for the development; it enabled multiple contributors from different parts of the planet.
Public Codebase: The project was publicly available since the early stages of development, it allowed other contributors to propose security fixes as well as report bugs and security issues. The license is MIT which matches the reference graphql-js and enables creation of multiple branches of similar projects plus forks with improvements as well as internal forks that require special characteristics that cover inner needs of private projects.
Rapid Prototyping: Since we leveraged GitHub as a central part of the workflow of the project, it enabled rapid iterations of including sets of changes from the graphql-js reference implementation versions.
Collaboration: It enabled different contributors from different parts that learned from the project and they were able to add value.
Validation process to verify that the software library implemented is successfully compatible with the standard.
Risk Assessment: Proactive approach of identifying and mitigating potential risk of the software library.
Functionality Testing: Process to check that the software library matched the standard.
Validation Testing: Process to ensure that the final software library meets the requirements of the stakeholders.
User Acceptance Testing: Process to test the software library with end-users in terms of metrics that proves their engagement and satisfaction aligned with the specification, the following criterias are collected and compared:
- GitHub Stars.
- GitHub Issues (Open/Closed).
- GitHub Issues (Open/Closed, comments that are related to the specification).
- GitHub Pull Requests (open/closed).
- GitHub Forks.
- GitHub Repository License.
Design Verification and Reviews: Process to oversee the software library design or architecture.
References: IEEE 1012, ISO 13485, ISO 15026.
It was decided to implement the GraphQL standard in Go following the methodologies listed above to have a working initial version and continue iterating from there:
Rapid application development (RAD): From the initial version there was multiple number of iterations that made the library stable:
Prototyping: We have earlier versions that have the implementation work as a prototype which helped us to validate and test main functionalities.
User Feedback: Since we used GitHub as a central main source, there was continued feedback from the community in order to incorporate those.
Fast turnaround: There were multiple pivots on the implementation of the main components with the aim of friendly APIs.
Open-source development methodology: We leveraged GitHub as source of the code, and we used multiple of those features to create the library as a open-source project:
Peer Review: The pull-requests that included new functionality had constantly reviewed to improve the quality and reduce the number of errors.
Decentralized Contributors: We had multiple contributors via GitHub since they were able to review the library and propose improvements and report bugs.
Public Codebase: The library was developed publicly therefore the codebase could be leveraged for different purposes.
Rapid Prototyping: The library was created as continuous iteration via multiple prototypes.
Collaboration: Since GitHub were used, collaboration is the central part of the development with constant collaboration of multiple contributors.
The following frameworks for compatibility validation were created:
- Compatibility Unit Tests: Validation compatibility library for comparing GraphQL implementations unit tests results.
- Compatibility Standard Definitions: Validation compatibility library for comparing GraphQL's standard definitions against implementations.
- Compatibility User Acceptance: Validation compatibility library for GraphQL implementation user acceptance.
It was decided to choose JavaScript because there are more JavaScript compilers available.
Risk Assessment: Compatibility validation of risks was done by wiring the three frameworks created to the continuous integration pipeline and in case of regressions against not fulfilling the standard there are warnings and newer changes are always being validated for keeping the implementation stable. The following frameworks guarantee risk assessment:
- Compatibility Unit Tests.
- Compatibility Standard Definitions.
- Compatibility User Acceptance.
Functionality Testing: Compatibility validation against the JavaScript official reference implementation unit tests was done via the following framework:
- Compatibility Unit Tests.
Validation Testing & Design Verification and Reviews: Compatibility validation against the official standard documentation was done via the following framework:
- Compatibility Standard Definitions.
User Acceptance Testing: Compatibility validation via collecting acceptance information from GitHub was done via the following framework:
- Compatibility User Acceptance.
Standard definitions and unit tests libraries have some cross-cover of validations, the standard definitions analysis are the type system level which enables detailed differences that could be leverage for cross-analysis of implementation versions.
Documentation Improvements: GitHub Repository: https://github.com/tomarrell/wrapcheck GitHub Pull Request: tomarrell/wrapcheck#62
The implementation was decided to be built because at the time the GraphQL standard was released in 2015 there were no implementations available in the Go programming language therefore the opportunity to create an open source software library was available.
One important highlight is that the standard was released at a similar time together with the reference implementation graphql-js therefore those two artifacts were used to identify the tasks that are part of this section.
The tasks match the main functionalities of the reference implementation graphql-js, so the main components were the central part of each iteration via GitHub so we could accomplish implementation at each version.
Listing the tasks that covers the implementation and compatibility validation:
| Implementation | ||||||||
| Priority | Task Name | Duration Interval Dates (Created At - Merged At) | Duration In Days | Contributors | Components | Description | Pull Requests | Version |
|---|---|---|---|---|---|---|---|---|
| 1 | Porting changes from graphql-js version 0.4.18. | 2016-03-07 09:07:29 +0000 UTC - 2016-05-30 01:52:47 +0000 UTC | 83 |
- https://github.com/sogko - https://github.com/coveralls - https://github.com/pspeter3 - https://github.com/chris-ramon - https://github.com/jvatic |
Errors, Languages, Types, Execution, Validation. |
Port changes from graphql-js up to the version v0.4.18 which includes the following functionalities: - Consolidates the extension definition outside the type definition. - Makes operation name optional. - Compliance with the int sizing based on the specification. - Changes on the function signature of graphql.NewTypeInfo - Enables the possibility of removing the experimental FieldDefFn. |
graphql-go/graphql#117 | 0.4.18. |
| 2 | Porting changes from graphql-js version 0.5.0. | 2016-04-18 08:46:26 +0000 UTC - 2016-06-11 21:13:46 +0000 UTC | 54 |
- https://github.com/sogko - https://github.com/switchtrue - https://github.com/bsr203 - https://github.com/coveralls - https://github.com/kmulvey |
Definition, Directives, Executor, Introspection, Rules, Schema, Types. |
Port changes from graphql-js up to the version v0.5.0 which includes the following functionalities: - Improvements on introspection related to directive locations. - Improvements in the schema language related to directives. - Consolidates the `getTypeOf` method into the executor component. - Schema changes related to types. - Consolidate arguments including context to executor. - Improvements in types overlapping in rules component. - Add schema definition into language component. |
graphql-go/graphql#123 | 0.5.0. |
| 3 | Porting changes from graphql-js version 0.6.0. | 2017-03-15 03:38:12 +0000 UTC - 2017-03-15 03:40:57 +0000 UTC | 1 |
- https://github.com/sogko - https://github.com/coveralls - https://github.com/F21 - https://github.com/tsunammis - https://github.com/mishudark - https://github.com/talk-to-my-car - https://github.com/chris-ramon - https://github.com/chris-ramon - https://github.com/coveralls - https://github.com/sogko" |
Definition, Directives, Executor, Introspection, Rules, Schema, Validator. |
Port changes from graphql-js up to the version v0.6.0 which includes the following functionalities: - Deepen introspection query from 3 levels to 7. - Improves validation error message when field names conflict. - Improves validation error messages by listing suggestions. - Fixes a bug where an empty "block" list could be skipped by the printer. - Exports introspection in public API. - Schema Language Directives. - Directive location: schema definition. - Deprecated directive tag. - Validation: improving overlapping fields quality. |
graphql-go/graphql#192 | 0.6.0. |
| 4 | Executor | 2015-09-16 04:23:35 +0000 UTC - 2015-09-25 23:13:25 +0000 UTC | 9 |
- https://github.com/sogko - https://github.com/chris-ramon" |
Errors, Executor, Language, Types. |
- Executor implementation. - Partial implementation of resolve fields. - Implemented most of the types. - Partial schema definition implementation - Added starwars graphql tests. |
graphql-go/graphql#8 | 0.4.18 |
| 5 | Source | 2015-09-13 09:36:05 +0000 UTC - 2015-09-14 23:09:05 +0000 UTC | 1 | - https://github.com/chris-ramon | Source. |
- Adding source api implementation. |
graphql-go/graphql#5 | 0.4.18 |
| 6 | Visitor | 2015-09-23 02:42:31 +0000 UTC - 2015-09-25 23:13:54 +0000 UTC | 2 |
- https://github.com/sogko - https://github.com/chris-ramon |
Visitor, Printer, Validator. |
- Visitor wired to Printer and Validator. - Visit component implementation. - AST implementation with enter and leave functions wiring. - Visitor implementation related to actions. - AST reducer of visitor component. - Schema parser tests. |
graphql-go/graphql#10 | 0.4.18 |
| 7 | Printer | 2015-09-23 02:42:31 +0000 UTC - 2015-09-25 23:13:54 +0000 UTC | 2 |
- https://github.com/sogko - https://github.com/chris-ramon |
Printer. |
- Printer implementation work related to ast node transformations. - Printer and visitor changes related to AST traversal. - Printer changes related to node to string transformations. - Printer and parser changes to transform string to node. |
graphql-go/graphql#10 | 0.4.18 |
| 8 | Parser | 2015-09-10 03:14:07 +0000 UTC - 2015-09-10 18:02:02 +0000 UTC | 1 |
- https://github.com/sogko - https://github.com/chris-ramon |
Parser, AST. |
- Parser unit tests. - AST types definitions. - AST files ordering. |
graphql-go/graphql#2 | 0.4.18 |
| 9 | Lexer | 2015-09-10 12:11:05 +0000 UTC - 2015-09-11 05:30:14 +0000 UTC | 1 |
- https://github.com/sogko - https://github.com/chris-ramon |
Lexer, Types, Errors. |
- Types improvements. |
graphql-go/graphql#3 | 0.4.18 |
| 10 | AST | 2015-09-10 12:11:05 +0000 UTC - 2015-09-11 05:30:14 +0000 UTC | 1 |
- https://github.com/sogko - https://github.com/chris-ramon |
AST. |
- AST structs and definitions. - AST types re-estructuring. |
graphql-go/graphql#3 | 0.4.18 |
| 11 | Resolver | 2018-03-01 09:36:38 +0000 UTC - 2018-03-08 21:51:05 +0000 UTC | 7 |
- https://github.com/dvic - https://github.com/coveralls - https://github.com/chris-ramon |
Resolver. |
- Adds field resolver interface support. |
0.4.18 | |
| 12 | Resolver | 2015-09-16 04:23:35 +0000 UTC - 2015-09-25 23:13:25 +0000 UTC | 9 |
- https://github.com/sogko - https://github.com/chris-ramon |
Resolver. |
- Adds resolver component implementation. |
0.4.18 | |
| 13 | Types | 2015-09-26 15:21:47 +0000 UTC - 2015-10-02 01:05:37 +0000 UTC | 5 |
- https://github.com/sogko - https://github.com/chris-ramon - https://github.com/chris-ramon - https://github.com/sogko |
Types. |
- Added introspection integration with types. - Consolidated types definitions. |
graphql-go/graphql#12 | 0.4.18 |
| 14 | Errors | 2018-11-28 04:14:47 +0000 UTC - 2018-12-03 01:16:35 +0000 UTC | 4 |
- https://github.com/chris-ramon - https://github.com/limoli - https://github.com/coveralls - https://github.com/racerxdl |
Errors. |
- Adds original error support. |
graphql-go/graphql#423 | 0.4.18 |
| 15 | CircleCI | 2018-07-19 03:27:49 +0000 UTC - 2018-07-19 03:41:48 +0000 UTC | 1 |
- https://github.com/chris-ramon - https://github.com/coveralls - https://github.com/chris-ramon - https://github.com/chris-ramon - https://github.com/coveralls |
CI, CD. |
- Replaces travis continous integration in favor of circle continous integration. |
graphql-go/graphql#361 | 0.6.0 |
| Compatibility Validation | ||||||||
| Architecture. | Compatibility Unit Tests. |
- Create the architecture project for the compatibility unit tests framework. |
0.6.0 | |||||
| Scaffolding. | Compatibility Unit Tests. |
- Create the scaffolding project for the compatibility unit tests framework. |
- graphql-go/compatibility-unit-tests#1 - graphql-go/compatibility-unit-tests#2 |
0.6.0 | ||||
| Implementation. | Compatibility Unit Tests. |
- Implement the compatibility unit tests framework. |
- graphql-go/compatibility-unit-tests#5 |
0.6.0 | ||||
| Architecture. | Compatibility Standard Definitions. |
- Create the architecture of the project for the compatibility standard definitions framework. |
0.6.0 | |||||
| Scaffolding. | Compatibility Standard Definitions. |
- Create the scaffolding project for the compatibility standard definitions framework. |
0.6.0 | |||||
| Implementation. | Compatibility Standard Definitions. |
- Implement the compatibility standard definitions framework. |
0.6.0 | |||||
| Architecture. | Compatibility Standard Definitions. |
- Create the architecture of the project for the compatibility user acceptance framework. |
0.6.0 | |||||
| Scaffolding. | Compatibility User Acceptance. |
- Create the scaffolding project for the compatibility user acceptance framework. |
0.6.0 | |||||
| Implementation. | Compatibility User Acceptance. |
- Implement the compatibility user acceptance framework. |
0.6.0 | |||||
Tasks mapping are based on matching the unit tasks against the project thesis requirements, below we list the tasks names against their requirements plus the main implementation components.
Task Name Requerimientos Funcionales GraphQL Component Pull Request
Research goals: Identify the problems of the traditional client-server protocols. Implement the GraphQL standard in Go. Produce a Go open-source library. Verify the compatibility against the GraphQL reference implementation. Create the environment for preserving the end-result.
Goals clarifications: Problems identified were opted-in to consider only the following protocols: Implementation limited up-to graphql-js version: 0.6.0. Specific features were implemented starting from graphql-js version: 0.6.0.
Potential biases identification: Implementation is committed to open-source. Standard implementation graphql-js compatible commitment.
Limits of the research: Limited up-to a graphql-js version 0.6.0 tests suites. Limited to GraphQL schema programmatically defined.
Not included in this research: Other type of schema definition strategies such as: Inference from text schema-first.
Related to how the system behaves at an internal level. How all different apis of gql behave
Requerimientos funcionales: Source: Source-in. Parser: Source to AST and Lexer. Lexer: Tokenizes the source-in. AST: The source as a tree. Collector: Matches AST to resolvers. Resolver: Produces end-result, meaning the source-out.
Related to the usability, ux of the end-user, at external level. Requerimientos no funcionales:
Performance: Usability: Scalability: Security: Maintainability: Compatibility: Portability:
Related to the domain category of the type of software, specific to the domain/industry. Requerimientos de dominio: GraphQL Standard Spec: Open-source Standards: Go Standards:
The graphql-go library contains the following components:
- GraphQL:
- Source.
- Extensions.
- GQLErrors.
- Language:
- AST.
- Kinds.
- Lexer.
- Location.
- Parser.
- Printer.
- Visitor.
- Executor:
- Resolver.
- Validator.
- Definition.
- Directives.
- Introspection.
- Rules.
- Schema.
GraphQL is the component that wraps most of the internal components, it contains the end-user Go APIs, which are syntactically similar to the graphql-js reference implementation APIs.
import {
graphql,
} from 'graphql';
graphql(schema, query).then(result => {
});
import "github.com/graphql-go/graphql"
params := graphql.Params{Schema: schema, RequestString: query}
graphql.Do(params)
The internal components wrapped are the following:
- Source.
- Extensions.
- Parse.
- Validate.
- Execute.
Source is the component that contains the GraphQL root operation in byte format.
Used within the Parse component as an entry point for accessing the library end-user GraphQL string operation.
Extensions is a component that provides an interface for extending GraphQL execution with custom functionality through hooks into various phases of the GraphQL execution lifecycle.
The Extensions interface defines methods that allow developers to intercept and extend the following phases:
- Init: Initialize the extension with context and parameters
- ParseDidStart: Hook called before GraphQL query parsing begins
- ValidationDidStart: Hook called before GraphQL query validation begins
- ExecutionDidStart: Hook called before GraphQL query execution begins
- ResolveFieldDidStart: Hook called before individual field resolution begins
Each phase hook returns a corresponding finish function that is called when the operation completes, allowing extensions to perform cleanup, logging, metrics collection, or other post-processing tasks. This design enables powerful extensibility for cross-cutting concerns like performance monitoring, authentication, caching, and custom validation logic.
GQLErrors is the component responsible for modeling, collecting, and propagating errors that occur during GraphQL execution. It provides structures for representing errors with context such as locations, paths, and original error messages, aligning with the GraphQL specification’s error format. The component is based on the upstream gqlerrors package and ensures that errors are reported in a consistent and extensible way. This enables clients to receive detailed and actionable error information in GraphQL responses.
AST (Abstract Syntax Tree) is the component that represents the parsed GraphQL document as a hierarchical tree structure of nodes.
The AST serves as the intermediate representation between the raw GraphQL query string and the execution engine. It is produced by the Parser component from the tokenized source and consumed by various other components including the Validator, Executor, and Printer.
The AST structure consists of different node types that correspond to GraphQL language constructs:
- Document: The root node representing the entire GraphQL document
- OperationDefinition: Represents query, mutation, or subscription operations
- FieldDefinition: Represents individual fields within operations
- FragmentDefinition: Represents reusable query fragments
- SelectionSet: Represents groups of field selections
- Arguments: Represents field arguments and their values
- Directives: Represents GraphQL directives applied to fields or fragments
The AST enables the GraphQL engine to traverse and analyze the query structure programmatically, allowing for validation rules to be applied, execution planning to be performed, and query introspection capabilities to be provided. This tree representation abstracts away the textual syntax and provides a structured format that can be efficiently processed by the subsequent stages of GraphQL execution.
Kinds is the component that defines constants for identifying different types of AST nodes in the GraphQL document structure.
The Kinds component serves as a central registry of node type identifiers that are used throughout the GraphQL processing pipeline. It provides string constants that categorize each AST node according to its syntactic role in the GraphQL language.
The Kinds constants include:
- Document: Identifies the root document node
- OperationDefinition: Identifies query, mutation, and subscription operations
- VariableDefinition: Identifies variable declarations in operations
- SelectionSet: Identifies groups of field selections
- Field: Identifies individual field selections
- Argument: Identifies field arguments
- FragmentSpread: Identifies fragment usage
- InlineFragment: Identifies inline fragment selections
- FragmentDefinition: Identifies reusable fragment definitions
- Variable: Identifies variable references
- IntValue, FloatValue, StringValue, BooleanValue: Identify literal values
- ListValue, ObjectValue: Identify complex value structures
- Directive: Identifies GraphQL directives
- NamedType, ListType, NonNullType: Identify type references
The Kinds component enables type-safe AST traversal and manipulation by providing a standardized way to identify and categorize nodes. This is essential for the Visitor pattern implementation, validation rules, and other components that need to process specific types of AST nodes. The constants ensure consistency across the entire GraphQL processing pipeline and facilitate debugging by providing human-readable node type identifiers.
Lexer is the component responsible for tokenizing the GraphQL source string into a sequence of meaningful tokens that can be processed by the Parser component.
The Lexer performs lexical analysis by scanning through the raw GraphQL query string character by character and identifying distinct tokens such as:
- Names: Field names, type names, and identifiers
- Keywords: GraphQL language keywords like
query,mutation,subscription,fragment - Punctuation: Braces
{}, brackets[], parentheses(), colons:, commas, - Operators: Equality operators, directives
@, spread operators... - Literals: String literals, numeric literals, boolean literals
- Comments: Single-line and multi-line comments that are preserved or ignored as needed
The tokenization process transforms the linear text input into a structured sequence of tokens, each containing:
- Token Type: The category of the token (e.g., NAME, STRING, NUMBER)
- Value: The actual text content of the token
- Location: Position information including line and column numbers for error reporting
The Lexer serves as the foundation for the parsing pipeline, providing clean, categorized input to the Parser component which then constructs the AST. This separation of concerns allows for efficient error detection and reporting at the lexical level, helping developers identify syntax issues in their GraphQL queries before they reach the parsing stage.
Parser is the component responsible for analyzing the sequence of tokens produced by the Lexer and constructing an Abstract Syntax Tree (AST) that represents the hierarchical structure of a GraphQL document.
The Parser performs syntactic analysis by consuming tokens from the Lexer and applying GraphQL grammar rules to build a structured representation of the query. It processes the tokens according to the GraphQL specification and creates corresponding AST nodes for each language construct.
The Parser handles various GraphQL language elements including:
- Document Parsing: Processes the root level of a GraphQL document containing operations and fragments
- Operation Parsing: Handles query, mutation, and subscription operations with their selection sets
- Selection Set Parsing: Processes field selections, fragment spreads, and inline fragments
- Field Parsing: Handles field names, arguments, aliases, and nested selections
- Fragment Parsing: Processes both named fragment definitions and inline fragments
- Value Parsing: Handles literals (strings, numbers, booleans), variables, lists, and objects
- Type Parsing: Processes type references including named types, list types, and non-null types
- Directive Parsing: Handles directive applications with their arguments
The parsing process includes comprehensive error handling and reporting:
- Syntax Error Detection: Identifies malformed GraphQL syntax and provides precise error locations
- Error Recovery: Attempts to continue parsing after encountering errors to find additional issues
- Location Tracking: Maintains line and column information for each AST node to support debugging
The Parser serves as the critical bridge between the raw text input and the structured AST representation, enabling subsequent components like the Validator and Executor to process GraphQL operations efficiently. By converting the linear token stream into a hierarchical tree structure, the Parser facilitates pattern matching, validation rule application, and execution planning across the GraphQL processing pipeline.
Location is the component responsible for tracking and maintaining position information within the GraphQL source text during parsing and processing.
The Location component provides precise positional data that enables accurate error reporting and debugging capabilities. It tracks multiple types of position information:
- Line Numbers: The line position where tokens, nodes, or errors occur in the source text
- Column Numbers: The column position within a specific line
- Character Offsets: The absolute character position from the beginning of the source
- Source References: Links back to the original source document
The Location information is embedded throughout the parsing pipeline:
- Tokens: Each token produced by the Lexer includes location data
- AST Nodes: Every AST node contains location information for the source text it represents
- Errors: GraphQL errors include precise location data to help developers identify problematic areas
- Validation: Location data enables context-aware validation messages
The Location component is essential for developer experience as it transforms generic parsing or validation errors into actionable feedback. Instead of reporting "syntax error," the system can report "syntax error at line 15, column 23," allowing developers to quickly locate and fix issues in their GraphQL queries. This positional tracking is maintained throughout the entire GraphQL processing lifecycle, from initial tokenization through final execution, ensuring that any errors or warnings can be traced back to their precise origin in the source text.
Visitor is the component responsible for traversing and processing Abstract Syntax Tree (AST) nodes using the visitor pattern, providing a systematic and extensible approach to AST manipulation and analysis.
The Visitor component implements the visitor design pattern, which separates algorithms from the object structure on which they operate. This enables the definition of new operations on AST nodes without modifying the node classes themselves, promoting extensibility and maintainability in the GraphQL processing pipeline.
The Visitor provides structured AST traversal capabilities:
- Node Visitation: Systematically visits each AST node in a defined order (depth-first traversal)
- Enter/Leave Hooks: Provides entry and exit points for each node, allowing pre-processing and post-processing operations
- Type-Specific Handling: Enables different processing logic for each AST node type (Document, Field, Fragment, etc.)
- Traversal Control: Allows visitors to control traversal flow, including skipping subtrees or early termination
- Context Preservation: Maintains traversal context and state throughout the visiting process
The Visitor component supports various traversal patterns:
- Pre-order Traversal: Processes parent nodes before their children (enter phase)
- Post-order Traversal: Processes parent nodes after their children (leave phase)
- Conditional Traversal: Allows selective processing based on node types or conditions
- Stateful Traversal: Maintains accumulated state across multiple node visits
- Multi-pass Traversal: Enables multiple traversal passes for complex analysis tasks
Key visitor operations include:
- AST Transformation: Modifies or replaces AST nodes during traversal
- Information Collection: Gathers data from AST nodes for analysis purposes
- Validation Support: Enables validation rules to be applied systematically across the AST
- Code Generation: Supports code generation workflows that require comprehensive AST analysis
- Query Analysis: Facilitates query complexity analysis and optimization planning
The Visitor component integrates with other language components:
- Parser Integration: Processes ASTs produced by the Parser component
- Printer Integration: Works with the Printer to enable AST-to-text transformation
- Validator Integration: Provides the foundation for validation rule application
- Executor Integration: Supports execution planning and field resolution analysis
Visitor implementation features:
- Extensible Design: New visitor types can be easily added without modifying existing code
- Composable Patterns: Multiple visitors can be combined for complex processing workflows
- Error Handling: Provides mechanisms for error collection and propagation during traversal
- Performance Optimization: Implements efficient traversal algorithms to minimize processing overhead
- Memory Management: Manages traversal state efficiently to prevent memory leaks during large AST processing
The Visitor component is fundamental to the GraphQL-Go implementation, enabling modular and extensible AST processing that supports validation, execution planning, introspection, and development tooling throughout the GraphQL processing pipeline.
Printer is the component responsible for converting Abstract Syntax Tree (AST) nodes back into their textual GraphQL representation, essentially performing the reverse operation of the Parser component.
The Printer component serves as a bridge between the structured AST representation and human-readable GraphQL syntax. It enables various use cases including query formatting, AST manipulation debugging, and GraphQL document generation from programmatically constructed ASTs.
The Printer performs AST-to-text transformation by traversing AST nodes and generating corresponding GraphQL syntax:
- Document Printing: Converts document nodes back to complete GraphQL documents
- Operation Printing: Transforms operation definition nodes into query, mutation, or subscription syntax
- Selection Set Printing: Converts selection set nodes into properly formatted field selections with proper indentation
- Field Printing: Handles field nodes including names, aliases, arguments, and nested selections
- Fragment Printing: Processes both named fragment definitions and inline fragment nodes
- Value Printing: Converts value nodes back to their literal representations (strings, numbers, booleans, lists, objects)
- Type Printing: Transforms type reference nodes into their textual type notation
- Directive Printing: Handles directive nodes with their arguments and proper placement
The Printer component works closely with the Visitor component to traverse the AST structure:
- AST Traversal: Uses visitor pattern to systematically process each node type
- Node Transformation: Applies specific formatting rules for each AST node kind
- String Building: Constructs the final GraphQL string representation incrementally
- Formatting Control: Maintains proper indentation, spacing, and line breaks for readable output
Key features of the Printer implementation include:
- Reversible Operations: Ensures that Parser → Printer → Parser operations preserve semantic meaning
- Format Consistency: Produces consistently formatted GraphQL output regardless of input formatting
- Debugging Support: Enables developers to inspect and understand AST transformations
- Integration with Visitor: Leverages the visitor pattern for extensible and maintainable AST traversal
The Printer component is essential for development tooling, query analysis, and any scenario where programmatically generated or modified ASTs need to be converted back to GraphQL syntax for human consumption or further processing.
The Executor/Resolver is a core component responsible for executing GraphQL operations and resolving the fields requested in a query. It forms the bridge between the parsed GraphQL Abstract Syntax Tree (AST) and the actual data retrieval logic, orchestrating the process from query execution to final response construction.
- Executor: Traverses the AST produced by the Parser, determining the operation type (query, mutation, subscription) and evaluating each node.
- Resolver: Each field in the schema can specify a resolver function. The resolver is called by the Executor to fetch or compute the data for that field. If no custom resolver is specified, a default resolver is used, often retrieving values directly from the source object.
Responsibilities:
- Receives an operation (query/mutation/subscription) and variables.
- Traverses the AST, matching fields to resolver functions.
- Calls each resolver with the parent object, arguments, context, and info about the execution state.
- Handles asynchronous and nested field resolution, supporting fragments, directives, and error propagation.
- Aggregates the resolved data into the final response structure.
This design allows for extensibility—custom business logic, authentication, and side effects can be injected at the resolver level. The clear separation also makes the execution engine reusable and testable.
Implementation details can be found throughout the codebase, notably in files and PRs related to Executor and Resolver components.
References:
The Validator component is responsible for ensuring that incoming GraphQL queries conform to the GraphQL specification and the schema defined by the application. After the Parser creates the Abstract Syntax Tree (AST) from the query, the Validator traverses the AST and applies a set of validation rules.
Key responsibilities:
- Confirms that the query structure and field selections are valid according to the schema.
- Ensures that fragments and operations are used correctly and do not conflict.
- Checks for type compatibility, required arguments, valid variable usage, and directive application.
- Leverages the Visitor pattern for AST traversal, enabling modular validation rule implementations.
- Produces detailed and actionable error messages for invalid queries.
The Validator is essential for catching errors and enforcing GraphQL best practices before execution proceeds, contributing to the reliability and security of the system.
Notable improvements include:
- Enhanced error messages with suggestions.
- Deepened introspection queries.
- Support for schema language directives and deprecations.
References:
The Definition component is responsible for providing the building blocks and structures needed to define GraphQL schemas programmatically. It contains the core type definitions and interfaces that developers use to construct their GraphQL type system.
Key responsibilities:
- Provides foundational type definitions for creating GraphQL schemas including Object types, Interface types, Union types, Enum types, and Scalar types.
- Defines the structure and interfaces for field definitions, argument definitions, and input type definitions.
- Establishes the contracts and behavior patterns that all GraphQL types must follow.
- Enables programmatic schema construction by providing the necessary building blocks and type safety.
- Supports schema extension and composition through modular type definitions.
Core definition types provided:
- ObjectTypeDefinition: Defines GraphQL object types with fields and resolvers
- InterfaceTypeDefinition: Defines GraphQL interface types for polymorphic schemas
- UnionTypeDefinition: Defines GraphQL union types for type alternatives
- EnumTypeDefinition: Defines GraphQL enum types for predefined value sets
- ScalarTypeDefinition: Defines custom GraphQL scalar types
- InputTypeDefinition: Defines GraphQL input types for mutations and field arguments
- FieldDefinition: Defines individual fields within types including their types, arguments, and resolvers
- ArgumentDefinition: Defines arguments for fields and directives
The Definition component serves as the foundation for schema construction, enabling developers to build type-safe GraphQL APIs by providing well-defined interfaces and structures. It ensures consistency across the schema definition process and facilitates schema validation and introspection capabilities.
This component integrates closely with the Schema component to construct complete GraphQL schemas and with the Validator component to ensure schema definitions conform to GraphQL specification requirements.
The Schema component is the central definition and organizational structure for all types, queries, mutations, and subscriptions in a GraphQL service. It acts as the contract between the client and server, specifying what operations and data are available, and how they relate.
Key responsibilities:
- Defines root operation types (Query, Mutation, Subscription).
- Registers all object types, scalars, enums, interfaces, unions, and input types.
- Describes relationships between types, fields, arguments, and return values.
- Provides the basis for the Validator to enforce query correctness and for the Executor to resolve fields.
- Supports schema introspection, allowing clients to query the schema structure itself.
- Integrates schema language features (e.g., directives, deprecations, custom extensions).
The Schema enables modular, type-safe API development, supporting both code-first and schema-first approaches. In GraphQL-Go, schema construction can be programmatic, mirroring the graphql-js reference implementation, with Go types and resolvers mapped to schema elements.
Implementation enhancements include:
- Support for schema language directives and introspection improvements.
- Schema definition integration into the language components.
- Enhanced handling of types and arguments, including context propagation to executors.
References:
The Directives component manages the definition, registration, and application of directives in the GraphQL execution pipeline. Directives are special annotations that can be attached to fields, fragments, and other schema elements to modify query behavior at runtime or affect schema interpretation.
Key responsibilities:
- Defines built-in directives (such as
@include,@skip, and@deprecated) and supports user-defined custom directives. - Registers directive locations (e.g., on fields, fragments, schema definitions) in compliance with the GraphQL specification.
- Applies directive logic during query validation and execution phases, allowing conditional field inclusion/exclusion, deprecation warnings, and extensible custom behaviors.
- Ensures that directives are type-safe, context-aware, and validated against their declared arguments and locations.
- Supports directive introspection, enabling clients to discover available directives and their intended use.
Directives extend the expressiveness and adaptability of the GraphQL schema and queries, supporting common patterns like conditional data fetching and schema evolution with minimal changes.
Implementation highlights:
- Integrates tightly with the schema, validator, and executor components.
- Enables extensible directive creation for reusable, cross-cutting concerns.
References:
The Rules component encapsulates the set of validation rules applied to GraphQL operations to ensure they conform to the GraphQL specification and the schema’s constraints. Each rule encapsulates a specific aspect of query validity, such as detecting field selection conflicts, enforcing fragment naming, or checking variable usage.
Key responsibilities:
- Defines and organizes individual validation rules, each targeting a different aspect of query or schema correctness according to the GraphQL specification.
- Integrates with the Validator and Visitor components to traverse the AST and apply rules systematically.
- Reports detailed error messages for violations, aiding developers in quickly resolving issues.
- Supports extensibility, allowing custom rules to be defined and composed with built-in rules.
- Handles advanced concerns such as overlapping fields, argument defaults, and fragment cycles.
The Rules component ensures the reliability and safety of GraphQL queries by systematically enforcing correctness, enabling robust validation workflows for both built-in and user-defined constraints.
Implementation highlights:
- Maintains parity with the reference graphql-js implementation.
- Improvements include enhanced validation error messages and deeper introspection for more comprehensive rule enforcement.
References:
The Introspection component enables clients and tools to query the schema structure and metadata at runtime. This functionality is central to GraphQL’s self-describing nature, supporting features like schema documentation, auto-completion, validation, and IDE integrations.
Key responsibilities:
- Implements the introspection system as defined by the GraphQL specification, exposing types, fields, directives, and their relationships via special introspection queries (such as
__schema,__type, and__directive). - Integrates with the Executor and Schema components to resolve introspection fields dynamically based on the current schema.
- Supports deep, recursive queries to inspect nested types and directives, enabling clients to fully explore the schema capabilities.
- Provides compatibility with tools and environments that rely on introspection, such as GraphQL Playground, GraphiQL, and code generators.
Implementation details:
- The introspection system is kept up to date with changes from the graphql-js reference, ensuring compatibility and feature parity.
- The standard introspection query used for validation and testing can be found at: testutil/introspection_query.go
References:
The TypeInfo component provides utilities for tracking and managing GraphQL type information during syntax tree traversal, such as when validating or analyzing queries. It encapsulates the logic required to keep track of the current type context, input and output types, field definitions, directives, and arguments as the AST is visited.
Key responsibilities:
- Maintains the current type context while traversing the GraphQL AST (Abstract Syntax Tree), updating its internal state as new nodes are entered or exited.
- Exposes methods to access the current type, parent type, input type, field definition, directive, and argument.
- Supports validation and schema analysis by providing accurate and up-to-date type information at any point in the traversal.
- Integrates with the Validator and Visitor components, serving as the backbone for context-aware validation rules and advanced analysis.
- Enables correct interpretation of fragments, fields, arguments, and directives as specified in the GraphQL specification.
Implementation highlights:
- The design mirrors the reference implementation in graphql-js, ensuring feature parity and reliability.
- TypeInfo is stateful and designed for reuse across different traversals within the same schema.
- Provides a clear interface for external tools and rules to query type context without directly mutating internal state.
References:
The compatibility-unit-tests framework contains the following main components that enable validation of GraphQL implementation compatibility against the graphql-js reference implementation:
-
App: The main application orchestrator that coordinates the compatibility validation process between different GraphQL implementations.
-
Cmd: The command-line interface component that provides user interaction capabilities for selecting GraphQL implementations and configuring validation parameters.
-
Extractor: The component responsible for extracting unit test names and structures from GraphQL implementations, supporting both JavaScript (graphql-js) and Go implementations.
-
Implementation: The component that defines and manages different GraphQL implementation configurations, including repository information, test extraction strategies, and implementation-specific metadata.
-
Result: The component that handles the collection, aggregation, and presentation of compatibility validation results, including success/failure metrics and detailed comparison outcomes.
-
Types: The component that defines the core data structures and type definitions used throughout the compatibility validation framework.
-
Validator: The component that performs the actual compatibility comparison between the reference implementation (graphql-js) and target implementations, validating test coverage and identifying compatibility gaps.
The compatibility framework operates by extracting unit test names from both the reference graphql-js implementation and the target implementation, then comparing them to identify missing tests or implementation gaps. This systematic approach ensures that GraphQL implementations maintain compatibility with the standard reference implementation.
References:
The compatibility-standard-definitions framework contains the following main components that enable validation of GraphQL implementation type system compatibility against the GraphQL specification and the graphql-js reference implementation:
-
Puller: The component responsible for cloning GraphQL repositories including the GraphQL specification repository, the GraphQL JavaScript reference implementation repository, and target GraphQL implementation repositories. It manages the retrieval and synchronization of source code from multiple repositories to enable cross-implementation compatibility analysis.
-
Extractor: The component that extracts type system definitions from different sources using multiple strategies. It pulls type system definitions by parsing from the GraphQL specification repository, by introspection from the GraphQL JavaScript reference implementation, and by introspection from target GraphQL implementations. This component bridges different extraction methodologies to create a unified comparison baseline.
-
Executor: The component that executes type system definitions introspection results on GraphQL implementations. It processes the extracted type system definitions and applies them to target implementations to validate their compatibility and correctness against the standard definitions.
-
Validator: The component that performs comprehensive validation by comparing schemas and type system definitions across different implementations. It validates the GraphQL specification schema against the GraphQL JavaScript reference implementation schema, compares GraphQL implementation schemas against the GraphQL specification schema, and performs detailed comparisons of type system definitions between the specification and implementations.
The standard definitions framework operates by systematically extracting, processing, and comparing type system definitions from the GraphQL specification, the reference implementation, and target implementations. This approach ensures that GraphQL implementations maintain structural compatibility with the standard type system definitions as defined by the GraphQL specification.
References:
The compatibility-user-acceptance framework contains the following main components that enable validation of GraphQL implementation developer experience metrics against the graphql-js reference implementation:
-
Extractor: The component responsible for extracting GitHub repository metrics from GraphQL implementations. It fetches comprehensive repository data including stars count, issues statistics, pull requests metrics, fork counts, license information, last commit dates, contributor counts, and GraphQL specification version compatibility. The extractor provides both live GitHub API integration and test data capabilities for development and validation purposes.
-
Repository: The component that contains extracted repository metrics as structured data. It serves as the data model for storing and organizing the GitHub repository information collected by the extractor, providing a standardized interface for accessing metrics such as stars count, issues, pull requests, and other repository characteristics used in compatibility validation.
-
CLI Tool: The component that provides a command-line interface for running compatibility validation between GraphQL implementations. It orchestrates the extraction process, compares metrics against reference thresholds, calculates difference ratios, and presents results in a user-friendly table format. The CLI tool validates compatibility by comparing various developer experience metrics including repository engagement, community activity, and specification compliance indicators.
The user acceptance framework operates by systematically extracting GitHub repository metrics from target GraphQL implementations, comparing them against the graphql-js reference implementation, and calculating difference ratios to determine compatibility levels. This approach ensures that GraphQL implementations maintain acceptable developer experience standards and community engagement metrics that align with the reference implementation's user acceptance patterns.
References:
How is the distribution of the task accomplished ? Let’s add here screenshots of the PRs, maybe a table, let’s break the task into a simpler list and here let’s add more detailed tasks.
Also we could add a taks mapping against the components and linking with the code components in Go down to graphql js components.
Also let’s add percentage of accomplishing for reaching the desire covering tests results of graphql-js.
Also we are matching the tasks names against the set of unit tests in order to make sure the compatibility against the graphql-js reference implementation.
Task Name GraphQL JS Unit Test GraphQL Go Unit Test Pull Request
TODO: Add screenshots of each tool, for main components of gql.
The open-source project was centralized in GitHub, where we leverage different features that enabled the openness of the library, such as:
Comments: Pull Requests: Issues: Stats: Documentation: Releases: Tags:
Also we had a continuous integration pipeline that guarantees that new functionality was fully tested against all the test suite, we leverage the following both providers: TravisCI and CircleCI.
TravisCI was deprecated in favor of CircleCI due to the latter having more modern functionality that was better for the future proof of the project.
The following features of the continuous integration are in place: Building: Per Go version building:
To guarantee that the suite of unit tests matched a high level standard set at a percentage accepted by the open-source community we leveraged Coveralls, and the following features were used:
Besides that we also leverage the Go’s feature “go doc” in order to self document the library
Artifacts were created as part of this thesis, most of them are Go open-source libraries that tackle specific purposes and detailed below:
GitHub Repository: https://github.com/graphql-go/graphql
Description:
Go open-source library that implements the graphql-js reference implementation.
GitHub Repository: https://github.com/graphql-go/compatibility-base
Description:
Go open-source library that has the base reusable code used by other graphql-go/* libraries.
GitHub Repository: https://github.com/graphql-go/compatibility-unit-tests
Description:
Go open-source library that compares unit test names between a GraphQL reference implementation and other GraphQL implementations.
GitHub Repository: https://github.com/graphql-go/compatibility-standard-definitions
Description:
Go open-source library that compares the GraphQL reference implementation type system with other GraphQL implementations.
GitHub Repository: https://github.com/graphql-go/compatibility-user-acceptance
Description:
Go open-source library that compares acceptance criteria between the GraphQL reference implementation and other GraphQL implementations.
GitHub Repository: https://github.com/chris-ramon/design-doc-self-generator
Description:
Go open-source library that obtains GitHub information.
The Go-based implementation, graphql-go/graphql, is a stable, open-source library developed in accordance with the April 2016 GraphQL Specification. It implements the full language, type system, validation logic, execution semantics, and introspection capabilities—within a simple and modular architecture.
The compatibility study compares the official JavaScript reference implementation, graphql/graphql-js, with the Go library graphql-go/graphql. A custom open-source tool uses introspection to compare internal type systems, aiming to confirm compatibility across both libraries.
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-goexamples, we adhere to Go coding style conventions. For instance, allResolvefunctions return two values—data and anerror—which is a fundamental idiom in Go for error handling.
Scalars are the basic leaf values in GraphQL.
int: {
type: GraphQLInt,
resolve() {
return 20;
},
},"int": &graphql.Field{
Type: graphql.Int,
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
return 20, nil
},
},float: {
type: GraphQLFloat,
resolve() {
return 20.01;
},
},"float": &graphql.Field{
Type: graphql.Float,
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
return 20.01, nil
},
},string: {
type: GraphQLString,
resolve() {
return "str";
},
},"string": &graphql.Field{
Type: graphql.String,
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
return "str", nil
},
},boolean: {
type: GraphQLBoolean,
resolve() {
return true;
},
},"boolean": &graphql.Field{
Type: graphql.Boolean,
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
return true, nil
},
},ID: {
type: GraphQLID,
resolve() {
return "d983b9d9-681c-4059-b5a3-5329d1c6f82d";
},
},"ID": &graphql.Field{
Type: graphql.ID,
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
return "d983b9d9-681c-4059-b5a3-5329d1c6f82d", nil
},
},Defines structured data types with fields.
const objectTypeWithArguments = new GraphQLObjectType({
name: "objectTypeWithArguments",
description: "An object with arguments.",
fields: () => {
return {
name: {
description: "The name of the object.",
type: GraphQLString,
},
};
},
});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,
},
},
})Used to abstract fields shared by multiple types.
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;
},
});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
},
})Allows fields to return one of multiple object types.
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;
},
});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
},
})Defines a fixed set of possible values.
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.",
},
},
});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.",
},
},
})Used for passing structured input to queries or mutations.
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.",
},
};
},
});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.",
},
},
})Represents arrays of values or objects.
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" },
];
},
},"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
},
},Represents a declaration that a type disallows null.
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",
};
},
},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
},
},Allows for conditional exclusion during execution.
query ExampleQuery($skipUserName: Boolean!, $skipProductPrice: Boolean!, ...) {
// ...
}query ExampleQuery($skipUserName: Boolean!, $skipProductPrice: Boolean!, ...) {
// ...
}Allows for conditional inclusion during execution.
query ExampleQuery(... , $includeUserName: Boolean!, $includeProductPrice: Boolean!) {
// ...
}query ExampleQuery(... , $includeUserName: Boolean!, $includeProductPrice: Boolean!) {
// ...
}Represents an operation to mutate data.
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,
};
},
},
},
}),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
},
},
},
})Represents an operation for subscribing to data.
subscription: new GraphQLObjectType({
name: "RootSubscriptionType",
fields: {
userAdded: {
type: userType,
resolve() {
return {
type: "user",
id: `user-${Date.now()}`,
name: "New User Added",
};
},
},
},
}),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.
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.
graphql-js |
graphql-go |
|---|---|
| Identical query, mutation, and subscription outputs | Identical query, mutation, and subscription outputs |
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"
}
}
}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"
}
}
}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"
}
}
}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.
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.
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-gocompatible with the GraphQL type system as defined and implemented bygraphql-js?”
The answer, supported by introspection-based comparisons, is yes.
What are the recommendations for future related work that is strongly tie to this thesis ?
-
Usability Testing: UX testing was not covered as part of this thesis, but further work in regards the API design could be done, for example the most high level APIs could be extracted from the reference implementation and compared against the implementation. Also each implementation could be tested as if the APIs are using the most accepted best practices from the community as if it cover the best DX.
-
Performance Testing: Similar than the compatibility libraries that were created as part of this thesis, another library could created to compare the response time of queries, mutations and subscriptions of a set of operations so reference and implementations could be compared.
I would like to thank OpenAI’s GPT‑4o model for its collaborative support in drafting, refining, and structuring parts of this thesis—particularly in articulating the implementation and validation results with clarity and precision.
-
Facebook, Inc. (2019). GraphQL standard reference: https://spec.graphql.org/October2021
-
GraphQL Spec: https://github.com/graphql/graphql-spec
-
Official Website: https://graphql-go.github.io/graphql-go.org
-
GraphQL Queries as state machine: https://rmosolgo.github.io/ruby/graphql/2016/11/12/graphql-query-as-a-state-machine.html
-
GraphQL Spec License: https://jointdevelopment.org
-
Open Systems Interconnection: https://aws.amazon.com/what-is/osi-model
-
RPC: https://datatracker.ietf.org/doc/html/rfc5531 https://datatracker.ietf.org/doc/html/rfc1050
-
GraphQL Implementations Releases: https://youtu.be/783ccP__No8?t=1253
-
ISO 13485: https://www.iso.org/obp/ui/en/#iso:std:iso:13485:ed-3:v1:en
-
ISO 15026: https://www.iso.org/standard/73567.html







