graphql-go-tools

command module
v2.0.0-rc.62 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 17, 2024 License: MIT Imports: 15 Imported by: 0

README

GoDoc v1-ci

GraphQL Router / API Gateway Framework written in Golang

Replacement for Apollo Router

If you're looking for a complete ready-to-use Open Source Router for Federation, have a look at the Cosmo Router which is based on this library.

Cosmo Router wraps this library and provides a complete solution for Federated GraphQL including the following features:

  • Federation Gateway
  • OpenTelemetry Metrics & Distributed Tracing
  • Prometheus Metrics
  • GraphQL Schema Usage Exporter
  • Health Checks
  • GraphQL Playground
  • Execution Tracing Exporter & UI in the Playground
  • Federated Subscriptions over WebSockets (graphql-ws & graphql-transport-ws protocol support) and SSE
  • Authentication using JWKS & JWT
  • Highly available & scalable using S3 as a backend for the Router Config
  • Persisted Operations / Trusted Documents
  • Traffic Shaping (Timeouts, Retries, Header & Body Size Limits, Subgraph Header forwarding)
  • Custom Modules & Middleware

Notes

This library is used in production at WunderGraph. We've recently introduced a v2 module that is not completely backwards compatible with v1, hence the major version bump. The v2 module contains big rewrites in the engine package, mainly to better support GraphQL Federation. Please consider the v1 module as deprecated and move to v2 as soon as possible.

We have customers who pay us to maintain this library and steer the direction of the project. Contact us if you're looking for commercial support, features or consulting.

Performance

The architecture of this library is designed for performance, high-throughput and low garbage collection overhead. The following benchmark measures the "overhead" of loading and resolving a GraphQL response from four static in-memory Subgraphs at 0,007459 ms/op. In more complete end-to-end benchmarks, we've measured up to 8x more requests per second and 8x lower p99 latency compared to Apollo Router, which is written in Rust.

cd v2/pkg/engine
go test -run=nothing -bench=Benchmark_NestedBatchingWithoutChecks -memprofile memprofile.out -benchtime 3s && go tool pprof memprofile.out
goos: darwin
goarch: arm64
pkg: github.com/wundergraph/graphql-go-tools/v2/pkg/engine/resolve
Benchmark_NestedBatchingWithoutChecks-10          473186              7134 ns/op          52.00 MB/s        2086 B/op         36 allocs/op

Tutorial

If you're here to learn how to use this library to build your own custom GraphQL Router or API Gateway, here's a speed run tutorial for you, based on how we use this library in Cosmo Router.

package main

import (
  "bytes"
  "context"
  "fmt"

  "github.com/cespare/xxhash/v2"
  "github.com/wundergraph/graphql-go-tools/v2/pkg/ast"
  "github.com/wundergraph/graphql-go-tools/v2/pkg/astnormalization"
  "github.com/wundergraph/graphql-go-tools/v2/pkg/astparser"
  "github.com/wundergraph/graphql-go-tools/v2/pkg/astprinter"
  "github.com/wundergraph/graphql-go-tools/v2/pkg/asttransform"
  "github.com/wundergraph/graphql-go-tools/v2/pkg/astvalidation"
  "github.com/wundergraph/graphql-go-tools/v2/pkg/astvisitor"
  "github.com/wundergraph/graphql-go-tools/v2/pkg/engine/datasource/staticdatasource"
  "github.com/wundergraph/graphql-go-tools/v2/pkg/engine/plan"
  "github.com/wundergraph/graphql-go-tools/v2/pkg/engine/resolve"
  "github.com/wundergraph/graphql-go-tools/v2/pkg/operationreport"
)

/*
ExampleParsePrintDocument shows you the most basic usage of the library.
It parses a GraphQL document and prints it back to a writer.
*/
func ExampleParsePrintDocument() {

	input := []byte(`query { hello }`)

	report := &operationreport.Report{}
	document := ast.NewSmallDocument()
	parser := astparser.NewParser()
	printer := &astprinter.Printer{}

	document.Input.ResetInputBytes(input)
	parser.Parse(document, report)

	if report.HasErrors() {
		panic(report.Error())
	}

	out := &bytes.Buffer{}
	err := printer.Print(document, nil, out)
	if err != nil {
		panic(err)
	}
	fmt.Println(out.String()) // Output: query { hello }
}

