Skip to content

Directive @selection

dermakov edited this page Aug 21, 2021 · 3 revisions

The @selection directive provides an ability to move optional arguments of GraphQL field to a lambda block in generated projection interface. Let define a GraphQL schema:

type Query {
    films(
        genre: Genre!,
        title: String,
        offset: Int! = 0,
        limit: Int! = 30
    ): [Film!]!
}

type Film {
    id: ID!
    genre: Genre!
    title: String!
}

enum Genre {
    DRAMA
    COMEDY
    THRILLER
    HORROR
}

This schema allows us to write a query using the generated DSL that looks like this:

val context: ExampleContext = exampleContextOf(createMyAdapter())
val response = context.query {
    films(Genre.DRAMA, title = "*Dream*", offset = 100, limit = 100) {
        id()
        genre()
        title()
    }
}

response.films.forEach { film ->
    println("Film [${film.id}]: ${film.title} (${film.genre})")
}

The films projection function has 3 optional arguments:

  • The title is optional because it is nullable.
  • The offset is optional because it has a default value.
  • The limit is optional because it has a default value.

We can move these arguments to the lambda block of the films projection function using the @selection directive. Let's modify our schema:

directive @selection on FIELD_DEFINITION

type Query {
    films(
        genre: Genre!,
        title: String,
        offset: Int! = 0,
        limit: Int! = 30
    ): [Film!]! @selection
}

type Film {
    id: ID!
    genre: Genre!
    title: String!
}

enum Genre {
    DRAMA
    COMEDY
    THRILLER
    HORROR
}

Now we can write our query like this:

val context: ExampleContext = exampleContextOf(createMyAdapter())
val response = context.query {
    films(Genre.DRAMA) {
        title = "*Dream*"
        offset = 100
        limit = 100

        id()
        genre()
        title()
    }
}

response.films.forEach { film ->
    println("Film [${film.id}]: ${film.title} (${film.genre})")
}

The @selection directive improves readability of DSL queries in case the GraphQL field contains many optional arguments.

How it works?

Without @selection directive for the GraphQL type Query, Kobby generates a projection interface that looks like this:

@ExampleDSL
interface QueryProjection {
    /**
     * @param offset Default: 0
     * @param limit Default: 30
     */
    fun films(
        genre: Genre,
        title: String? = null,
        offset: Int? = null,
        limit: Int? = null,
        __projection: FilmProjection.() -> Unit
    ): Unit
}

After applying @selection directive, the generated interface will change as follows:

@ExampleDSL
interface QueryProjection {
    fun films(genre: Genre, __query: QueryFilmsQuery.() -> Unit): Unit
}

@ExampleDSL
interface QueryFilmsSelection {
    var title: String?

    /**
     * Default: 0
     */
    var offset: Int?

    /**
     * Default: 30
     */
    var limit: Int?
}

@ExampleDSL
interface QueryFilmsQuery : QueryFilmsSelection, FilmProjection

Clone this wiki locally