Skip to content

Commit 9550519

Browse files
smyrickShane Myrickdariuszkuc
authored
Common graphql-kotlin-server (#988)
* Create common graphql-kotlin-server module * Migrate ktor to use common server code * Update spark code * Rename dir to servers * Consistent gradle file and non-null registry factory * Create GraphQLRequestParse and GraphQLServer * Use common server code in spring * Refactor subscription context factories * Update example subscription test * Start creating the new docs for common server * Update example subscription tests * Add new server docs * Add default arg to example * Update docs/getting-started.md Co-authored-by: Dariusz Kuc <[email protected]> * Update method to match library patterns Co-authored-by: Shane Myrick <[email protected]> Co-authored-by: Dariusz Kuc <[email protected]>
1 parent 422a1a5 commit 9550519

File tree

107 files changed

+1711
-1079
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

107 files changed

+1711
-1079
lines changed

docs/examples.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,3 @@ directly from your IDE. Alternatively you can also use the Spring Boot plugin fr
4949

5050
Once the app has started you can explore the example schema by opening Playground endpoint at
5151
[http://localhost:8080/playground](http://localhost:8080/playground).
52-

docs/getting-started.md

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -36,21 +36,20 @@ Using a JVM dependency manager, simply link any `graphql-kotlin-*` library to yo
3636
version and other examples in [Sonatype Central
3737
Repository](https://search.maven.org/artifact/com.expediagroup/graphql-kotlin-spring-server)
3838

39-
### Maven
40-
39+
<!--DOCUSAURUS_CODE_TABS-->
40+
<!--Gradle Kotlin-->
41+
```kotlin
42+
implementation("com.expediagroup", "graphql-kotlin-spring-server", latestVersion)
43+
```
44+
<!--Maven-->
4145
```xml
4246
<dependency>
43-
<groupId>com.expediagroup</groupId>
44-
<artifactId>graphql-kotlin-spring-server</artifactId>
45-
<version>${latestVersion}</version>
47+
<groupId>com.expediagroup</groupId>
48+
<artifactId>graphql-kotlin-spring-server</artifactId>
49+
<version>${latestVersion}</version>
4650
</dependency>
4751
```
48-
49-
### Gradle
50-
51-
```kotlin
52-
implementation("com.expediagroup", "graphql-kotlin-spring-server", latestVersion)
53-
```
52+
<!--END_DOCUSAURUS_CODE_TABS-->
5453

5554
## Generating a Schema
5655

@@ -65,9 +64,9 @@ Using `graphql-kotlin-federation`, you can generate an [Apollo Federation](https
6564
See the docs in [Apollo Federation](./federated/apollo-federation.md).
6665

6766
## Running a Server
68-
`graphql-kotlin-spring-server` is a combination of the schema generator, fedeation, and server libraries. If you are looking to run a GraphQL server, this is the place to start.
67+
`graphql-kotlin-server` is a combination of the schema generator, federation, and server libraries. If you are looking to run a GraphQL server, this is the place to start.
6968

70-
See the docs in [Spring Server Overview](./spring-server/spring-overview.md).
69+
See the docs in [GraphQL Kotlin Server](./server/graphql-server.md).
7170

7271
## Creating a Client
7372
`graphql-kotlin-plugins` can be used to generate a `graphql-kotlin-client` from an existing schema that is easy to use and type-safe.

docs/schema-generator/execution/contextual-data.md

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,10 @@ for the GraphQL context would be trace headers for an OpenTracing system such as
1010
its function, but the server needs the information to ensure observability.
1111

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

2118
## GraphQLContext Interface
2219

@@ -52,8 +49,7 @@ type Query {
5249

5350
Note that the argument that implements `GraphQLContext` is not reflected in the GraphQL schema.
5451

55-
## Spring Server
56-
For more details on how to create the context while using `graphql-kotlin-spring-server` see the [spring graphql context page](../../spring-server/spring-graphql-context.md).
57-
58-
### Customization
59-
The context is injected into the execution through the `FunctionDataFetcher` class. If you want to customize the logic on how the context is determined, that is possible to override. See more details on the [Fetching Data documentation](./fetching-data)
52+
## Injection Customization
53+
The context is injected into the execution through the `FunctionDataFetcher` class.
54+
If you want to customize the logic on how the context is determined, that is possible to override.
55+
See more details on the [Fetching Data documentation](./fetching-data.md)

docs/schema-generator/writing-schemas/nested-arguments.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ are a couple ways we can access it:
2626
## DataFetchingEnvironment
2727

2828
You can add the `DataFetchingEnvironment` as an argument. This class will be ignored by the schema generator and will allow
29-
you to view the entire query sent to the server. See more in the [DataFetchingEnvironment documentation](../execution/data-fetching-environment)
29+
you to view the entire query sent to the server. See more in the [DataFetchingEnvironment documentation](../execution/data-fetching-environment.md)
3030

3131
```kotlin
3232
class User {
@@ -40,7 +40,7 @@ class User {
4040
## GraphQL Context
4141

4242
You can add the `GraphQLContext` as an argument. This class will be ignored by the schema generator and will allow you to
43-
view the context object you set up in the data fetchers. See more in the [GraphQLContext documentation](../execution/contextual-data)
43+
view the context object you set up in the data fetchers. See more in the [GraphQLContext documentation](../execution/contextual-data.md)
4444

4545
```kotlin
4646
class User {
@@ -52,7 +52,7 @@ class User {
5252
```
5353

5454
## Excluding Arguments from the Schema
55-
You can construct the child objects by passing down arguments as non-public fields or annotate the argument with [@GraphQLIgnore](../customizing-schemas/excluding-fields)
55+
You can construct the child objects by passing down arguments as non-public fields or annotate the argument with [@GraphQLIgnore](../customizing-schemas/excluding-fields.md)
5656

5757
```kotlin
5858
class User(private val userId: String) {
@@ -69,4 +69,4 @@ class User(private val userId: String) {
6969

7070
## Spring Integration
7171

72-
See [Writing Schemas with Spring](../../spring-server/spring-schema.md) for integration details.
72+
See [Writing Schemas with Spring](../../server/spring-server/spring-schema.md) for integration details.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
---
2+
id: data-loader-registry-factory
3+
title: DataLoaderRegistryFactory
4+
---
5+
6+
[Data loaders](https://github.com/graphql/dataloader) are a popular caching pattern from the JavaScript GraphQL implementation.
7+
`graphql-java` provides [support for this pattern](https://www.graphql-java.com/documentation/v16/batching/) using the `DataLoader` and `DataLoaderRegistry`.
8+
9+
Since `graphql-kotlin` allows you to abstract the schema generation and data fetching code, you may not even need data loaders if instead you have some persistant cache on your server.
10+
11+
```kotlin
12+
class User(val id: ID) {
13+
14+
// The friendService and userService, which have nothing to do with GraphQL,
15+
// should be concerned with caching and batch calls instead of your schema classes
16+
fun getFriends(): List<User> {
17+
val friends: List<ID> = friendService.getFriends(id)
18+
return userService.getUsers(friends)
19+
}
20+
21+
}
22+
```
23+
24+
If you still want to use data loaders though, they are supported through the common interfaces.
25+
26+
## `DataLoaderRegistryFactory`
27+
28+
Data loaders should be created per-request as the caching pattern may not work for serving multiple users requesting data simultaneously.
29+
The [GraphQLRequestHandler](./graphql-request-handler.md) accepts an optional `DataLoaderRegistryFactory` interface that will be called on every request to get a `DataLoaderRegistry`.
30+
31+
```kotlin
32+
interface DataLoaderRegistryFactory {
33+
fun generate(): DataLoaderRegistry
34+
}
35+
```
36+
37+
The `DataLoaderRegistry` is a map of a unique keys to a `DataLoader` object that handles the cache for an output type in your graph.
38+
A `DataLoader` caches the types by some unique value, usually by the type id.
39+
40+
```kotlin
41+
class MyCustomDataLoaderRegistryFactory : DataLoaderRegistryFactory {
42+
43+
private val userLoader = DataLoader<ID, User> { id ->
44+
CompletableFuture.supplyAsync { userService.getUser(id) }
45+
}
46+
47+
override fun generate(): DataLoaderRegistry {
48+
val registry = DataLoaderRegistry()
49+
registry.register("userLoader", userLoader)
50+
return registry
51+
}
52+
}
53+
```
54+
55+
> NOTE: Because the execution of data loaders is handled by `graphql-java`, which runs using `CompletionStage`, currently we do not support `suspend` functions.
56+
> See issue [#986](https://github.com/ExpediaGroup/graphql-kotlin/issues/986).
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
id: graphql-context-factory
3+
title: GraphQLContextFactory
4+
---
5+
6+
Similar to the [GraphQLRequestParser](./graphql-request-parser.md), `GraphQLContextFactory` has a generic method for handling the `Request` and the `GraphQLContext`.
7+
8+
```kotlin
9+
interface GraphQLContextFactory<out Context : GraphQLContext, Request> {
10+
fun generateContext(request: Request): Context?
11+
}
12+
```
13+
14+
Given the server request, the interface should create the custom `GraphQLContext` class to be used for every new operation.
15+
The context must implement the `GraphQLContext` interface from `graphql-kotlin-schema-generator`.
16+
See [execution context](../schema-generator/execution/contextual-data.md) for more info on how the context can be used in the schema functions.
17+
18+
A specific `graphql-kotlin-*-server` library may provide an abstract class on top of this interface so users only have to be concerned with the context.
19+
20+
For example the `graphql-kotlin-spring-server` provides the following class, which sets the request type:
21+
22+
```kotlin
23+
abstract class SpringGraphQLContextFactory<out T : GraphQLContext> : GraphQLContextFactory<T, ServerRequest>
24+
```
25+
26+
## HTTP Headers
27+
28+
For common use cases around authorization, authentication, or tracing you may need to read HTTP headers.
29+
This should be done in the `GraphQLContextFactory` and relevant data should be added to the context to be accessible during schema exectuion.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
id: graphql-request-handler
3+
title: GraphQLRequestHandler
4+
---
5+
6+
The `GraphQLRequestHandler` is an open and extendable class that contains the basic logic to get a `GraphQLResponse` from `graphql-kotlin-types`.
7+
It accepts a `GraphQLRequest`, an optional [GraphQLContext](./graphql-context-factory.md) and sends that to the GraphQL schema along with the [DataLoaderRegistryFactory](./data-loader-registry-factory.md).
8+
9+
There shouldn't be much need to change this class but if you wanted to add custom logic or logging it is possible to override it or just create your own.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
id: graphql-request-parser
3+
title: GraphQLRequestParser
4+
---
5+
6+
The `GraphQLRequestParser` interface is requrired to parse the library-specific HTTP request object into the common `GraphQLRequest` class from `graphql-kotlin-types`.
7+
8+
```kotlin
9+
interface GraphQLRequestParser<Request> {
10+
suspend fun parseRequest(request: Request): GraphQLRequest?
11+
}
12+
```
13+
14+
While not offically part of the spec, there is a standard format used by most GraphQL clients and servers for [serving GraphQL over HTTP](https://graphql.org/learn/serving-over-http/).
15+
16+
If the request is not a valid GraphQL format, the interface should return `null` and let the server specific code return a bad request status to the client.
17+
This is not the same as a GraphQL error or an exception thrown by the schema.
18+
Those types of errors should still parse the request and return a valid response with errors set via the [GraphQLRequestHandler](./graphql-request-handler.md).
19+
20+
This interface should only be concerned with parsing the request, not about forwarding info to the context or execution.
21+
That is handled by the [GraphQLContextFactory](./graphql-context-factory.md).

docs/server/graphql-server.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
id: graphql-server
3+
title: GraphQLServer
4+
---
5+
6+
`graphql-kotlin-server` provides a common code and basic interfaces to setup a GraphQL server in a specific server library.
7+
8+
The official reference implementations are:
9+
10+
* [graphql-kotlin-spring-server](./spring-server/spring-overview.md)
11+
12+
We reccomend using one of the implementations if you can as the common code has very little logic.
13+
It is more of a scaffolding on how to write your server code.
14+
There are demos of how to use these server libraries in the `/examples` folder of the repo.
15+
16+
## `GraphQLServer`
17+
18+
The top level object in the common package is `GraphQLServer<T>`.
19+
This class is open for extensions and requires that you specify the type of the http requests you will be handling.
20+
21+
* For [Spring Reactive](https://spring.io/reactive) we would define a `GraphQLServer<ServerRequest>`
22+
* For [Ktor](https://ktor.io/) we would define a `GraphQLServer<ApplicationRequest>`
23+
24+
In its simplest form, a GraphQL server has the following responsibilties:
25+
26+
* Parse the request info from the HTTP request
27+
* Create a `GraphQLContext` object from the HTTP request to be used during execution
28+
* Send the request and the context to the GraphQL schema to execute and get a response (may contain `data` or `errors`)
29+
* Send the reponse back to the client over HTTP
30+
31+
Most of the logic in a GraphQL server that is specific to your application is already in the schema, so if we have interfaces for all these
32+
common functions, we can abstract away the library specific features.
33+
34+
The one method we don't have an interface for is sending back the response to the client. Once you get the response back from `GraphQLServer`,
35+
we leave the rest up to your application to call it's server specific methods to encode and send the response.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
id: server-subscriptions
3+
title: Subscriptions
4+
---
5+
If you are using one of the official server implementations for GraphQL Kotlin, it will have subscription handling setup for you.
6+
7+
* See `graphql-kotlin-spring-server` [subscriptions](spring-server/spring-subscriptions.md)
8+
9+
Subscriptions require a more indepth knoweldge of how the specific server library handles protocols and streaming.
10+
Since we can only support `Publisher` from `graphql-java` in this common library, we can not provide any common logic for subscriptions.
11+
Therefore you will still need to implement the route and request handling for subscriptions separately if you are not using a provided server implementation.

0 commit comments

Comments
 (0)