/*
Okay, that was easy, but also not very useful.
Let's try to parse a more complex document and print it back to a writer.
*/

// ExampleParseComplexDocument shows a special feature of the printer
func ExampleParseComplexDocument() {

	input := []byte(`
		query {
			hello
			foo {
				bar
			}
		}
	`)

	report := &operationreport.Report{}
	document := ast.NewSmallDocument()
	parser := astparser.NewParser()
	printer := &astprinter.Printer{}

	document.Input.ResetInputBytes(input)
	parser.Parse(document, report)

	if report.HasErrors() {
		panic(report.Error())
	}

	out := &bytes.Buffer{}
	err := printer.Print(document, nil, out)
	if err != nil {
		panic(err)
	}
	fmt.Println(out.String()) // Output: query { hello foo { bar } }
}

/*
You'll notice that the printer removes all whitespace and newlines.
But what if we wanted to print the document with indentation?
*/

func ExamplePrintWithIndentation() {

	input := []byte(`
		query {
			hello
			foo {
				bar
			}
		}
	`)

	report := &operationreport.Report{}
	document := ast.NewSmallDocument()
	parser := astparser.NewParser()

	document.Input.ResetInputBytes(input)
	parser.Parse(document, report)

	if report.HasErrors() {
		panic(report.Error())
	}

	out, err := astprinter.PrintStringIndent(document, nil, "  ")
	if err != nil {
		panic(err)
	}
	fmt.Println(out)
	// Output: query {
	//   hello
	//   foo {
	//     bar
	//   }
	// }
}

/*
Okay, fantastic. We can parse and print GraphQL documents.
As a next step, we could analyze the document and extract some information from it.
What if we wanted to know the name of the operation in the document, if any?
And what if we wanted to know about the Operation type?
*/

func ExampleParseOperationNameAndType() {

	input := []byte(`
		query MyQuery {
			hello
			foo {
				bar
			}
		}
	`)

	report := &operationreport.Report{}
	document := ast.NewSmallDocument()
	parser := astparser.NewParser()

	document.Input.ResetInputBytes(input)
	parser.Parse(document, report)

	if report.HasErrors() {
		panic(report.Error())
	}

	operationCount := 0
	var (
		operationNames []string
		operationTypes []ast.OperationType
	)

	for _, node := range document.RootNodes {
		if node.Kind != ast.NodeKindOperationDefinition {
			continue
		}
		operationCount++
		name := document.RootOperationTypeDefinitionNameString(node.Ref)
		operationNames = append(operationNames, name)
		operationType := document.RootOperationTypeDefinitions[node.Ref].OperationType
		operationTypes = append(operationTypes, operationType)
	}

	fmt.Println(operationCount) // Output: 1
	fmt.Println(operationNames) // Output: [MyQuery]
}

/*
We've now seen how to analyze the document and learn a bit about it.
We could now add some validation to our application,
e.g. we could check for the number of operations in the document,
and return an error if there are multiple anonymous operations.

We could also validate the Operation content against a schema.
But before we do this, we need to normalize the document.
This is important because validation relies on the document being normalized.
It was much easier to build the validation and many other features on top of a normalized document.

Normalization is the process of transforming the document into a canonical form.
This means that the document is transformed in a way that makes it easier to reason about it.
We inline fragments, we remove unused fragments,
we remove duplicate fields, we remove unused variables,
we remove unused operations etc...

So, let's normalize the document!
*/

