Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/jarlakxen/drunk
A simple GraphQL client on top of Sangria, Akka HTTP and Circe.
https://github.com/jarlakxen/drunk
akka-http circe graphql graphql-client sangria scala
Last synced: 26 days ago
JSON representation
A simple GraphQL client on top of Sangria, Akka HTTP and Circe.
- Host: GitHub
- URL: https://github.com/jarlakxen/drunk
- Owner: Jarlakxen
- License: apache-2.0
- Created: 2018-01-19T21:17:16.000Z (almost 7 years ago)
- Default Branch: master
- Last Pushed: 2023-10-06T22:35:57.000Z (about 1 year ago)
- Last Synced: 2024-10-11T13:03:21.082Z (26 days ago)
- Topics: akka-http, circe, graphql, graphql-client, sangria, scala
- Language: Scala
- Homepage:
- Size: 41 KB
- Stars: 62
- Watchers: 6
- Forks: 21
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# Drunk
A simple GraphQL client on top of [Sangria](http://sangria-graphql.org/), [Akka HTTP](https://doc.akka.io/docs/akka-http/current/) and [Circe](https://circe.github.io/circe/).
## Quickstart
Add the following dependency:
```scala
resolvers += Resolver.bintrayRepo("jarlakxen", "maven")"com.github.jarlakxen" %% "drunk" % "2.5.0"
```Then, import:
```scala
import com.github.jarlakxen.drunk._
import io.circe._, io.circe.generic.semiauto._
import sangria.macros._
```There are three ways to create a `GraphQLClient`:
1) 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)```
2) 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)```
3) 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
```#### Working with the `GraphQLCursor`
```scala
type HerosQuery = Map[String, List[Hero]]
case class Pagination(offset: Int, size: Int)val query =
graphql"""
query Heros($offset: Int, $size: Int) {
heros(offset: $offset, size: $size) {
id
name
friends {
name
}
appearsIn
}
}
"""
val page1: GraphQLCursor[HerosQuery, Pagination] =
client.query(query, Pagination(0, 10))
val page2: GraphQLCursor[HerosQuery, Pagination] =
page1.fetchMore(lastPage => lastPage.copy(offset = lastPage.offset + lastPage.size ) )
```### Mutations
```scala
import com.github.jarlakxen.drunk._
import io.circe._, io.circe.generic.semiauto._
import sangria.macros._case class User(id: String, name: String)
val client = GraphQLClient(s"http://$host:$port/api/graphql")val mutation =
graphql"""
mutation($user1: String!, $user2: String!) {
user1: newUser(name: $user1) {
id
name
}
user2: newUser(name: $user2) {
id
name
}
}
"""
val result: Future[GraphQLResponse[Map[String, User]]] =
client.query(mutation, Map("user1" -> "123", "user2" -> "456"))
```### Schema Introspection
```scala
import com.github.jarlakxen.drunk._
import sangria.introspection.IntrospectionSchemaval client = GraphQLClient(s"http://$host:$port/api/graphql")
val result: Future[GraphQLResponse[IntrospectionSchema]] = client.schema
```## Typename Derive
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 in `com.github.jarlakxen.drunk.circe._`:
```scala
import com.github.jarlakxen.drunk.circe._
import io.circe._, io.circe.generic.semiauto._trait Character {
def id: String
def name: Option[String]
def friends: List[String]
def appearsIn: List[Episode.Value]
}
case class Human(
id: String,
name: Option[String],
friends: List[String],
appearsIn: List[Episode.Value],
homePlanet: Option[String]) extends Character
case class Droid(
id: String,
name: Option[String],
friends: List[String],
appearsIn: List[Episode.Value],
primaryFunction: Option[String]) extends Character
implicit val humanDecoder: Decoder[Human] = deriveDecoder
implicit val droidDecoder: Decoder[Droid] = deriveDecoder
implicit val characterDecoder: 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:
```json
{
"__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.
## Extensions
There are several extension for GraphQL, this client supports:
* [Apollo cache control](https://github.com/apollographql/apollo-cache-control)
* [sangria-slowlog](http://sangria-graphql.org/learn/#profiling-graphql-query-execution)To get the information of the extensions you can:
```scala
val cursor: GraphQLCursor = client.query[HeroQuery](query)
val extensions: Future[GraphQLExtensions] = cursor.extensions
val metrics: Future[Option[GraphQLMetricsExtension]] = extensions.map(_.metrics)
val cacheControl: Future[Option[GraphQLCacheControlExtension]] = extensions.map(_.cacheControl)
```## Contributing
If you have a question, or hit a problem, feel free to ask in the [issues](https://github.com/jarlakxen/drunk/issues)!
Or, if you encounter a bug, something is unclear in the code or documentation, don’t hesitate and open an issue.