Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions docs/customizing-schemas/documenting-fields.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
id: documenting-fields
title: Documenting Schema
---

Since Javadocs are not available at runtime for introspection, `graphql-kotlin-schema-generator` includes an annotation
class `@GraphQLDescription` that can be used to add schema descriptions to *any* GraphQL schema element:

```kotlin
@GraphQLDescription("A useful widget")
data class Widget(
@GraphQLDescription("The widget's value that can be null")
val value: Int?
)

class WidgetQuery: Query {

@GraphQLDescription("creates new widget for given ID")
fun widgetById(@GraphQLDescription("The special ingredient") id: Int): Widget? = Widget(id)
}
```

The above query would produce the following GraphQL schema:

```graphql
schema {
query: Query
}

type Query {
"""creates new widget for given ID"""
widgetById(
"""The special ingredient"""
id: Int!
): Widget
}

"""A useful widget"""
type Widget {
"""The widget's value that can be null"""
value: Int
}
```
42 changes: 42 additions & 0 deletions docs/customizing-schemas/evolving-schema.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
id: evolving-schema
title: Evolving Schema
---

### Deprecating Fields

GraphQL schemas can have fields marked as deprecated. Instead of creating a custom annotation,
`graphql-kotlin-schema-generator` just looks for the `kotlin.Deprecated` annotation and will use that annotation message
for the deprecated reason.

```kotlin
class SimpleQuery {
@Deprecated(message = "this query is deprecated", replaceWith = ReplaceWith("shinyNewQuery"))
@GraphQLDescription("old query that should not be used always returns false")
fun simpleDeprecatedQuery(): Boolean = false

@GraphQLDescription("new query that always returns true")
fun shinyNewQuery(): Boolean = true
}
```

The above query would produce the following GraphQL schema:

```graphql
schema {
query: Query
}

type Query {

"""old query that should not be used always returns false"""
simpleDeprecatedQuery: Boolean! @deprecated(reason: "this query is deprecated, replace with shinyNewQuery")

"""new query that always returns true"""
shinyNewQuery: Boolean!
}
```

While you can deprecate any fields/functions/classes in your Kotlin code, GraphQL only supports deprecation directive on
the fields (which correspond to Kotlin fields and functions) and enum values.

34 changes: 34 additions & 0 deletions docs/customizing-schemas/excluding-fields.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
id: excluding-fields
title: Excluding Fields
---

There are two ways to ensure the GraphQL schema generation omits fields when using Kotlin reflection:

* The first is by marking the field as non-`public` scope (`private`, `protected`, `internal`)
* The second method is by annotating the field with `@GraphQLIgnore`.

```kotlin
class SimpleQuery {
@GraphQLIgnore
fun notPartOfSchema() = "ignore me!"

private fun privateFunctionsAreNotVisible() = "ignored private function"

fun doSomething(value: Int): Boolean = true
}
```

The above query would produce the following GraphQL schema:

```graphql
schema {
query: Query
}

type Query {
doSomething(value: Int!): Boolean!
}
```

Note that the public method `notPartOfSchema` is not included in the schema.
Original file line number Diff line number Diff line change
@@ -1,67 +1,67 @@
---
id: generator-config
title: Schema Generator Configuration
---