func ExampleNormalizeDocument() {

	input := []byte(`
		query MyQuery {
			hello
			hello
			foo {
				bar
				bar
			}
			...MyFragment
		}

		fragment MyFragment on Query {
			hello
			foo {
				bar
			}
		}
	`)

	schema := []byte(`
		type Query {
			hello: String
			foo: Foo
		}
	
		type Foo {
			bar: String
		}
	`)

	report := &operationreport.Report{}
	document := ast.NewSmallDocument()
	parser := astparser.NewParser()

	document.Input.ResetInputBytes(input)
	parser.Parse(document, report)

	if report.HasErrors() {
		panic(report.Error())
	}

	schemaDocument := ast.NewSmallDocument()
	schemaParser := astparser.NewParser()
	schemaDocument.Input.ResetInputBytes(schema)
	schemaParser.Parse(schemaDocument, report)

	if report.HasErrors() {
		panic(report.Error())
	}

	// graphql-go-tools is very strict about the schema
	// the above GraphQL Schema is not fully valid, e.g. the `schema { query: Query }` part is missing
	// we can fix this automatically by merging the schema with a base schema
	err := asttransform.MergeDefinitionWithBaseSchema(schemaDocument)
	if err != nil {
		panic(err)
	}

	// you can customize what rules the normalizer should apply
	normalizer := astnormalization.NewWithOpts(
		astnormalization.WithExtractVariables(),
		astnormalization.WithInlineFragmentSpreads(),
		astnormalization.WithRemoveFragmentDefinitions(),
		astnormalization.WithRemoveNotMatchingOperationDefinitions(),
	)

	// It's generally recommended to always give your operation a name
	// If it doesn't have a name, just add one to the AST before normalizing it
	// This is not strictly necessary, but ensures that all normalization rules work as expected
	normalizer.NormalizeNamedOperation(document, schemaDocument, []byte("MyQuery"), report)

	if report.HasErrors() {
		panic(report.Error())
	}

	out, err := astprinter.PrintStringIndent(document, nil, "  ")
	if err != nil {
		panic(err)
	}

	fmt.Println(out)
	// Output: query MyQuery {
	//   hello
	//   foo {
	//     bar
	//   }
	// }
}

/*
Okay, that was a lot of work, but now we have a normalized document.
As you can see, all the duplicate fields have been removed and the fragment has been inlined.

What can we do with it?
Well, the possibilities are endless,
but why don't we start with validating the document against a schema?
Alright. Let's do it!
*/

func ExampleValidateDocument() {
	schemaDocument := ast.NewSmallDocument()
	operationDocument := ast.NewSmallDocument()
	report := &operationreport.Report{}
	validator := astvalidation.DefaultOperationValidator()
	validator.Validate(schemaDocument, operationDocument, report)
	if report.HasErrors() {
		panic(report.Error())
	}
}

/*
Fantastic, we've now got a GraphQL document that is valid against a schema.

As a next step, we could generate a cache key for the document.
This is very useful if we want to start doing expensive operations afterward that could be de-duplicated or cached.
At the same time, generating a cache key from a normalized document is not as trivial as it sounds.
Let's take a look!
*/

func ExampleGenerateCacheKey() {
	operationDocument := ast.NewSmallDocument()
	schemaDocument := ast.NewSmallDocument()
	report := &operationreport.Report{}

	normalizer := astnormalization.NewWithOpts(
		astnormalization.WithExtractVariables(),
		astnormalization.WithInlineFragmentSpreads(),
		astnormalization.WithRemoveFragmentDefinitions(),
		astnormalization.WithRemoveNotMatchingOperationDefinitions(),
	)

	normalizer.NormalizeNamedOperation(operationDocument, schemaDocument, []byte("MyQuery"), report)
	printer := &astprinter.Printer{}
	keyGen := xxhash.New()
	err := printer.Print(operationDocument, schemaDocument, keyGen)
	if err != nil {
		panic(err)
	}

	// you might be thinking that we're done now, but we're not
	// we've extracted the variables, so we need to add them to the cache key

	_, err = keyGen.Write(operationDocument.Input.Variables)
	if err != nil {
		panic(err)
	}

	key := keyGen.Sum64()
	fmt.Printf("%x", key) // Output: {cache key}
}

