What’s Changed
⚠️ Client Codegen migration to graphql-dgs-codegen-client-core (#439) @berngp
As part of an effort to migrate the DGS Client Components that should belong to the DGS Codegen project, we are removing from the graphql-dgs-client
module all components in the com.netflix.graphql.dgs.client.codegen
package. If you depend on them you will have to use the DGS Codegen plugin, such plugin will add a new module automatically with the artifacts. This change will allow us to evolve the both projects independently. In other words, we will not need to release a new version of the DGS Framework if we need changes to the DGS Codegen components.
Implemented @connection
directive for generating SDL types for cursor-based pagination. (#487) @srinivasankavitha
As a new feature we are releasing the new graphql-dgs-pagination
module. It adds support for generating schema types for cursor-based pagination based on the relay spec.
The benefit is to avoid having the user define related Connection and Edge types in the schema for every type that needs to be paginated. The following example below shows the schema the user needs to define:
type Query {
something: MovieConnection
}
type Movie @connection {
movieID: ID
title: String
}
Note the @connection
directive against the type Movie
. This will generate a MovieConnection
and MovieEdge
as follows:
type MovieConnection {
edges: [MovieEdge]
pageInfo: PageInfo
}
type MovieEdge {
node: Movie
cursor: String
}
type PageInfo {
hasPreviousPage: Boolean!
hasNextPage: Boolean!
startCursor: String
endCursor: String
}
PageInfo
is generated only once, if necessary.
Add basic support for coroutines in datafetchers (#472) @paulbakker
You can now use Kotlin Coroutines to define datafetchers. For example, let's say we have a field called range
that will compute the sum between two numbers. You could define your datafetcher as follows:
@DgsQuery
suspend fun range(@InputArgument from: Int, to: Int): Int = coroutineScope {
var sum = 0
withContext(executor.asCoroutineDispatcher()) {
repeat(from.rangeTo(to).count()) {
sum++
// Forcing a blocking call to demonstrate running with a thread pool
Thread.sleep(50)
}
sum
}
}
Using a coroutine is very similar to returning CompletableFuture
. In fact, coroutines are internally resolved as CompletableFuture
.
A coroutine (similar to returning CompletableFuture
) marks a datafetcher as potentially async.
However, coroutine datafetchers are dispatched using the unconfined dispatcher, which in practice means they won't automatically get any parallel behavior. This means, that multiple datafetchers defined as coroutines will still not run in parallel.
To get parallel execution, the developer is in charge of setting up the correct context. A great way to do this is using an Executor
.
Also remember that any thread-bound context, such as request data stored on thread-local, will not be available when executing on a different thread. An executor has to be set up explicitly for this.
Again, this is exactly the same when using CompletableFuture
for parallel datafetcheres.
Complete list of changes
- Identify implicit GQL Operation Name for the gql.operation.name tag. (#491) @berngp
- Graphiql to autoupdate url parameters (#476) @subbarao
- Added subscription test example (#488) @paulbakker
- Bump mockk from 1.11.0 to 1.12.0 (#464) @dependabot
- Reuse ObjectMapper in DgsWebSocketHandler (#468) @stdevi
- Errors should be included in a subscription response (#485) @paulbakker