`graphql-kotlin-schema-generator` provides a single function, `toSchema,` to generate a schema from Kotlin objects. This
function accepts four arguments: config, queries, mutations and subscriptions. The queries, mutations and subscriptions
are a list of
[TopLevelObjects](https://github.com/ExpediaGroup/graphql-kotlin/blob/master/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/TopLevelObject.kt)
and will be used to generate corresponding GraphQL root types. The
[config](https://github.com/ExpediaGroup/graphql-kotlin/blob/master/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/SchemaGeneratorConfig.kt)
contains all the extra information you need to pass, including custom hooks, supported packages and name overrides.
`SchemaGeneratorConfig` has some default settings but you can override them and add custom behaviors for generating your
schema.

* `supportedPackages` **[Required]** - List of Kotlin packages that can contain schema objects. Limits the scope of
packages that can be scanned using reflections.
* `topLevelNames` _[Optional]_ - Set the name of the top level GraphQL fields, defaults to `Query`, `Mutation` and
`Subscription`
* `hooks` _[Optional]_ - Set custom behaviors for generating the schema, see below for details.
* `dataFetcherFactory` _[Optional]_ - Sets custom behavior for generating data fetchers

## Schema generator hooks

Hooks are lifecycle events that are called and triggered while the schema is building that allow users to customize the
schema.

For exact names and details of every hook, see the comments and descriptions in our latest
[javadocs](https://www.javadoc.io/doc/com.expediagroup/graphql-kotlin-schema-generator) or directly in the source file:
[SchemaGeneratorHooks.kt](https://github.com/ExpediaGroup/graphql-kotlin/blob/master/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/hooks/SchemaGeneratorHooks.kt)

As an example here is how you would write a custom hook and provide it through the configuration

```kotlin
class MyCustomHooks : SchemaGeneratorHooks {
// Only generate functions that start with "dog"
// This would probably be better just to use @GraphQLIgnore, but this is just an example
override fun isValidFunction(function: KFunction<*>) = function.name.startsWith("dog")
}

class Query {
fun dogSound() = "bark"

fun catSound() = "meow"
}

val config = SchemaGeneratorConfig(supportedPackages = listOf("org.example"), hooks = MyCustomHooks())

val queries = listOf(TopLevelObject(Query()))

toSchema(queries = queries, config = config)
```

will generate

```graphql

schema {
query: Query
}

type Query {
dogSound: String!
}
```

Notice there is no `catSound` function.
---
id: generator-config
title: Generator Configuration
---

`graphql-kotlin-schema-generator` provides a single function, `toSchema,` to generate a schema from Kotlin objects. This
function accepts four arguments: config, queries, mutations and subscriptions. The queries, mutations and subscriptions
are a list of
[TopLevelObjects](https://github.com/ExpediaGroup/graphql-kotlin/blob/master/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/TopLevelObject.kt)
and will be used to generate corresponding GraphQL root types. The
[config](https://github.com/ExpediaGroup/graphql-kotlin/blob/master/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/SchemaGeneratorConfig.kt)
contains all the extra information you need to pass, including custom hooks, supported packages and name overrides.
`SchemaGeneratorConfig` has some default settings but you can override them and add custom behaviors for generating your
schema.

* `supportedPackages` **[Required]** - List of Kotlin packages that can contain schema objects. Limits the scope of
packages that can be scanned using reflections.
* `topLevelNames` _[Optional]_ - Set the name of the top level GraphQL fields, defaults to `Query`, `Mutation` and
`Subscription`
* `hooks` _[Optional]_ - Set custom behaviors for generating the schema, see below for details.
* `dataFetcherFactory` _[Optional]_ - Sets custom behavior for generating data fetchers

## Schema generator hooks

Hooks are lifecycle events that are called and triggered while the schema is building that allow users to customize the
schema.

For exact names and details of every hook, see the comments and descriptions in our latest
[javadocs](https://www.javadoc.io/doc/com.expediagroup/graphql-kotlin-schema-generator) or directly in the source file:
[SchemaGeneratorHooks.kt](https://github.com/ExpediaGroup/graphql-kotlin/blob/master/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/hooks/SchemaGeneratorHooks.kt)

As an example here is how you would write a custom hook and provide it through the configuration

```kotlin
class MyCustomHooks : SchemaGeneratorHooks {
// Only generate functions that start with "dog"
// This would probably be better just to use @GraphQLIgnore, but this is just an example
override fun isValidFunction(function: KFunction<*>) = function.name.startsWith("dog")
}

class Query {
fun dogSound() = "bark"

fun catSound() = "meow"
}

val config = SchemaGeneratorConfig(supportedPackages = listOf("org.example"), hooks = MyCustomHooks())

val queries = listOf(TopLevelObject(Query()))

toSchema(queries = queries, config = config)
```

will generate

```graphql

schema {
query: Query
}

type Query {
dogSound: String!
}
```

Notice there is no `catSound` function.
19 changes: 19 additions & 0 deletions docs/customizing-schemas/renaming-fields.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
id: renaming-fields
title: Renaming Fields
---

By default, schema generator will use simple name of the underlying class for the type names and function/property names for field names.
You can change this default behavior by annotating target class/field with `@GraphQLName` annotation. The following Kotlin `Widget` class
will be renamed to `MyCustomName` GraphQL type.

```kotlin
@GraphQLName("MyCustomName")
data class Widget(val value: Int?)
```

```graphql
type MyCustomName {
value: Int
}
```
48 changes: 48 additions & 0 deletions docs/execution/contextual-data.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
id: contextual-data
title: Contextual Data
---

All GraphQL servers have a concept of a "context". A GraphQL context contains metadata that is useful to the GraphQL
server, but shouldn't necessarily be part of the GraphQL query's API. A prime example of something that is appropriate
for the GraphQL context would be trace headers for an OpenTracing system such as
[Haystack](https://expediadotcom.github.io/haystack). The GraphQL query itself does not need the information to perform
its function, but the server itself needs the information to ensure observability.

The contents of the GraphQL context vary across applications and it is up to the GraphQL server developers to decide
what it should contain. For Spring based applications, `graphql-kotlin-spring-server` provides a simple mechanism to
build context per query execution through
[GraphQLContextFactory](https://github.com/ExpediaGroup/graphql-kotlin/blob/master/graphql-kotlin-spring-server/src/main/kotlin/com/expediagroup/graphql/spring/execution/GraphQLContextFactory.kt).
Once context factory bean is available in the Spring application context it will then be used in a corresponding
[ContextWebFilter](https://github.com/ExpediaGroup/graphql-kotlin/blob/master/graphql-kotlin-spring-server/src/main/kotlin/com/expediagroup/graphql/spring/execution/ContextWebFilter.kt)
to populate GraphQL context based on the incoming request and make it available during query execution.

Once your application is configured to build your custom `MyGraphQLContext`, simply add `@GraphQLContext` annotation to
any function argument and the corresponding GraphQL context from the environment will be automatically injected during
execution.

```kotlin
class ContextualQuery {

fun contextualQuery(
value: Int,
@GraphQLContext context: MyGraphQLContext
): ContextualResponse = ContextualResponse(value, context.myCustomValue)
}
```

The above query would produce the following GraphQL schema:

```graphql
schema {
query: Query
}

type Query {
contextualQuery(
value: Int!
): ContextualResponse!
}
```

Note that the `@GraphQLContext` annotated argument is not reflected in the GraphQL schema.
Loading