/*
Good job! We now have a correct cache key for the document.
We're using this ourselves in production to de-duplicate e.g. planning the execution of a GraphQL Operation.

There's just one problem with the above code.
An attacker could easily send the same document with a different Operation name and get a different cache key.
This could quite easily fill up our cache with duplicate entries.
To prevent this, we can make the operation name static.
Let's change out code to account for this.
*/

func ExampleGenerateCacheKeyWithStaticOperationName() {

	staticOperationName := []byte("O")

	operationDocument := ast.NewSmallDocument()
	schemaDocument := ast.NewSmallDocument()
	report := &operationreport.Report{}

	normalizer := astnormalization.NewWithOpts(
		astnormalization.WithExtractVariables(),
		astnormalization.WithInlineFragmentSpreads(),
		astnormalization.WithRemoveFragmentDefinitions(),
		astnormalization.WithRemoveNotMatchingOperationDefinitions(),
	)

	// First, we add the static operation name to the document and get an "address" to the byte slice (string) in the document
	// We cannot just add a string to an AST because the AST only stores references to byte slices
	// Storing strings in AST nodes would be very inefficient and would require a lot of allocations
	nameRef := operationDocument.Input.AppendInputBytes(staticOperationName)

	for _, node := range operationDocument.RootNodes {
		if node.Kind != ast.NodeKindOperationDefinition {
			continue
		}
		name := operationDocument.OperationDefinitionNameString(node.Ref)
		if name != "MyQuery" {
			continue
		}
		// Then we set the name of the operation to the address of the static operation name
		// Now we have renamed MyQuery to O
		operationDocument.OperationDefinitions[node.Ref].Name = nameRef
	}

	// Now we can normalize the modified document
	// All Operations that don't have the name O will be removed
	normalizer.NormalizeNamedOperation(operationDocument, schemaDocument, staticOperationName, report)

	printer := &astprinter.Printer{}
	keyGen := xxhash.New()
	err := printer.Print(operationDocument, schemaDocument, keyGen)
	if err != nil {
		panic(err)
	}

	_, err = keyGen.Write(operationDocument.Input.Variables)
	if err != nil {
		panic(err)
	}

	key := keyGen.Sum64()
	fmt.Printf("%x", key) // Output: {cache key}
}

/*
With these changes, the name of the operation doesn't matter anymore.
Independent of the name, the cache key will always be the same.

As a next step, we could start planning the execution of the operation.
This is a very complex topic, so we'll just show you how to plan the operation.
Going into detail would be beyond the scope of this example.
It took us years to get this right, so we won't be able to explain it in a few lines of code.

graphql-go-tools is not a GraphQL server by itself.
It's a library that you can use to build Routers, Gateways, or even GraphQL Server frameworks on top of it.
What this means is that there's no built-in support to define "resolvers".
Instead, you have to define DataSources that are used to resolve fields.

A DataSource can be anything, e.g. a static value, a HTTP JSON API, a GraphQL API, a WASM Lambda, a Database etc.
It's up to you to implement the DataSource interface.

The simplest DataSource is the StaticDataSource.
It's a DataSource that returns a static value for a field.
Let's see how to use it!

You have to attach the DataSource to one or more fields in the schema,
and you have to provide a config and a factory for the DataSource,
so that the planner knows how to create an execution plan for the DataSource and an "instance" of the DataSource.
*/

