Docs
Launch GraphOS Studio

Apollo AST


To generate client code, parses both your and each you write against it into an Abstract Syntax Tree (AST). An AST represents a in a type-safe, machine-readable format.

's parser has its own module (apollo-ast), which you can use independently of apollo-runtime or apollo-api.

Features of apollo-ast include:

  • Parsing schema and operation documents into abstract syntax trees (ASTs)
  • Providing input validation to raise warnings and errors (See the GraphQL spec)
  • Support for outputting ASTs as valid, indented s
  • Support for manipulation of ASTs via the transform API

Installation

Add the apollo-ast dependency to your project:

build.gradle[.kts]
dependencies {
// ...
implementation("com.apollographql.apollo3:apollo-ast:3.8.2")
}

Parsing a document

Use the parseAsGQLDocument method to parse a from a File, a String, or an Okio BufferedSource.

val graphQLText = """
query HeroForEpisode(${"$"}ep: Episode) {
hero(episode: ${"$"}ep) {
name
friends {
height
}
foobar
}
}
""".trimIndent()
val parseResult = Buffer().writeUtf8(graphQLText).parseAsGQLDocument()

This method returns a GQLResult<GQLDocument>, which contains the and/or parsing issues, each of which can have a severity of either WARNING or ERROR. Because there can be warnings, it is possible to have both a valid and issues at the same time.

To get the and throw on errors, use valueAssertNoErrors():

val queryGqlDocument = parseResult.valueAssertNoErrors()

GQLDocument is the root of the AST. It contains a list of GQLDefinitions that together represent the .

All nodes in an AST are subclasses of GQLNode (all named with the GQL prefix). Each subclass exposes specific properties and methods relevant to the corresponding node type.

Example AST structure

In the HeroForEpisode example above, here's the structure of the AST returned by the parser:

GQLDocument
└─GQLOperationDefinition query "HeroForEpisode"
├─GQLVariableDefinition "ep": "Episode"
└─GQLSelectionSet
└─GQLField "hero"
├─GQLSelectionSet
│ ├─GQLField "name"
│ ├─GQLField "friends"
│ │ └─GQLSelectionSet
│ │ └─GQLField "height"
│ └─GQLField "foobar"
└─GQLArguments
└─GQLArgument "episode"
└─GQLVariableValue "ep"

Note that this structure and its node names closely follow the GraphQL specification.

Validating input

In addition to parsing, the apollo-ast library provides methods to perform higher-level validation of .

To validate a parsed GQLDocument:

  • If the document represents a schema, call the validateAsSchema method.
  • If the document represents one or more operations, call the validateAsExecutable method.

validateAsSchema

validateAsSchema returns a GQLResult<Schema>. The following snippet parses and validates a short invalid schema that uses an undefined (@private):

val schemaText = """
type Query {
hero(episode: Episode): Character
}
enum Episode {
NEWHOPE
EMPIRE
}
type Character @private {
name: String
height: Int @deprecated
friends: [Character]
}
""".trimIndent()
val schemaGQLDocument = Buffer().writeUtf8(schemaText).parseAsGQLDocument().valueAssertNoErrors()
val schemaResult = schemaGQLDocument.validateAsSchema()
println(schemaResult.issues.map { it.severity.name + ": " + it.message })

When executed, this snippet prints [WARNING: Unknown directive 'private'].

Because this is a warning and not an error, you can still use the returned schemaResult.valueAssertNoErrors()

validateAsExecutable

The validateAsExecutable method checks whether a 's defined are valid against a particular provided Schema. You can obtain this Schema parameter by calling the above validateAsSchema on the GQLDocument that represents the schema:

val schema = schemaGQLDocument.validateAsSchema().valueAssertNoErrors()
val executableIssues = queryGqlDocument.validateAsExecutable(schema)
println(executableIssues.map { it.severity.name + ": " + it.message })

If the queryGqlDocument queries a deprecated and misspells another, this snippet might print the following:

[WARNING: Use of deprecated field 'height', ERROR: Can't query 'frends' on type 'Character']

Outputting SDL

You can output a GQLDocument to standard syntax with the toUtf8 :

// Returns a string
println(queryGqlDocument.toUtf8())
// Output to a File
queryGqlDocument.toUtf8(file)
// Output to an Okio BufferedSink
queryGqlDocument.toUtf8(sink)

Transforming an AST

You can use the transform method of GQLDocument to modify an existing AST.

You pass transform a lambda that accepts a GQLNode and also returns instructions to manipulate the AST:

  • Continue: keep the node as-is and continue visiting the children
  • Delete: delete the node
  • Replace(GQLNode): replace the node with the given one

The transform method traverses the AST and executes the lambda for each node, then acts on the AST according to the lambda's return value.

Note that with Delete and Replace, the node's children are not visited automatically, so you should call transform recursively if that is needed.

For example, this snippet removes all named restrictedField from defined in queryGqlDocument and prints the result:

val transformedQuery = queryGqlDocument.transform{ node ->
if (node is GQLField && node.name == "restrictedField") {
TransformResult.Delete
} else {
TransformResult.Continue
}
}
println(transformedQuery!!.toUtf8())
Previous
Kotlin native
Next
Response based codegen
Edit on GitHubEditForumsDiscord

© 2024 Apollo Graph Inc.

Privacy Policy

Company