Git Product home page Git Product logo

caliban's Introduction

Caliban

Release Artifacts Snapshot Artifacts Badge-Discord

Caliban is a purely functional library for building GraphQL servers and clients in Scala.

The design principles behind the library are the following:

  • minimal amount of boilerplate: no need to manually define a schema for every type in your API.
  • high performance: while every public interface is pure and immutable, library internals have been optimized for speed.
  • clean separation between schema definition and implementation: schema is defined and validated at compile time using Scala standard types, resolver (RootResolver) is a simple value provided at runtime.

Consult the Documentation to learn how to use Caliban.

Any questions? Head up to the #caliban channel on ZIO Discord.

Adopters

Here is a partial list of companies using Caliban in production.

Want to see your company here? Submit a PR!

caliban's People

Contributors

adamgfraser avatar alixba avatar blast-hardcheese avatar darl avatar develeon avatar fluxx avatar fokot avatar frekw avatar galales avatar ghostdogpr avatar guizmaii avatar guymers avatar irevive avatar javimartinez avatar jeejeeone avatar joprice avatar kubukoz avatar kyri-petrou avatar moonkev avatar nox213 avatar oyvindberg avatar paulpdaniels avatar phderome avatar rtimush avatar scala-steward avatar sh0hei avatar svenw avatar vpavkin avatar yarian avatar yoohaemin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

caliban's Issues

Add ArgBuilder.mapM

This makes it easier to reuse existing builders.

def mapM[A](f: T => IO[ExecutionError, A]): ArgBuilder[A]