func ExamplePlanOperation() {
    staticDataSource, err := plan.NewDataSourceConfiguration[staticdatasource.Configuration](
      "StaticDataSource",
      &staticdatasource.Factory[staticdatasource.Configuration]{},
      &plan.DataSourceMetadata{
        RootNodes: []plan.TypeField{
          {
            TypeName:   "Query",
            FieldNames: []string{"hello"},
          },
        },
      },
      staticdatasource.Configuration{
        Data: `{"hello":"world"}`,
      },
    )
  
    config := plan.Configuration{
      DataSources: []plan.DataSource{
        staticDataSource,
      },
      Fields: []plan.FieldConfiguration{
        {
          TypeName:              "Query", // attach this config to the Query type and the field hello
          FieldName:             "hello",
          DisableDefaultMapping: true,              // disable the default mapping for this field which only applies to GraphQL APIs
          Path:                  []string{"hello"}, // returns the value of the field "hello" from the JSON data
        },
      },
      IncludeInfo: true,
    }
	if err != nil {
        panic(err)
    }

	operationDocument := ast.NewSmallDocument() // containing the following query: query O { hello }
	schemaDocument := ast.NewSmallDocument()
	report := &operationreport.Report{}
	operationName := "O"

	planner := plan.NewPlanner(context.Background(), config)
	executionPlan := planner.Plan(operationDocument, schemaDocument, operationName, report)
	if report.HasErrors() {
		panic(report.Error())
	}
	fmt.Printf("%+v", executionPlan) // Output: Plan...
}

/*
As you can see, the planner has created a plan for us.
This plan can now be executed by using the Resolver.
*/

func ExampleExecuteOperation() {
	var preparedPlan plan.Plan
	resolver := resolve.New(context.Background(), true)

	ctx := resolve.NewContext(context.Background())

	switch p := preparedPlan.(type) {
	case *plan.SynchronousResponsePlan:
		out := &bytes.Buffer{}
		err := resolver.ResolveGraphQLResponse(ctx, p.Response, nil, out)
		if err != nil {
			panic(err)
		}
		fmt.Println(out.String()) // Output: {"data":{"hello":"world"}}
	case *plan.SubscriptionResponsePlan:
		// this is a Query, so we ignore Subscriptions for now, but they are supported
	}
}

/*
Well done! You've now seen how to parse, print, validate, normalize, plan and execute a GraphQL document.
You've built a complete GraphQL API Gateway from scratch.
That said, this was really just the tip of the iceberg.

When you look under the hood of graphql-go-tools, you'll notice that a lot of its functionality is built on top of the AST,
more specifically on top of the "astvisitor" package.
It comes with a lot of useful bells and whistles that help you to solve complex problems.

You'll notice that almost everything, from normalization to printing, planning, validation, etc.
is built on top of the AST and the astvisitor package.

Let's take a look at a basic example of how to use the astvisitor package to build higher level functionality.
Here's a simple use case:

Let's walk through the AST of a GraphQL document and extract all tuples of (TypeName, FieldName).
This is useful, e.g. when you want to extract information about the fields that are used in a document.
*/

type visitor struct {
	walker                *astvisitor.Walker
	operation, definition *ast.Document
	typeFields            [][]string
}

func (v *visitor) EnterField(ref int) {
	// get the name of the enclosing type (Query)
	enclosingTypeName := v.walker.EnclosingTypeDefinition.NameString(v.definition)
	// get the name of the field (hello)
	fieldName := v.operation.FieldNameString(ref)
	// get the type definition of the field (String)
	definitionRef, exists := v.walker.FieldDefinition(ref)
	if !exists {
		return
	}
	// get the name of the field type (String)
	fieldTypeName := v.definition.FieldDefinitionTypeNameString(definitionRef)
	v.typeFields = append(v.typeFields, []string{enclosingTypeName, fieldName, fieldTypeName})
}

func ExampleWalkAST() {

	operationDocument := ast.NewSmallDocument() // containing the following query: query O { hello }
	schemaDocument := ast.NewSmallDocument()    // containing the following schema: type Query { hello: String }
	report := &operationreport.Report{}

	walker := astvisitor.NewWalker(24)

	vis := &visitor{
		walker:     &walker,
		operation:  operationDocument,
		definition: schemaDocument,
	}

	walker.RegisterEnterFieldVisitor(vis)
	walker.Walk(operationDocument, schemaDocument, report)
	if report.HasErrors() {
		panic(report.Error())
	}
	fmt.Printf("%+v", vis.typeFields) // Output: [[Query hello String]]
}

