A tiny module to add refined and newtype support for Schema and ArgBuilder
Add the following line in build.sbt
libraryDependencies += "com.github.niqdev" %% "caliban-refined" % "0.1.5"
Replace all the custom implementations like
implicit val nonEmptyStringSchema: Schema[Any, NonEmptyString] =
Schema.stringSchema.contramap(_.value)
implicit val nonNegIntArgBuilder: ArgBuilder[NonNegInt] = {
case value: IntValue =>
NonNegInt.from(value.toInt).leftMap(CalibanError.ExecutionError(_))
case other =>
Left(CalibanError.ExecutionError(s"Can't build a NonNegInt from input $other"))
}
with an import
// add import
import caliban.refined._
@newtype case class Id(int: PosInt)
@newtype case class Name(string: NonEmptyString)
case class User(id: Id, name: Name)
case class UserArg(id: Id)
case class Query(user: UserArg => User)
val resolver = Query(arg => User(arg.id, Name("myName")))
val api = GraphQL.graphQL(RootResolver(resolver))
See the tests for a complete example
A minimalistic version of GraphQL GitHub api with pagination, filters and authentication (TODO)
# run example
sbt -jvm-debug 5005 "examples/runMain com.github.niqdev.caliban.Main"
# verify endpoint
http -v :8080/api/graphql query='{users(first:1){totalCount}}'
query counts {
countUsers: users(first: 1) {
totalCount
}
countRepositories: repositories(first: 1) {
totalCount
}
countIssues: issues(first: 1) {
totalCount
}
}
{
"data": {
"countUsers": {
"totalCount": 2
},
"countRepositories": {
"totalCount": 40
},
"countIssues": {
"totalCount": 40
}
}
}
query nodes {
userNodes: users(first: 1) {
nodes {
id
name
createdAt
updatedAt
#repository > issue | issues
#repositories > issue | issues
}
}
repositoryNodes: repositories(first: 1) {
nodes {
id
name
url
isFork
createdAt
updatedAt
#issue
#issues
}
}
issueNodes: issues(first: 1) {
nodes {
id
number
status
title
body
createdAt
updatedAt
}
}
}
{
"data": {
"userNodes": {
"nodes": [
{
"id": "dXNlcjp2MTpmMGZiZTEzMS0zZjY1LTQxNDUtYjM3My01YmJmYzFjOWExYWU=",
"name": "zio",
"createdAt": "2020-08-11T18:25:25.411363Z",
"updatedAt": "2020-08-11T18:25:25.411363Z"
}
]
},
"repositoryNodes": {
"nodes": [
{
"id": "cmVwb3NpdG9yeTp2MToxOTQ2MDQ4Ny01ZmZkLTRkMzgtYjBlOS0xNmRiNDQ4NDYxNTU=",
"name": "zio-s3",
"url": "https://github.com/zio/zio-s3",
"isFork": false,
"createdAt": "2020-08-11T18:25:25.496188Z",
"updatedAt": "2020-08-11T18:25:25.496188Z"
}
]
},
"issueNodes": {
"nodes": [
{
"id": "aXNzdWU6djE6MjMzNTlhMjktZDQxYy00ODAxLWE2MjMtNGM2YzNmMGU5NjMy",
"number": 27,
"status": "CLOSE",
"title": "title6",
"body": "body6",
"createdAt": "2020-08-11T18:25:25.571263Z",
"updatedAt": "2020-08-11T18:25:25.571263Z"
}
]
}
}
}
# user:v1:f0fbe131-3f65-4145-b373-5bbfc1c9a1ae
echo "dXNlcjp2MTpmMGZiZTEzMS0zZjY1LTQxNDUtYjM3My01YmJmYzFjOWExYWU=" | base64 --decode
# repository:v1:19460487-5ffd-4d38-b0e9-16db44846155
echo "cmVwb3NpdG9yeTp2MToxOTQ2MDQ4Ny01ZmZkLTRkMzgtYjBlOS0xNmRiNDQ4NDYxNTU=" | base64 --decode
# issue:v1:23359a29-d41c-4801-a623-4c6c3f0e9632
echo "aXNzdWU6djE6MjMzNTlhMjktZDQxYy00ODAxLWE2MjMtNGM2YzNmMGU5NjMy" | base64 --decode
query findByParameter {
user(name: "zio") {
name
# it doesn't verify user ownership
repository(name: "shapeless") {
name
url
isFork
# it doesn't verify repository ownership
issue(number: 1) {
number
status
title
body
}
}
}
}
{
"data": {
"user": {
"name": "zio",
"repository": {
"name": "shapeless",
"url": "https://github.com/milessabin/shapeless",
"isFork": false,
"issue": {
"number": 1,
"status": "OPEN",
"title": "title0",
"body": "body0"
}
}
}
}
}
query findUserPaginated {
users(first: 10, after: "opaqueCursor") {
edges {
cursor
node {
name
repositories(first: 4) {
nodes {
name
}
}
}
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
totalCount
}
}
{
"data": {
"users": {
"edges": [
{
"cursor": "Y3Vyc29yOnYxOjI=",
"node": {
"name": "zio",
"repositories": {
"nodes": [
{
"name": "zio-lambda"
},
{
"name": "zio-web"
},
{
"name": "zio-ftp"
},
{
"name": "zio-actors"
}
]
}
}
},
{
"cursor": "Y3Vyc29yOnYxOjE=",
"node": {
"name": "typelevel",
"repositories": {
"nodes": [
{
"name": "scodec"
},
{
"name": "ciris"
},
{
"name": "refined"
},
{
"name": "shapeless"
}
]
}
}
}
],
"pageInfo": {
"hasNextPage": false,
"hasPreviousPage": false,
"startCursor": "Y3Vyc29yOnYxOjI=",
"endCursor": "Y3Vyc29yOnYxOjE="
},
"totalCount": 2
}
}
}
query findByNodeIds {
nodes(
ids: [
"dXNlcjp2MTpmMGZiZTEzMS0zZjY1LTQxNDUtYjM3My01YmJmYzFjOWExYWU=",
"cmVwb3NpdG9yeTp2MToyOTBlYzI2NS1lYzkxLTRhOWItYmRkYS03YjA5NzBkYjk5Y2I=",
"aXNzdWU6djE6MjMzNTlhMjktZDQxYy00ODAxLWE2MjMtNGM2YzNmMGU5NjMy"
]
) {
id
... on User {
name
}
... on Repository {
name
url
isFork
}
... on Issue {
number
status
title
body
}
}
}
{
"data": {
"nodes": [
{
"id": "dXNlcjp2MTpmMGZiZTEzMS0zZjY1LTQxNDUtYjM3My01YmJmYzFjOWExYWU=",
"name": "zio"
},
{
"id": "cmVwb3NpdG9yeTp2MToyOTBlYzI2NS1lYzkxLTRhOWItYmRkYS03YjA5NzBkYjk5Y2I=",
"name": "refined",
"url": "https://github.com/fthomas/refined",
"isFork": false
},
{
"id": "aXNzdWU6djE6MjMzNTlhMjktZDQxYy00ODAxLWE2MjMtNGM2YzNmMGU5NjMy",
"number": 27,
"status": "CLOSE",
"title": "title6",
"body": "body6"
}
]
}
}
- GraphQL
- GraphiQL
- GraphiQL
- Altair GraphQL Client (GUI)
- GraphiQL.app (GUI)
- graphiql (Docker)
- Pagination
- Filter
- Caliban
- Caliban (Documentation)
- Caliban: Designing a Functional GraphQL Library by Pierre Ricadat (Video)
- GraphQL in Scala with Caliban
- Other
- cats example
- query
node
andnodes
- query
user
andusers
- query
repository
andrepositories
- query
issue
andissues
- query
- abstract node
- abstract pagination (relay spec)
- abstract filters (drupal spec with droste) example
- pagination module - issue with
Node
interface, see possible solution - filters module
- refined/newtype module
- migrate from cats to zio
- mutations example
- subscriptions example
- enumeratum module (?)
- TLS
- JWT auth
- static GraphiQL
- static doc (markdown)
- helm chart + argocd deployment (live demo)
- tests !!!