Example usage (with refined's NonEmptyString):

implicit val nonEmptyStringArgBuilder: ArgBuilder[NonEmptyString] = 
  ArgBuilder.string.mapM(value => IO.fromEither(NonEmptyString.from(value)).mapError(ExecutionError(_)))

Better compile errors when failing to derive the Schema

Sometimes when you miss an instance of Schema for a custom type that is not a case class nor a sealed trait, the compile error just tells you it couldn't find the Schema but doesn't tell you what's missing exactly.

I'm not sure how much can be done here and how much needs to be done in Magnolia but it's worth investigating.

Use of custom types in GQL input types?

Hello,

First, kudos to an amazingly clean GQL service. Impossible to make it any better.

I am stuck with some code that users Enumerations instead of sealed traits to express Enums. It was easy enough to create a schema for Enumeration types (see below).

My question is what do I need to provide the schema to be able to use the Enum type also as an "input type" in GQL.

Simply putting it in as a field in the arguments case class is not enough (not surprising). It complains that it misses the Query Case class Schema, probably because it does not know how to marshall the input into the internal type.

Any insights on how to provide the compiler with the right info/implcits will be more than welcome. If I come up with a general approach, I'll be glad to make it available.

Thanks

The barebones example code (that does not compile) looks like:

import caliban.RootResolver
import caliban.GraphQL._

import scala.collection.mutable

object QueryWithEnum {
	type ThingType = ThingType.Value
	object ThingType extends Enumeration {
		val BIG = Value(1)
		val SMALLISH = Value(2)
	}
	import caliban.schema
	def enumSchema[E <: Enumeration](name: String) = schema.Schema.scalarSchema[E#Value]("name", None, e => caliban.ResponseValue.StringValue(e.toString))
	implicit val thingTypeSchema = enumSchema[ThingType.type]("ThingType")

	case class Thing(k: String, name: Option[String], thingType: ThingType, value: Double)

	private val thingStore: mutable.Map[String, Thing] = mutable.Map(
		"thingOne" -> Thing("thingOne", None, ThingType.BIG, 33.2),
		"thingTwo" -> Thing("thingTwo", Some("Dearest Thingy"), ThingType.SMALLISH, 324.34)
	)
	case class ByKey(k: String)
	def allThingsEnum: List[Thing] = thingStore.values.toList
	def oneThingEnum(tk: ByKey): Option[Thing] = thingStore.get(tk.k)

	case class ByType(t: ThingType)
	def someThingsEnum(tk: ByType): List[Thing] = allThingsEnum.filter(_.thingType == tk.t)

	case class Query(things: List[Thing], thing: ByKey => Option[Thing], someThings: ByType => List[Thing])

	val queryService = Query(allThingsEnum, oneThingEnum, someThingsEnum)
	val interpreter = graphQL(RootResolver(queryService))

}

And the compiler error:

[error] MessageQuery.scala:34:27: could not find implicit value for parameter querySchema: caliban.schema.Schema[R,com.cruxsystems.ais.service.inspection.QueryWithEnum.Query] [error] val interpreter = graphQL(RootResolver(queryService))

Make the examples to compile

Trying the examples (to understand the lib), but they not compile.
Is it also possible to have a build.sbt as example for running the project?

Thanks

Akka HTTP Adapter

Add a new module similar to caliban-http4s named caliban-akka-http that exposes a REST and a WebSocket API for a given GraphQL interpreter.

If there is common code between the http4s and the akka one, it should be factored out.

I haven't used Akka in a while, so external contributors are welcome to pick this one up!

EDIT: HTTP is done, only WebSocket is missing

Http4s Adapter: use Circe to transform the result to Json

The current code in Http4sAdapter converts the result to a string, then is parsed into Json. Instead, we could use Circe (which is already in scope in the Http4sAdapter) to convert the result to Json directly. This will take care of edge cases (breaklines, etc) and will be faster.

Here's the existing code:

result <- execute(interpreter, query)
                     .fold(
                       err => s"""{"errors":["${err.toString.replace("\"", "'")}"]}""",
                       result => s"""{"data":$result}"""
                     )
json     <- Task.fromEither(parse(result))

Instead, we can define an Encoder[ResponseValue] and Encoder[ErrorResponse] to convert to Json directly.

Batch queries

Sangria has deferred resolvers
https://sangria-graphql.org/learn/#deferred-value-resolution
or Fetchers
https://sangria-graphql.org/learn/#high-level-fetch-api

In your example I would like to return CharacterDetails instead of Characters. I do not want to calculate statistics everytime only when they are queried and I do want to calculate statistics for all returned characters with single query (not having N+1 problem). Look at Repo.statistics. I find solutions with memoizing statistics call to repo. Is there an easier way to do that?

case class CharacterDetail(name: String, nicknames: List[String], origin: Origin, role: Option[Role], statistics: Task[Int])

object CharacterDetail {
  def apply(c: Character, statistics: Task[Int]): CharacterDetail =
    new CharacterDetail(c.name, c.nicknames, c.origin, c.role, statistics)
}

object Repo {
  def statistics(characterNames: List[String]): Task[Map[String, Int]] = ZIO.effect({
    println(s"statistics for ${characterNames.mkString("[", ",", "]")}")     characterNames.map(n => (n, n.length)).toMap
  })
}

  case class Queries(
    @GQLDescription("Return all characters from a given origin")
    characters: CharactersArgs => URIO[Console, List[CharacterDetail]],
  )

val interpreter = interpreter = graphQL(
        RootResolver(
          Queries(
            args => service.getCharacters(args.origin).flatMap(characters =>
              Repo.statistics(characters.map(_.name)).memoize
                .map(allStatistics =>
                  characters.map(c => CharacterDetail(c, allStatistics.map(_(c.name))))
                )
            ),
          ),
          Mutations(args => service.deleteCharacter(args.name)),
          Subscriptions(service.deletedEvents)
        )
      )

Modelling computed fields depending on parent

I used this a lot in sangria gql api. In your example I would like to return CharacterDetails instead of Characters. I do not want to calculate statistics everytime only when they are queried, that's why I model this field as a Task and not an Int.

case class CharacterDetail(name: String, nicknames: List[String], origin: Origin, role: Option[Role], statistics: Task[Int])

  object CharacterDetail {
    def apply(c: Character): CharacterDetail =
      new CharacterDetail(c.name, c.nicknames, c.origin, c.role, ZIO.effect({
        println(s"calculating statistics for ${c.name}")
        c.nicknames.length
      }))
  }

  case class Queries(
    @GQLDescription("Return all characters from a given origin")
    characters: CharactersArgs => URIO[Console, List[Character]],
    @GQLDeprecated("Use `characters`")
    character: CharacterArgs => URIO[Console, Option[CharacterDetail]],
  )

What is the proper way to model it? Is this a good start? And how to write character function?

Parameters for subscriptions?

I am looking to add a subscription to the GQL endpoint that I developing with Caliban, and I can't see the way to provide parameters in the subscription request. It seems that the mechanism should be similar to the queries (e.g. from the apollo documentation --> https://www.apollographql.com/docs/react/data/subscriptions/).

In the Caliban documentation (https://ghostdogpr.github.io/caliban/docs/#subscriptions), I see that the parameter type for the subscription must be ZStream[Any, Nothing, ] and not a function => ZStream[Any, Nothing, ] following the pattern of Queries and Mutations.

The examples don't take any parameters (e.g. deletedEvents) either.

I am not sure what I am not seeing, I would appreciate a pointer in the right direction.

Thanks

Miguel

Support BigInt and BigDecimal

It's not officially supported by the spec but it could be nice to have for people who want it. Maybe using an ADT for IntValue and FloatValue?

Support partial results

Currently, execute either fully succeeds with ResponseValue or fails with CalibanError. It would be nice to support partial results โ€” any evaluation error should bubble up until the first nullable node, and the error itself should be reported together with the response. For example, given the following schema:

Queries(one: Task[Int], two: Task[Int])

the query

query {
  one
  two
}

should be able to produce:

{
  "data": {
    "one": 1,
    "two": null
  },
  "errors": [
    {
      "message": "Failed :(",
      "locations": [ { "line": 3, "column": 3 } ],
      "path": [ "two" ]
    }
  ]
}

In terms of schema, this means that effects other than UIO should be represented as nullable fields.

Add FAQ to website

Frequently Asked Questions:

  • compile error saying it doesn't find a Schema, why?
  • how to deal with recursive types?
  • how to deal with authentication/authorization?

Implement missing query validations

Cats Effect instead of ZIO?

Hi,

Any particular reason to stick to ZIO instead of using Cats Effect?
This looks exactly what we need, but we went with Monix, which makes integration a bit more complicated...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.