/*
This is just a very basic example of what you can do with the astvisitor package,
but you can see that it's very powerful and flexible.

You can register callbacks for every AST node and do whatever you want with it.
In addition, the walker helps you to keep track of the current position in the AST,
and it can help you to figure out the enclosing type of a field,or the ancestors or a node.
*/

I hope this tutorial gave you a good overview of what you can do with this library. If you have any questions, feel free to open an issue. Following, here's a list of all the important packages in this library and what problems they solve.

  • ast: the GraphQL AST and all the logic to work with it.
  • astimport: import GraphQL documents from one AST into another
  • astnormalization: normalize a GraphQL document
  • astparser: parse a string into a GraphQL AST
  • astprinter: print a GraphQL AST into a string
  • asttransform: transform a GraphQL AST, e.g. merge it with a base schema
  • astvalidation: validate a GraphQL AST against a schema
  • astvisitor: walk through a GraphQL AST and execute callbacks for every node
  • engine/datasource: the DataSource interface and some implementations
  • engine/datasource/graphql_datasource: the GraphQL DataSource implementation, including support for Federation
  • engine/plan: plan the execution of a GraphQL document
  • engine/resolve: execute the plan
  • introspection: convert a GraphQL Schema into an introspection JSON document
  • lexer: turn a string containing a GraphQL document into a list of tokens
  • playground: add a GraphQL Playground to your Go HTTP server
  • subscription: implements GraphQL Subscriptions over WebSockets and SSE

Contributors

  • Jens Neuse (Project Lead & Active Maintainer)
    • Initial version of graphql-go-tools
    • Currently responsible for the loader and resolver implementation
  • Sergiy Petrunin 🇺🇦 (Active Maintainer)
    • Helped cleaning up the API of the pipeline package
    • Refactored the ast package into multiple files
    • Author of the introspection converter (introspection JSON -> AST)
    • Fixed various bugs in the parser & visitor & printer
    • Refactored and enhanced the astimport package
    • Current maintainer of the plan package
  • Patric Vormstein (Active Maintainer)
    • Fixed lexer on windows
    • Author of the graphql package to simplify the usage of the library
    • Refactored the http package to simplify usage with http servers
    • Author of the starwars package to enhance testing
    • Refactor of the Subscriptions Implementation
  • Mantas Vidutis (Inactive)
    • Contributions to the http proxy & the Context Middleware
  • Jonas Bergner (Inactive)
    • Contributions to the initial version of the parser, contributions to the tests
    • Implemented Type Extension merging (deprecated)
  • Vasyl Domanchuk (Inactive)
    • Implemented the logic to generate a federation configuration
    • Added federation example
    • Added the initial version of the batching implementation

Contributions

Feel free to file an issue in case of bugs. We're open to your ideas to enhance the repository.

You are open to contribute via PR's. Please open an issue to discuss your idea before implementing it so we can have a discussion. Make sure to comply with the linting rules. You must not add untested code.

Documentation

Overview

Package graphql-go-tools is library to create GraphQL services using the go programming language.

About GraphQL

GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.

Source: https://graphql.org

About this library

This library is intended to be a set of low level building blocks to write high performance and secure GraphQL applications. Use cases could range from writing layer seven GraphQL proxies, firewalls, caches etc.. You would usually not use this library to write a GraphQL server yourself but to build tools for the GraphQL ecosystem.

To achieve this goal the library has zero dependencies at its core functionality. It has a full implementation of the GraphQL AST and supports lexing, parsing, validation, normalization, introspection, query planning as well as query execution etc.

With the execution package it's possible to write a fully functional GraphQL server that is capable to mediate between various protocols and formats. In it's current state you can use the following DataSources to resolve fields: - Static data (embed static data into a schema to extend a field in a simple way) - HTTP JSON APIs (combine multiple Restful APIs into one single GraphQL Endpoint, nesting is possible) - GraphQL APIs (you can combine multiple GraphQL APIs into one single GraphQL Endpoint, nesting is possible) - Webassembly/WASM Lambdas (e.g. resolve a field using a Rust lambda)

