Skip to main content

Resolver

A resolver is a function which is in charge of returning a field of the Graph.

Basic resolver

Let's take this GraphQL schema as example

type Query {
movies: [Movie!]!
}

type Mutation {
addMovie(title: String!, directorName: String!): Movie!
}

type Movie {
title: String!
director: Director!
}

type Director {
name: String!
movies: [Movie!]
}

It will allow users to fetch books and/or add a book. You'll have to register a first resolver function which will resolve the field Query.movies.

To do so, register your resolver like this:

data class Director(val name: String)
data class Movie(val title: String, val director: Director)

val movies = mutableListOf<Movie>()

fun main() = arianeServer {

resolvers {
Query {
movies {
movies // This will return all the movies
}
}
}
}.launch()

Arguments

Now, let's look at the field Mutation.addMovie:

type Mutation {
addMovie(title: String!, directorName: String!): Movie!
}

It's receiving parameters (title and director).

To handle them, you can use one field of the map passed to the resolver's lambda.

val movies = mutableListOf<Movie>()

fun main() = arianeServer {

resolvers {
Mutation {
resolve("addMovie") {
val title = it["title"]
val directorName = it["director"]

val movie = Movie(title, Director(directorName))
movies.add(movie)

movie
}
}
}
}.launch()

Arguments typing

You can also map the argument to a defined type like this:

data class AddMovieArgument(val title: String, val director: String)

val movies = mutableListOf<Movie>()

fun main() = arianeServer {

resolvers {
Mutation {
resolve<AddMovieArgument>("addMovie") { argument ->
val title = argument.title
val directorName = argument.director

val movie = Movie(title, Director(directorName))
movies.add(movie)

movie
}
}
}
}.launch()

The codegen plugin generates everything for you:

val movies = mutableListOf<Movie>()

fun main() = arianeServer {

resolvers {
Mutation {
addMovie { (title, director) ->

val movie = Movie(title, Director(director))
movies.add(movie)

movie
}
}
}
}.launch()

Nesting fields

If you look at the type Director:

type Director {
name: String!
movies: [Movie!]
}

It contains the field movies, but we never returned it. So, what happen if a client execute this query?

query GetMovies {
movies {
title
director {
name
movies {
title
}
}
}
}

Ariane won't be able to resolve the field Director.movies.

To do so, you can add a resolver function like this:

val movies = mutableListOf<Movie>()

fun main() = arianeServer {

resolvers {
Director {
movies {
movies.filter { it.director.name == source.name }
}
}
}
}.launch()

Object oriented Resolver

If you prefer to use resolver as a Class, you can create one and register it like this:

data class AddMovieArgument(val title: String, val director: String)

class AddMovieResolver : Resolver<GraphQLTypes.Mutation, GetMovieArguments> {

override suspend fun resolve(arguments: AddMovieArgument, source: GraphQLTypes.Query, context: GraphQLContext, info: Info): Any? {
// Return the movies here
}
}
info

The generic type refers to the source parameter type, and its argument type.See (resolver parameters)

Then register it like this:

val addMovieResolver = AddMovieResolver()

fun main() = arianeServer {

resolvers {
Mutation {
resolve("addMovie", addMovieResolver)
}
}
}.launch()

With the codegen plugin, it's even simpler, it'll generate the interface so you can simply implements it.

The name of the generated interface will be ${SourceName}${FieldName}Resolver. Example:

//generated
interface MutationAddMovieResolver: Resolver<GraphQLTypes.Mutation, AddMovieArgument>

So you just have to implement the generated interface

class GetMoviesResolver : MutationAddMovieResolver {

override suspend fun resolve(arguments: AddMovieArgument, source: GraphQLTypes.Query, context: GraphQLContext, info: Info): Any? {
// Return the movies here
}
}

And register it like this:

val addMovieResolver = AddMovieResolver()

fun main() = arianeServer {

resolvers {
Mutation {
addMovie(addMovieResolver)
}
}
}.launch()

Organize resolvers

As your project grows, you may want to split resolvers in different files.

It's possible to merge them into a single ariane configuration like this:

fun main() = arianeServer {

resolvers {
addResolvers(movieResolvers)
addResolvers(directorResolvers)
}
}.launch()