A simple GraphQL client on top ofSangria,Akka HTTPandCirce.
Add the following dependency:
resolvers+=Resolver.bintrayRepo("jarlakxen","maven")
"com.github.jarlakxen"%%"drunk"%"2.5.0"
Then, import:
importcom.github.jarlakxen.drunk._
importio.circe._,io.circe.generic.semiauto._
importsangria.macros._
There are three ways to create aGraphQLClient
:
- As Akka Https flow connection
import akka.http.scaladsl.model.Uri
val uri: Uri = Uri(s "https://$host:$port/api/graphql" )
val http: HttpExt = Http()
val flow: Flow[HttpRequest, HttpResponse, Future[OutgoingConnection]] = http.outgoingConnectionHttps(uri.authority.host.address(), uri.effectivePort)
val client = GraphQLClient(uri, flow, clientOptions = ClientOptions.Default, headers = Nil)
- As Akka Http flow connection
import akka.http.scaladsl.model.Uri
val uri: Uri = Uri(s "http://$host:$port/api/graphql" )
val http: HttpExt = Http()
val flow: Flow[HttpRequest, HttpResponse, Future[OutgoingConnection]] = http.outgoingConnection(uri.authority.host.address(), uri.effectivePort)
val client = GraphQLClient(uri, flow, clientOptions = ClientOptions.Default, headers = Nil)
- As Akka Http single request
val client = GraphQLClient(s "http://$host:$port/api/graphql" )
Then, query:
val query =
graphql "" "
query HeroAndFriends {
hero {
id
name
friends {
name
}
appearsIn
}
}
"""
val cursor: GraphQLCursor = client.query[HeroQuery](query)
val data: Future[GraphQLResponse[HeroQuery]] = cursor.result
typeHerosQuery=Map[String,List[Hero]]
caseclassPagination(offset:Int,size:Int)
valquery=
graphql"""
query Heros($offset:Int,$size:Int) {
heros(offset:$offset,size:$size) {
id
name
friends {
name
}
appearsIn
}
}
"""
valpage1:GraphQLCursor[HerosQuery,Pagination]=
client.query(query,Pagination(0,10))
valpage2:GraphQLCursor[HerosQuery,Pagination]=
page1.fetchMore(lastPage=>lastPage.copy(offset=lastPage.offset+lastPage.size ) )
importcom.github.jarlakxen.drunk._
importio.circe._,io.circe.generic.semiauto._
importsangria.macros._
caseclassUser(id:String,name:String)
valclient=GraphQLClient(s"http://$host:$port/api/graphql")
valmutation=
graphql"""
mutation($user1:String!,$user2:String!) {
user1: newUser(name:$user1) {
id
name
}
user2: newUser(name:$user2) {
id
name
}
}
"""
valresult:Future[GraphQLResponse[Map[String,User]]]=
client.query(mutation,Map("user1"->"123","user2"->"456"))
importcom.github.jarlakxen.drunk._
importsangria.introspection.IntrospectionSchema
valclient=GraphQLClient(s"http://$host:$port/api/graphql")
valresult:Future[GraphQLResponse[IntrospectionSchema]]=client.schema
It's very common in GraphQL to have response with polymorphic objects in the responses. One way to discriminate the type of object is to check the__typename
field. For that purpose there an special derive decoder incom.github.jarlakxen.drunk.circe._
:
importcom.github.jarlakxen.drunk.circe._
importio.circe._,io.circe.generic.semiauto._
traitCharacter{
defid:String
defname:Option[String]
deffriends:List[String]
defappearsIn:List[Episode.Value]
}
caseclassHuman(
id:String,
name:Option[String],
friends:List[String],
appearsIn:List[Episode.Value],
homePlanet:Option[String])extendsCharacter
caseclassDroid(
id:String,
name:Option[String],
friends:List[String],
appearsIn:List[Episode.Value],
primaryFunction:Option[String])extendsCharacter
implicitvalhumanDecoder:Decoder[Human]=deriveDecoder
implicitvaldroidDecoder:Decoder[Droid]=deriveDecoder
implicitvalcharacterDecoder:Decoder[Character]=deriveByTypenameDecoder(
"Human".decodeAs[Human],//for __typename: 'Human' is going to use humanDecoder
"Droid".decodeAs[Droid]//for __typename: 'Droid' is going to use droidDecoder
)
This code can be use to parse a response like:
{
"__typename":"Droid"
"name":"R2D2"
....
}
Take into account the client automatically adds the '__typename' field to every selector, so it's not required to be added in the queries.
There are several extension for GraphQL, this client supports:
To get the information of the extensions you can:
valcursor:GraphQLCursor=client.query[HeroQuery](query)
valextensions:Future[GraphQLExtensions]=cursor.extensions
valmetrics:Future[Option[GraphQLMetricsExtension]]=extensions.map(_.metrics)
valcacheControl:Future[Option[GraphQLCacheControlExtension]]=extensions.map(_.cacheControl)
If you have a question, or hit a problem, feel free to ask in theissues!
Or, if you encounter a bug, something is unclear in the code or documentation, don’t hesitate and open an issue.