If you're looking for a ready to use solution that has all this functionality packaged as a Gateway have a look at: https://wundergraph.com

Created by Jens Neuse

Directories

Path Synopsis
examples
chat Module
federation Module
kafka_pubsub Module
pkg
ast
Package ast defines the GraphQL AST and offers helper methods to interact with the AST, mostly to get the necessary information from the ast.
Package ast defines the GraphQL AST and offers helper methods to interact with the AST, mostly to get the necessary information from the ast.
astimport
Package astimport can be used to import Nodes manually into an AST.
Package astimport can be used to import Nodes manually into an AST.
astnormalization
Package astnormalization helps to transform parsed GraphQL AST's into a easier to use structure.
Package astnormalization helps to transform parsed GraphQL AST's into a easier to use structure.
astparser
Package astparser is used to turn raw GraphQL documents into an AST.
Package astparser is used to turn raw GraphQL documents into an AST.
astprinter
Package astprinter takes a GraphQL document and prints it as a String with optional indentation.
Package astprinter takes a GraphQL document and prints it as a String with optional indentation.
asttransform
Package asttransform contains a set of helper methods to make recursive ast transformations possible.
Package asttransform contains a set of helper methods to make recursive ast transformations possible.
astvalidation
Package astvalidation implements the validation rules specified in the GraphQL specification.
Package astvalidation implements the validation rules specified in the GraphQL specification.
astvisitor
Package astvisitor enables efficient and powerful traversal of GraphQL document AST's.
Package astvisitor enables efficient and powerful traversal of GraphQL document AST's.
imports
Package imports helps combining multiple GraphQL documents into one document using import comments.
Package imports helps combining multiple GraphQL documents into one document using import comments.
internal/unsafeparser
Package unsafeparser is for testing purposes only when error handling is overhead and panics are ok
Package unsafeparser is for testing purposes only when error handling is overhead and panics are ok
internal/xcontext
Package xcontext is a package to offer the extra functionality we need from contexts that is not available from the standard context package.
Package xcontext is a package to offer the extra functionality we need from contexts that is not available from the standard context package.
introspection
Package introspection takes a GraphQL Schema and provides the introspection JSON to fulfill introspection queries.
Package introspection takes a GraphQL Schema and provides the introspection JSON to fulfill introspection queries.
lexer
Package lexer contains the logic to turn an ast.Input into lexed tokens
Package lexer contains the logic to turn an ast.Input into lexed tokens
lexer/identkeyword
Package identkeyword contains all possible keywords for GraphQL identifiers
Package identkeyword contains all possible keywords for GraphQL identifiers
lexer/keyword
Package keyword contains all possible GraphQL keywords
Package keyword contains all possible GraphQL keywords
lexer/literal
Package literal contains a selection of frequently used literals with GraphQL APIs
Package literal contains a selection of frequently used literals with GraphQL APIs
lexer/position
Package position contains the objects and logic to properly describe the position of a token in a GraphQL document
Package position contains the objects and logic to properly describe the position of a token in a GraphQL document
lexer/runes
Package runes contains all possible 'special' runes in a GraphQL document
Package runes contains all possible 'special' runes in a GraphQL document
lexer/token
Package token contains the object and logic needed to describe a lexed token in a GraphQL document
Package token contains the object and logic needed to describe a lexed token in a GraphQL document
middleware
Package middleware contains useful middleware components for GraphQL services, e.g.
Package middleware contains useful middleware components for GraphQL services, e.g.
middleware/operation_complexity
package operation_complexity implements two common algorithms used by GitHub to calculate GraphQL query complexity
package operation_complexity implements two common algorithms used by GitHub to calculate GraphQL query complexity
operationreport
Package operationreport helps generating the errors object for a GraphQL Operation.
Package operationreport helps generating the errors object for a GraphQL Operation.
playground
Package playground is a http.Handler hosting the GraphQL Playground application.
Package playground is a http.Handler hosting the GraphQL Playground application.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL