### Serve GraphQL API with minimal setup Source: https://ghostdogpr.github.io/caliban/docs/adapters.html Use the unsafe.runServer extension method to quickly start a GraphQL server. ```scala import caliban._ import caliban.quick._ val api: GraphQL[Any] = ??? api.unsafe.runServer( port = 8080, apiPath = "/api/graphql", graphiqlPath = Some("/graphiql"), uploadPath = Some("/upload/graphql"), // optional, for enabling GraphQL uploads ) ``` -------------------------------- ### Example of Manual Schema Generation Source: https://ghostdogpr.github.io/caliban/docs/server-codegen.html An example of the `calibanGenSchema` command, specifying the input schema path, output file path, and enabling derivation. ```bash calibanGenSchema project/schema.graphql src/main/MyAPI.scala --addDerives true ``` -------------------------------- ### GraphQL @defer Response Examples Source: https://ghostdogpr.github.io/caliban/docs/optimization.html Example JSON responses for the initial and incremental parts of a deferred GraphQL query. ```json { "data": { "characters": [ { "name": "James Holden", "nicknames": [ "Hoss", "Holden" ] }, { "name": "Naomi Nagata", "nicknames": [ "Naomi" ] } ] }, "hasNext": true } ``` ```json { "incremental": [{ "label": "characterDetails", "path": ["characters", 0], "data": { "role": "Captain", "origin": "Earth" } }, { "label": "characterDetails", "path": ["characters", 1], "data": { "role": "Executive Officer", "origin": "Belter" } }], "hasNext": true } ``` ```json { "hasNext": false } ``` -------------------------------- ### Caliban with Cats Effect and ZIO Contextual Interop Source: https://ghostdogpr.github.io/caliban/docs/interop.html A comprehensive example of setting up Caliban with Cats Effect and ZIO, including contextual interop for logging and tracing. Requires Async, Logger, Local, InjectEnv, and Runtime instances. ```scala import caliban._ import caliban.interop.cats._ import caliban.interop.cats.implicits._ import caliban.schema.GenericSchema import cats.data.Kleisli import cats.effect.{ Async, ExitCode, IO, IOApp } import cats.effect.std.Dispatcher import cats.effect.std.Console import cats.syntax.flatMap._ import cats.syntax.functor._ import cats.mtl.Local import cats.mtl.syntax.local._ import zio.{ Runtime, ZEnvironment } object Simple extends IOApp { case class Queries[F[_]](numbers: List[Int], randomNumber: F[Int]) val query = """ { numbers randomNumber } """ case class TraceId(value: String) type TraceLocal[F[_]] = Local[F, TraceId] trait Logger[F[_]] { def info(message: String): F[Unit] } def program[F[_]: Async](implicit logger: Logger[F], local: Local[F, TraceId], inject: InjectEnv[F, TraceId], runtime: Runtime[TraceId] ): F[ExitCode] = Dispatcher.parallel[F].use { implicit dispatcher => implicit val interop: CatsInterop.Contextual[F, TraceId] = CatsInterop.contextual(dispatcher) // required for a derivation of the schema val genRandomNumber = logger.info("Generating number") >> Async[F].delay(scala.util.Random.nextInt()) val queries = Queries( List(1, 2, 3, 4), genRandomNumber.scope[TraceId](TraceId("gen-number")) ) val api: GraphQL[TraceId] = { val schema: GenericSchema[TraceId] = new GenericSchema[TraceId] {} import schema.auto._ graphQL(RootResolver(queries)) } for { interpreter <- api.interpreterF[F] result <- interpreter.executeAsync[F](query) _ <- logger.info(result.data.toString) } yield ExitCode.Success } override def run(args: List[String]): IO[ExitCode] = { type Effect[A] = Kleisli[IO, TraceId, A] val root = TraceId("root") implicit val runtime = Runtime.default.withEnvironment(ZEnvironment(root)) implicit val logger = new Logger[Effect] { def info(message: String): Effect[Unit] = for { traceId <- Local[Effect, TraceId].ask[TraceId] _ <- Console[Effect].println(s"$message - $traceId") } yield () } program[Effect].run(root) } } ``` -------------------------------- ### GraphQL @defer Directive Example Source: https://ghostdogpr.github.io/caliban/docs/optimization.html Example of a GraphQL query using the experimental @defer directive to delay fragment execution. ```graphql query { characters { name nicknames ... @defer(label: "characterDetails") { role origin } } } ``` -------------------------------- ### Define GraphQL API Instance Source: https://ghostdogpr.github.io/caliban/docs/client-codegen.html Example of a public GraphQL instance required for the plugin to access. ```scala package com.example.my.awesome.project.api import caliban._ object CalibanServer { val graphqlApi: GraphQL[MyEnv] = graphQL(Resolvers.resolver) } ``` -------------------------------- ### Define custom Schema for types Source: https://ghostdogpr.github.io/caliban/docs/schema.html Examples of creating custom Schema instances using contramap or the scalarSchema helper. ```scala import caliban.schema._ implicit val nonEmptyStringSchema: Schema[Any, NonEmptyString] = Schema.stringSchema.contramap(_.value) ``` ```scala import caliban.schema._ import caliban.ResponseValue.ObjectValue implicit val unitSchema: Schema[Any, Unit] = Schema.scalarSchema("Unit", None, None, None, _ => ObjectValue(Nil)) ``` -------------------------------- ### Configure Akka Http Adapter with Authentication Interceptor Source: https://ghostdogpr.github.io/caliban/docs/adapters.html This example demonstrates how to transform a GraphQL interpreter into an HttpUploadInterpreter, define authentication logic, and use an interceptor to modify the interpreter's environment for the Akka Http adapter. ```scala val graphQLInterpreter: GraphQLInterpreter[AuthToken, CalibanError] = ??? // turn our GraphQL interpreter into an HttpInterpreter val noAuthInterpreter: HttpInterpreter[AuthToken, CalibanError] = HttpInterpreter(graphQLInterpreter) // define authentication logic (from a ServerRequest, fail or build an AuthToken) val auth: ZLayer[ServerRequest, TapirResponse, AuthToken] = ??? // pass our interceptor to eliminate the AuthToken requirement from the environment val authInterpreter: HttpUploadInterpreter[Any, CalibanError] = httpInterpreter.intercept(auth) // get our route for Akka Http val route = AkkaHttpAdapter.default().makeHttpService(authInterpreter) ``` -------------------------------- ### Enable CalibanPlugin in build.sbt Source: https://ghostdogpr.github.io/caliban/docs/client-codegen.html Enable the `CalibanPlugin` in your `build.sbt` file to start generating client code from schema files. ```sbt enablePlugins(CalibanPlugin) ``` -------------------------------- ### Manually Enable Federated Tracing Source: https://ghostdogpr.github.io/caliban/docs/federation.html If not using wrappers or for custom setups, manually enable tracing by calling `request.withFederatedTracing` after detecting the trace header. ```scala request.withFederatedTracing ``` -------------------------------- ### Query Relay Connection Source: https://ghostdogpr.github.io/caliban/docs/relay-connections.html Example GraphQL query structure for a paginated connection field. ```graphql { queryName(first: 5, after: "cursor") { pageInfo { hasNextPage hasPreviousPage startCursor endCursor } edges { cursor node { id # additional fields } } } } ``` -------------------------------- ### GraphQL Query for GitHub Repository Data Source: https://ghostdogpr.github.io/caliban/docs/stitching.html An example GraphQL query to fetch user and repository information, including details about open pull requests and their authors. This query can be executed against the configured GitHub API endpoint. ```graphql query { GetUser(name:"ghostdogpr", repository:"caliban") { id name featuredRepository { pullRequests(states: OPEN, first: 10) { edges { node { title author { login } } } } } } } ``` -------------------------------- ### Configure WebSocket for subscriptions Source: https://ghostdogpr.github.io/caliban/docs/laminext.html Initialize a WebSocket with the graphql-ws protocol and prepare a subscription using the toSubscription extension. ```scala val ws = WebSocket.url("ws://localhost:8088/ws/graphql", "graphql-ws").graphql.build() def deletedCharacters = Subscriptions.characterDeleted.toSubscription(ws) ``` -------------------------------- ### Connect and receive subscription events Source: https://ghostdogpr.github.io/caliban/docs/laminext.html Establish the WebSocket connection, initialize the protocol, and process incoming subscription events. ```scala ws.connect, ws.connected .map(_ => ws.init()) .flatMap(_ => deletedCharacters.received.collectRight) --> (name => characters.update(_.filterNot(_ == name))), ``` -------------------------------- ### Create and execute a GraphQL query with Cats IO Source: https://ghostdogpr.github.io/caliban/docs/interop.html Demonstrates creating an interpreter and executing a query using Cats IO, requiring an implicit ZIO runtime and a Dispatcher for schema derivation. ```scala import caliban._ import caliban.interop.cats.implicits._ import caliban.schema.Schema.auto._ import cats.effect.{ ExitCode, IO, IOApp } import cats.effect.std.Dispatcher import zio.Runtime object ExampleCatsInterop extends IOApp { implicit val zioRuntime = Runtime.default case class Queries(numbers: List[Int], randomNumber: IO[Int]) val queries = Queries(List(1, 2, 3, 4), IO(scala.util.Random.nextInt())) val query = """ { numbers randomNumber }""" override def run(args: List[String]): IO[ExitCode] = Dispatcher.parallel[IO].use { implicit dispatcher => // required for a derivation of the schema val api = graphQL(RootResolver(queries)) for { interpreter <- api.interpreterF[IO] result <- interpreter.executeAsync[IO](query) _ <- IO(println(result.data)) } yield ExitCode.Success } } ``` -------------------------------- ### Customize Client Generation Settings Source: https://ghostdogpr.github.io/caliban/docs/client-codegen.html Advanced configuration for client generation, including package name and file splitting. ```scala lazy val api = project .enablePlugins(CompileTimeCalibanServerPlugin) .settings( Compile / ctCalibanServer / ctCalibanServerSettings := Seq( "com.example.my.awesome.project.api.CalibanServer.graphqlApi" -> ClientGenerationSettings( packageName = "com.example.my.awesome.project.client.generated", clientName = "CalibanClient", splitFiles = true ), ) ) ``` -------------------------------- ### Generated Case Class with Lazy Field Source: https://ghostdogpr.github.io/caliban/docs/server-codegen.html Example of a generated Scala case class where a field annotated with `@lazy` is wrapped in `zio.UIO`. ```scala case class MyType(myLazyField: zio.UIO[String], myField: String) ``` -------------------------------- ### Generate Client from Server URL Source: https://ghostdogpr.github.io/caliban/docs/client-codegen.html Configure `calibanSetting` with a server URL to generate a client directly from a running GraphQL endpoint. Specify a client name and package. ```sbt Compile / caliban / calibanSettings += calibanSetting(url("http://my-example-service/graphql"))( cs => cs.clientName("ExampleServiceClient") .packageName("com.example.graphql.client") ) ``` -------------------------------- ### Generate Client via CLI Source: https://ghostdogpr.github.io/caliban/docs/client-codegen.html Command to generate a Scala client file from a GraphQL schema. ```bash calibanGenClient project/schema.graphql src/main/client/Client.scala --genView true ``` -------------------------------- ### Implement DataSource Source: https://ghostdogpr.github.io/caliban/docs/optimization.html Boilerplate for creating a batched DataSource. ```scala val UserDataSource = new DataSource.Batched[Any, GetUserName] { override val identifier: String = ??? override def run(requests: Chunk[GetUserName]): ZIO[Any, Nothing, CompletedRequestMap] = ??? } ``` -------------------------------- ### Define cyclical types for manual schema construction Source: https://ghostdogpr.github.io/caliban/docs/schema.html Example of co-recursive data structures that require manual schema definition to avoid derivation errors. ```scala import zio.UIO case class Group(id: String, users: UIO[List[User]], parent: UIO[Option[Group]], organization: UIO[Organization]) case class Organization(id: String, groups: UIO[List[Group]]) case class User(id: String, group: UIO[Group]) ``` -------------------------------- ### Define @oneOf input objects Source: https://ghostdogpr.github.io/caliban/docs/schema.html Examples of defining @oneOf input objects using sealed traits in Scala 2 and enums in Scala 3. ```scala import caliban.schema.Annotations.GQLOneOfInput case class Name(firstName: String, lastName: String) @GQLOneOfInput sealed trait AuthorInput object AuthorInput { case class ById(id: String) extends AuthorInput case class ByName(name: Name) extends AuthorInput } case class AuthorArgs(lookup: AuthorInput) case class Queries(author: AuthorArgs => String) ``` ```scala import caliban.schema.Annotations.GQLOneOfInput case class Name(firstName: String, lastName: String) @GQLOneOfInput enum AuthorInput { case ById(id: String) case ByName(name: Name) } case class AuthorArgs(lookup: AuthorInput) case class Queries(author: AuthorArgs => String) ``` -------------------------------- ### Configure Server-Side Plugin Source: https://ghostdogpr.github.io/caliban/docs/client-codegen.html Minimal configuration to link the GraphQL instance to the server-side plugin. ```scala lazy val api = project .enablePlugins(CompileTimeCalibanServerPlugin) .settings( Compile / ctCalibanServer / ctCalibanServerSettings := Seq( "com.example.my.awesome.project.api.CalibanServer.graphqlApi" -> ClientGenerationSettings.default ) ) ``` -------------------------------- ### Exclude Field using ExcludeDirectives Transformer Source: https://ghostdogpr.github.io/caliban/docs/schema.html This example demonstrates how to exclude a field annotated with a specific directive. The `Beta` directive is used to mark the `nicknames` field, and then `ExcludeDirectives` is applied to remove it from the v1 API. ```scala case class Beta() extends GQLDirective(Directive("beta")) case class Queries(character: Character) case class Character( name: String, @Beta nicknames: List[String] ) val apiBeta = graphQL(RootResolver(Queries(???, ???))) val apiV1 = apiBeta.transform(Transformer.ExcludeDirectives(Beta())) ``` -------------------------------- ### Compose ZQuery for Batching Source: https://ghostdogpr.github.io/caliban/docs/optimization.html Demonstrates how to use ZQuery to automatically batch parallel requests. ```scala val getAllUserIds: ZQuery[Any, Nothing, List[Int]] = ??? def getUserNameById(id: Int): ZQuery[Any, Nothing, String] = ??? for { userIds <- getAllUserIds userNames <- ZQuery.foreachPar(userIds)(getUserNameById) } yield userNames ``` -------------------------------- ### Configure server module reference Source: https://ghostdogpr.github.io/caliban/docs/client-codegen.html Linking the server module to the client module to enable code generation for the specified API. ```scala lazy val client = project .enablePlugins(CompileTimeCalibanClientPlugin) .settings( Compile / ctCalibanClient / ctCalibanClientsSettings := Seq(api) ) ``` -------------------------------- ### Serve GraphQL API over HTTP Source: https://ghostdogpr.github.io/caliban/docs Use the `unsafe.runServer` method with the caliban-quick module to serve your GraphQL API on a specified port and path. This requires importing `caliban.quick._`. ```scala import caliban.quick._ // adds extension methods to `api` api.unsafe.runServer( port = 8080, apiPath = "/api/graphql", ) ``` -------------------------------- ### Compose GraphQL handlers manually Source: https://ghostdogpr.github.io/caliban/docs/adapters.html Create a ZIO HTTP handler and manually compose it into an application for more control. ```scala import caliban._ import caliban.quick._ import zio.http._ val api: GraphQL[Any] = ??? for { handlers <- api.handlers // Alternatively, without imported syntax: handlers2 <- api.interpreter.map(QuickAdapter(_).handlers) // Creates a handler which serves the GraphiQL API from CDN graphiql = GraphiQLHandler.handler(apiPath = "/api/graphql", wsPath = None) app = Routes( Method.ANY / "api" / "graphql" -> handlers.api, Method.GET / "graphiql" -> graphiql, Method.POST / "upload" / "graphql" -> handlers.upload // Add more routes, apply middleware, etc. ) _ <- app.serve[Any].provide(Server.defaultWithPort(8080)) } yield () ``` -------------------------------- ### Add Caliban-Quick Dependency Source: https://ghostdogpr.github.io/caliban/docs Add the caliban-quick dependency to your build.sbt file to easily serve your GraphQL API over HTTP using zio-http. ```scala "com.github.ghostdogpr" %% "caliban-quick" % "3.0.0" ``` -------------------------------- ### Import Laminext extensions Source: https://ghostdogpr.github.io/caliban/docs/laminext.html Import the necessary extensions to enable Laminext functionality for Caliban. ```scala import caliban.client.laminext._ ``` -------------------------------- ### Create ZQuery from Request Source: https://ghostdogpr.github.io/caliban/docs/optimization.html Constructs a ZQuery using a request and a data source. ```scala def getUserNameById(id: Int): ZQuery[Any, Nothing, String] = ZQuery.fromRequest(GetUserName(id))(UserDataSource) ``` -------------------------------- ### Create GraphQL API Definition Source: https://ghostdogpr.github.io/caliban/docs Wrap your query resolver in a RootResolver and use the `graphQL` function to create the GraphQL API definition. The schema is derived at compile time. ```scala import caliban._ import caliban.schema.Schema.auto._ import caliban.schema.ArgBuilder.auto._ val api = graphQL(RootResolver(queries)) ``` -------------------------------- ### Configuration for GitHub Token Source: https://ghostdogpr.github.io/caliban/docs/stitching.html Defines a Configuration case class and a layer to read the GitHub token from the environment variable GITHUB_TOKEN. This is necessary for authenticating with the GitHub API. ```scala case class Configuration(githubToken: String) object Configuration { def fromEnvironment = (for { githubToken <- read("GITHUB_TOKEN") } yield Configuration(githubToken)).toLayer private def read(key: String): Task[String] = Task.effect( sys.env(key) ) } ``` -------------------------------- ### Manually Generate Caliban Client Source: https://ghostdogpr.github.io/caliban/docs/client-codegen.html Use the `calibanGenClient` sbt command to manually trigger client code generation. Specify schema path, output path, and various optional settings. ```bash calibanGenClient schemaPath outputPath [--scalafmtPath path] [--headers name:value,name2:value2] [--genView true|false] [--scalarMappings gqlType:f.q.d.n.Type,gqlType2:f.q.d.n.Type2] [--imports a.b.c._,c.d.E] [--splitFiles true|false] [--enableFmt true|false] [--extensibleEnums true|false] [--excludeDeprecated true|false] ``` -------------------------------- ### Configure output directory to target Source: https://ghostdogpr.github.io/caliban/docs/client-codegen.html Redirecting generated code from the source directory to the target directory to avoid versioning generated files. ```scala lazy val client = project .enablePlugins(CompileTimeCalibanClientPlugin) .settings( Compile / ctCalibanClient / ctCalibanClientsSettings := Seq(api), Compile / ctCalibanClient / ctCalibanClientsVersionedCode := false // By default, it's true. ) ``` -------------------------------- ### Cats Effect Contextual Interop with ZIO Source: https://ghostdogpr.github.io/caliban/docs/interop.html Share a context between cats-effect and ZIO using CatsInterop. Requires implicit Dispatcher and Runtime. ```scala import cats.data.Kleisli import cats.effect.IO import cats.effect.std.Dispatcher import caliban.interop.cats.CatsInterop import zio.RIO trait Context type Effect[A] = Kleisli[IO, Context, A] implicit val dispatcher: Dispatcher[Effect] = ??? implicit val runtime: Runtime[Context] = ??? val interop: CatsInterop.Contextual[Effect, Context] = CatsInterop.contextual(dispatcher) val rio: RIO[Context, Int] = ??? val ce: Kleisli[IO, Context, Int] = ??? val fromRIO: Kleisli[IO, Context, Int] = interop.toEffect(rio) val fromCE: RIO[Context, Int] = interop.fromEffect(ce) ``` -------------------------------- ### Enable CompileTimeCalibanServerPlugin Source: https://ghostdogpr.github.io/caliban/docs/client-codegen.html Enable the server plugin in an sbt project module. ```scala lazy val api = project .enablePlugins(CompileTimeCalibanServerPlugin) ``` -------------------------------- ### Apply Wrappers to a GraphQL API Source: https://ghostdogpr.github.io/caliban/docs/middleware.html Demonstrates how to register a wrapper with a GraphQL API using the withWrapper method or the @@ operator. ```scala val api = graphQL(...).withWrapper(wrapper) // or val api = graphQL(...) @@ wrapper ``` -------------------------------- ### Create a ServerEndpoint Source: https://ghostdogpr.github.io/caliban/docs/interop.html Convert a standard endpoint into a ServerEndpoint to reuse logic for both HTTP and GraphQL. ```scala import sttp.tapir.server.ServerEndpoint val addBookEndpoint: ServerEndpoint.Full[Unit, Unit, (Book, String), Nothing, Unit, Any, UIO] = addBook.serverLogicSuccess[UIO] { case (book, token) => bookAddLogic(book, token) } ``` -------------------------------- ### Add Caliban Quick dependency Source: https://ghostdogpr.github.io/caliban/docs/adapters.html Add this line to your build.sbt file to include the library. ```scala libraryDependencies += "com.github.ghostdogpr" %% "caliban-quick" % "3.0.0" ``` -------------------------------- ### Generate Caliban Client Code Source: https://ghostdogpr.github.io/caliban/docs/client.html Run this sbt command to generate Scala client code from a GraphQL schema. ```bash calibanGenClient schemaPath outputPath ``` ```bash calibanGenClient project/schema.graphql src/main/client/Client.scala ``` -------------------------------- ### Generate Schema Manually via SBT Command Source: https://ghostdogpr.github.io/caliban/docs/server-codegen.html Use this sbt command to manually generate the schema, providing the path to your schema file and the desired output path for the generated code. ```bash calibanGenSchema schemaPath outputPath [options] ``` -------------------------------- ### Visualize Generated GraphQL Schema Source: https://ghostdogpr.github.io/caliban/docs Use the `render` method on the API object to visualize the generated GraphQL schema. ```graphql type Character { name: String! age: Int! } type Queries { characters: [Character!]! character(name: String!): Character } ``` -------------------------------- ### Provide Environment to Interpreter Source: https://ghostdogpr.github.io/caliban/docs/middleware.html Provides a specific environment to a GraphQL interpreter, changing its environment type to `Any`. ```scala // provide the environment val i3: GraphQLInterpreter[Any, CalibanError] = i.provide(myEnv) ``` -------------------------------- ### Implement DataSource Run Logic Source: https://ghostdogpr.github.io/caliban/docs/optimization.html Handles single and batch request execution within the DataSource. ```scala override def run(requests: Chunk[GetUserName]): ZIO[Any, Nothing, CompletedRequestMap] = { val resultMap = CompletedRequestMap.empty requests.toList match { case request :: Nil => // get user by ID e.g. SELECT name FROM users WHERE id = $id val result: Task[String] = ??? result.exit.map(resultMap.insert(request)) case batch => // get multiple users at once e.g. SELECT id, name FROM users WHERE id IN ($ids) val result: Task[List[(Int, String)]] = ??? result.fold( err => requests.foldLeft(resultMap) { case (map, req) => map.insert(req)(Exit.fail(err)) }, _.foldLeft(resultMap) { case (map, (id, name)) => map.insert(GetUserName(id))(Exit.succeed(name)) } ) } } ``` -------------------------------- ### Enable Auto Derivation Source: https://ghostdogpr.github.io/caliban/docs/schema.html Import the auto derivation package to automatically generate Schema instances for case classes and sealed traits. ```scala import caliban.schema.Schema.auto._ ``` -------------------------------- ### GraphQL API with Custom Environment (Scala 3) Source: https://ghostdogpr.github.io/caliban/docs/schema.html In Scala 3, explicitly specify the ZIO environment type parameter to `graphQL` when it's not `Any`. Alternatively, provide an implicit `Schema` instance for the root operations to allow inference. ```scala val queries = Queries(ZIO.attempt(???), _ => ZIO.succeed(???)) val api = graphQL[MyEnv, Queries, Unit, Unit](RootResolver(queries)) // or // implicit val queriesSchema: Schema[MyEnv, Queries] = Schema.gen // val api = graphQL(RootResolver(queries)) // it will infer MyEnv thanks to the instance above ``` -------------------------------- ### Enable Apollo Federated Tracing Source: https://ghostdogpr.github.io/caliban/docs/federation.html Apply the `ApolloFederatedTracing.wrapper()` to your API to enable federated tracing. This wrapper automatically detects the `apollo-federation-include-trace` header. ```scala import caliban.federation.tracing.ApolloFederatedTracing val api = schema @@ federated(resolver, additionalResolvers: _*) @@ ApolloFederatedTracing.wrapper() ``` -------------------------------- ### Combine Pre-defined Wrappers Source: https://ghostdogpr.github.io/caliban/docs/middleware.html Shows the application of multiple pre-defined wrappers to a GraphQL API using the chaining operator. ```scala val api = graphQL(...) @@ maxDepth(50) @@ timeout(3 seconds) @@ printSlowQueries(500 millis) @@ apolloTracing() @@ Caching.extension() ``` -------------------------------- ### Convert ServerEndpoint to GraphQL Source: https://ghostdogpr.github.io/caliban/docs/interop.html Generate a GraphQL API directly from a ServerEndpoint. ```scala import caliban.schema.Schema.auto._ import caliban.schema.ArgBuilder.auto._ val api: GraphQL[Any] = addBookEndpoint.toGraphQL ``` -------------------------------- ### Apply Tracing Wrapper to API Source: https://ghostdogpr.github.io/caliban/docs/middleware.html Integrate OpenTelemetry tracing into your Caliban API by applying the TracingWrapper. This measures each effect as a span for optimization analysis. ```scala import caliban.tracing.TracingWrapper import zio.telemetry.opentelemetry.Tracing val api: GraphQL[Any] = ??? val tracedApi = api @@ TracingWrapper.traced ``` -------------------------------- ### Configure ArgBuilder for custom types Source: https://ghostdogpr.github.io/caliban/docs/schema.html Import auto-derivation or manually define ArgBuilder and Schema instances for custom types. ```scala import caliban.schema.ArgBuilder.auto._ ``` ```scala import caliban.schema.{ArgBuilder, Schema} case class MyClass(field: String) implicit val argBuilderForMyClass: ArgBuilder[MyClass] = ArgBuilder.gen implicit val schemaForMyClass: Schema[Any, MyClass] = Schema.gen ``` ```scala import caliban.schema.{ArgBuilder, Schema} case class MyClass(field: String) derives Schema.SemiAuto, ArgBuilder // if you don't want to use the `derives` syntax, you can also use the following: given ArgBuilder[MyClass] = ArgBuilder.gen given Schema[Any, MyClass] = Schema.gen ``` -------------------------------- ### Customizing Federation V2 with ComposeDirective Source: https://ghostdogpr.github.io/caliban/docs/federation.html Extend `FederationV2` to customize federation directives, such as adding custom directives like `@myDirective` and `@anotherDirective` using `ComposeDirective` and `Link`. ```scala package object myFederation extends FederationV2( Versions.v2_3 :: Link("https://myspecs.dev/myDirective/v1.0", List( Import("@myDirective"), Import("@anotherDirective", as = Some("@hello")) )) :: ComposeDirective("@myDirective") :: ComposeDirective("@hello") :: Nil ) ) with FederationDirectivesV2_3 // Then import your new federation object instead of `caliban.federation.v2_3` import myFederation._ ``` -------------------------------- ### Enable Caliban Plugin and Configure Settings Source: https://ghostdogpr.github.io/caliban/docs/server-codegen.html Enable the Caliban plugin in your `build.sbt` and configure code generation settings, specifying the schema file and generation type. ```scala import _root_.caliban.codegen.Codegen lazy val myproject = project // enable caliban codegen plugin .enablePlugins(CalibanPlugin) .settings( // add code generation settings Compile / caliban / calibanSettings ++= Seq( calibanSetting(file("myproject/src/main/graphql/myapi.graphql"))( // important to set this, otherwise you'll get client code _.genType(Codegen.GenType.Schema) // you can customize the codegen further with this DSL .clientName("NameOfApi.scala") .packageName("myproject.mypackage") ), ) ) ``` -------------------------------- ### Configure Caliban Settings for a Schema File Source: https://ghostdogpr.github.io/caliban/docs/client-codegen.html Use `calibanSettings` and `calibanSetting` to configure code generation for a specific schema file, including package name, scalar mappings, and imports. ```sbt Compile / caliban / calibanSettings += calibanSetting(file("Service.graphql"))( cs => cs.packageName("com.example.graphql.client") .scalarMapping( "LanguageCode" -> "com.example.models.LanguageCode", ) .scalarMapping( "Timestamp" -> "java.sql.Timestamp", "DayOfWeek" -> "java.time.DayOfWeek", "IntRange" -> "com.github.tminglei.slickpg.Range[Int]" ) .imports("com.example.graphql.client.implicits._") ) ``` -------------------------------- ### Add Laminext dependency Source: https://ghostdogpr.github.io/caliban/docs/laminext.html Include the caliban-client-laminext module in your project configuration. ```sbt "com.github.ghostdogpr" %%% "caliban-client-laminext" % "3.0.0" ``` -------------------------------- ### Define field arguments Source: https://ghostdogpr.github.io/caliban/docs/schema.html Define arguments by creating a case class and using it as a function input in your query definition. ```scala case class Character(name: String, origin: Origin) case class FilterArgs(origin: Option[Origin]) case class Queries(characters: FilterArgs => List[Character]) ``` ```graphql type Queries { characters(origin: Origin): [Character!]! } ``` -------------------------------- ### Configure and Register Schema Reporting Source: https://ghostdogpr.github.io/caliban/docs/schema-reporting.html Define a GraphQL API, configure the SchemaReporter, and register graphs with the ReportingDaemon to enable automatic schema publishing. ```scala // Define your GraphQL schema normally val api: GraphQL[Any] = graphQL(RootResolver(Queries(characters = List(Character("Amos"))))) // Define a SchemaReporter that will communicate with Apollo val reporterL = SchemaReporter.fromDefaultConfig // Loads the access token from an environment variable called "APOLLO_KEY" // Or load it from a configuration type // val reporterL = ZLayer.service[ApolloConfig] >>> SchemaReporter.fromConfig[ApolloConfig](_.key) // Define your graph references val daemon: ZIO[Scope with ReportingDaemon, Unit] = for { graph1 <- SchemaReportingRef.make(api, "my-graph@production") // For dynamic or possibly updating schemas you can provide a Ref and a transform function that will allow // you to push schema updates that occur at runtime. ref <- Ref.make[String]("schema { query: Query }\n type Query { hello: String! }") graph2 <- SchemaReportingRef.fromRef(ref, "dynamic-graph@production")(identity) _ <- ReportingDaemon.register(graph1) _ <- ReportingDaemon.register(graph2) } yield () // Now wire it up ZIO.scoped(daemon).provide(reporterL, AsyncHttpClientZioBackend.layer(), ReportingDaemon.live) ``` -------------------------------- ### Specify Explicit Caliban Schema Files Source: https://ghostdogpr.github.io/caliban/docs/client-codegen.html Use `caliban / sources` to explicitly list the schema files for code generation, overriding the default directory. ```sbt Compile / caliban / sources := List(file("caliban") / "Service.graphql") ``` -------------------------------- ### Schema Derivation with `derives` (Scala 3) Source: https://ghostdogpr.github.io/caliban/docs/schema.html When using Scala 3's `derives` syntax, create an object extending `SchemaDerivation[R]` and use its `SemiAuto` method for schema generation. This is useful for types requiring a specific ZIO environment. ```scala object customSchema extends SchemaDerivation[MyEnv] case class Queries(test: RIO[MyEnv, List[Int]]) derives customSchema.SemiAuto ``` -------------------------------- ### Custom Schema for ZIO Environment (Scala 2) Source: https://ghostdogpr.github.io/caliban/docs/schema.html For Scala 2 when requiring a ZIO environment (R != Any), extend `GenericSchema[R]` to create a custom schema object. Use `gen` or `auto` from this object to generate schemas for types that depend on the custom environment. ```scala import caliban._ import caliban.schema._ type MyEnv = Console object customSchema extends GenericSchema[MyEnv] import customSchema.auto._ // if you use semi-auto generation, use this instead: // implicit val characterSchema: Schema[MyEnv, Character] = customSchema.gen // implicit val queriesSchema: Schema[MyEnv, Queries] = customSchema.gen val queries = Queries(ZIO.attempt(???), _ => ZIO.succeed(???)) val api = graphQL(RootResolver(queries)) ``` -------------------------------- ### Monix Task Interop with Caliban Source: https://ghostdogpr.github.io/caliban/docs/interop.html Integrate Caliban with Monix Task, enabling asynchronous execution using Monix's Task type. Requires importing caliban.interop.monix.implicits._ and an implicit zio.Runtime. ```scala import caliban._ import caliban.interop.monix.implicits._ import cats.effect.ExitCode import monix.eval.{ Task, TaskApp } import monix.execution.Scheduler import zio.Runtime object ExampleMonixInterop extends TaskApp { implicit val zioRuntime = Runtime.default implicit val monixScheduler: Scheduler = scheduler case class Queries(numbers: List[Int], randomNumber: Task[Int]) val queries = Queries(List(1, 2, 3, 4), Task.eval(scala.util.Random.nextInt())) val api = graphQL(RootResolver(queries)) val query = """ { numbers randomNumber } """ override def run(args: List[String]): Task[ExitCode] = for { interpreter <- api.interpreterAsync result <- interpreter.executeAsync(query) _ <- Task.eval(println(result.data)) } yield ExitCode.Success } ``` -------------------------------- ### Implement endpoint logic Source: https://ghostdogpr.github.io/caliban/docs/interop.html Define the business logic function to be executed by the endpoint. ```scala import zio.UIO def bookAddLogic(book: Book, token: String): UIO[Unit] = ??? ``` -------------------------------- ### SchemaComparison.compare Source: https://ghostdogpr.github.io/caliban/docs/schema-comparison.html Compares two GraphQL schemas using SchemaLoader instances and returns a list of SchemaComparisonChange objects. ```APIDOC ## SchemaComparison.compare ### Description Compares two schemas from different origins to identify differences, including breaking changes. ### Method Scala Function Call ### Parameters - **loader1** (SchemaLoader) - Required - The first schema source. - **loader2** (SchemaLoader) - Required - The second schema source. ### SchemaLoader Constructors - **fromCaliban**: Accepts a Caliban GraphQL object. - **fromFile**: Accepts a file path to a GraphQL IDL file. - **fromString**: Accepts a string containing GraphQL IDL. - **fromIntrospection**: Accepts a URL of a GraphQL server supporting introspection. ### Response - **Task[List[SchemaComparisonChange]]** - A ZIO Task containing a list of changes. ### SchemaComparisonChange - **breaking** (Boolean) - Indicates if the change is breaking (e.g., removing a field or type). - **toString** (String) - Returns a description of the change. ``` -------------------------------- ### Fetch data with EventStream Source: https://ghostdogpr.github.io/caliban/docs/laminext.html Use the toEventStream extension method on a SelectionBuilder to create an EventStream for data fetching. ```scala val characters: Var[List[String]] = Var(Nil) val uri = "http://localhost:8088/api/graphql" val getCharacters = Queries.characters(None)(Client.Character.name).toEventStream(uri) val view: Div = div( "Characters: ", getCharacters.collectRight --> characters.set _, child <-- characters.signal.map(c => div(c.mkString(", "))) ) ``` -------------------------------- ### Enable Caliban Plugin in build.sbt Source: https://ghostdogpr.github.io/caliban/docs/client.html Enable the CalibanPlugin in your build.sbt file to use its sbt commands. ```scala enablePlugins(CalibanPlugin) ``` -------------------------------- ### Implement Relay Connection in Caliban Source: https://ghostdogpr.github.io/caliban/docs/relay-connections.html Boilerplate for defining connection types, edges, and resolver logic using Caliban's relay abstractions. ```scala import caliban._ import caliban.schema.Schema.auto._ import caliban.schema.ArgBuilder.auto._ import caliban.relay._ import zio._ // The entity you want to paginate over case class Item(name: String) // The specific edge type for your connection. // The default cursor implementation is a base64 encoded index offset, // but you can easily implement your own cursor to support e.g // cursor based pagination from your database. case class ItemEdge(cursor: Base64Cursor, node: Item) extends Edge[Base64Cursor, Item] object ItemEdge { def apply(x: Item, i: Int): ItemEdge = ItemEdge(Base64Cursor(i), x) } // The top level connection itself case class ItemConnection( pageInfo: PageInfo, edges: List[ItemEdge] ) extends Connection[ItemEdge] object ItemConnection { val fromList = Connection.fromList(ItemConnection.apply)(ItemEdge.apply)(_, _) } // The arguments for your resolver. // These are the minimal set of fields needed, // but you can easily customize it to add e.g // sorting or filtering. case class Args( first: Option[Int], last: Option[Int], before: Option[String], after: Option[String] ) extends PaginationArgs[Base64Cursor] case class Query(connection: Args => ZIO[Any, CalibanError, ItemConnection]) val api = graphQL( RootResolver( Query(args => for { pageInfo <- Pagination(args) items = ItemConnection.fromList(List(Item("1"), Item("2"), Item("3")), pageInfo) } yield items ) ) ) ``` -------------------------------- ### Enable CompileTimeCalibanClientPlugin in sbt Source: https://ghostdogpr.github.io/caliban/docs/client-codegen.html Initial activation of the plugin within an sbt project definition. ```scala lazy val client = project .enablePlugins(CompileTimeCalibanClientPlugin) ``` -------------------------------- ### Add caliban-tracing to build.sbt Source: https://ghostdogpr.github.io/caliban/docs/middleware.html Include the caliban-tracing package in your project's dependencies by adding this line to your built.sbt file. ```scala "com.github.ghostdogpr" %% "caliban-tracing" % "3.0.0" ``` -------------------------------- ### Reuse Existing Selections Source: https://ghostdogpr.github.io/caliban/docs/client.html Existing selections can be reused directly, simplifying query construction and promoting code reuse. ```scala val query: SelectionBuilder[RootQuery, List[CharacterView]] = Query.characters { character } ``` -------------------------------- ### Derive all methods as fields Source: https://ghostdogpr.github.io/caliban/docs/schema.html Use @GQLFieldsFromMethods to automatically derive all public methods in a class as GraphQL fields. ```scala import caliban.schema.Annotations.GQLFieldsFromMethods @GQLFieldsFromMethods case class Person( fullName: String ) derives Schema.SemiAuto { private val split = fullName.split(" ") def firstName: String = split.head def lastName: String = split.last } ``` -------------------------------- ### Add Caliban dependencies to build.sbt Source: https://ghostdogpr.github.io/caliban/docs Include these dependencies in your build configuration to enable support for specific web servers, effect libraries, and GraphQL features. ```scala "com.github.ghostdogpr" %% "caliban-http4s" % "3.0.0" // routes for http4s "com.github.ghostdogpr" %% "caliban-akka-http" % "3.0.0" // routes for akka-http "com.github.ghostdogpr" %% "caliban-pekko-http" % "3.0.0" // routes for pekko-http "com.github.ghostdogpr" %% "caliban-play" % "3.0.0" // routes for play "com.github.ghostdogpr" %% "caliban-cats" % "3.0.0" // interop with cats-effect "com.github.ghostdogpr" %% "caliban-monix" % "3.0.0" // interop with monix "com.github.ghostdogpr" %% "caliban-tapir" % "3.0.0" // interop with tapir "com.github.ghostdogpr" %% "caliban-federation" % "3.0.0" // apollo federation "com.github.ghostdogpr" %% "caliban-reporting" % "3.0.0" // apollo schema reporting "com.github.ghostdogpr" %% "caliban-tracing" % "3.0.0" // open-telemetry "com.github.ghostdogpr" %% "caliban-stitching" % "3.0.0" // stitching ``` -------------------------------- ### Provide Arguments for Fields Source: https://ghostdogpr.github.io/caliban/docs/client.html When a field requires arguments, the corresponding helper method will also require them. Pass arguments directly when calling the method. ```scala val query: SelectionBuilder[RootQuery, List[CharacterView]] = Query.characters(Origin.MARS) { character } ``` -------------------------------- ### Customize Caliban Schema Source Directory Source: https://ghostdogpr.github.io/caliban/docs/client-codegen.html Override the `calibanSources` setting to change the directory where Caliban looks for schema files. ```sbt Compile / caliban / calibanSources := file("caliban") ``` -------------------------------- ### Convert endpoint to GraphQL Source: https://ghostdogpr.github.io/caliban/docs/interop.html Use the toGraphQL extension method to transform a Tapir endpoint into a Caliban GraphQL API. ```scala import caliban._ import caliban.interop.tapir._ // summons 'toGraphQL' extension import caliban.schema.ArgBuilder.auto._ import caliban.schema.Schema.auto._ val api: GraphQL[Any] = addBook.toGraphQL((bookAddLogic _).tupled) ``` -------------------------------- ### Add Caliban Codegen SBT Plugin Source: https://ghostdogpr.github.io/caliban/docs/server-codegen.html Add this dependency to your `project/plugins.sbt` file to enable the Caliban code generation plugin. ```sbt addSbtPlugin("com.github.ghostdogpr" % "caliban-codegen-sbt" % "3.0.0") ``` -------------------------------- ### Enable custom schema derivation configuration Source: https://ghostdogpr.github.io/caliban/docs/schema.html Define a custom schema derivation object to override default configuration settings like semantic non-nullability. ```scala import caliban.schema.SchemaDerivation object MySchemaDerivation extends SchemaDerivation[Any] { override def config = DerivationConfig( // add your config overrides here enableSemanticNonNull = true ) } case class MyClass(field: String) // use the custom schema derivation defined above implicit val schemaForMyClass: Schema[Any, MyClass] = MySchemaDerivation.gen ``` ```scala import caliban.schema.SchemaDerivation object MySchemaDerivation extends SchemaDerivation[Any] { override def config = DerivationConfig( // add your config overrides here enableSemanticNonNull = true ) } case class MyClass(field: String) // use the custom schema derivation defined above given Schema[Any, MyClass] = MySchemaDerivation.gen ``` ```scala import caliban.schema.{ CommonSchemaDerivation, Schema } trait MySchemaDerivation[R] extends CommonSchemaDerivation { override def config = DerivationConfig( // add your config overrides here enableSemanticNonNull = true ) final class SemiAuto[A](impl: Schema[R, A]) extends Schema[R, A] { export impl.* } object SemiAuto { inline def derived[A]: SemiAuto[A] = new SemiAuto[A](MySchemaDerivation.derived[R, A]) } } object MySchemaDerivation extends MySchemaDerivation[Any] case class MyClass(field: String) derives MySchemaDerivation.SemiAuto ``` -------------------------------- ### Implement Semi-auto Derivation Source: https://ghostdogpr.github.io/caliban/docs/schema.html Manually define Schema instances for specific types to avoid the overhead and limitations of full auto derivation. ```scala import caliban.schema.Schema case class MyClass(field: String) implicit val schemaForMyClass: Schema[Any, MyClass] = Schema.gen ``` ```scala import caliban.schema.Schema case class MyClass(field: String) case class MyClass(field: String) derives Schema.SemiAuto // if you don't want to use the `derives` syntax, you can also use the following: given Schema[Any, MyClass] = Schema.gen ``` -------------------------------- ### Add caliban-tools dependency Source: https://ghostdogpr.github.io/caliban/docs/tools.html Include this dependency to access GraphQL introspection and schema comparison features. ```sbt "com.github.ghostdogpr" %% "caliban-tools" % "3.0.0" ``` -------------------------------- ### Add Caliban Client Dependency (ScalaJS) Source: https://ghostdogpr.github.io/caliban/docs/client.html Use this dependency for ScalaJS projects. ```scala "com.github.ghostdogpr" %%% "caliban-client" % "3.0.0" ``` -------------------------------- ### Add Caliban Federation Dependency Source: https://ghostdogpr.github.io/caliban/docs/federation.html Include this dependency in your build.sbt file to use the federation module. ```scala "com.github.ghostdogpr" %% "caliban-federation" % "3.0.0" ``` -------------------------------- ### Execute GraphQL Request with sttp Source: https://ghostdogpr.github.io/caliban/docs/client.html Transform a SelectionBuilder into an sttp request using .toRequest. This function takes the server URL and options like useVariables and queryName. ```scala import sttp.client4._ import sttp.client4.httpclient.zio.HttpClientZioBackend HttpClientZioBackend.layer().flatMap { backend => val serverUrl = uri"http://localhost:8088/api/graphql" val result: Task[List[CharacterView]] = query.toRequest(serverUrl).send(backend).map(_.body).absolve ... } ``` -------------------------------- ### Load and Parse GitHub Introspection Schema Source: https://ghostdogpr.github.io/caliban/docs/stitching.html Loads the introspection schema from GitHub's GraphQL API and parses it into a Caliban `__Schema` object. This is the first step in integrating an external API. ```scala val GITHUB_API = "https://api.github.com/graphql" val api = for { sttpClient <- ZIO.environment[SttpClient] // 1 schemaLoader = SchemaLoader.fromIntrospection(GITHUB_API, None) schema <- schemaLoader.load // 2 remoteSchema <- ZIO.fromOption(RemoteSchema.parseRemoteSchema(schema)) remoteSchemaResolvers = RemoteSchemaResolver.fromSchema(remoteSchema) } yield { // 3 implicit val githubProfileSchema: Schema[Any, Repository] = remoteSchemaResolvers .remoteResolver("Repository")( // 4 // Here we need to translate our local `Repository` case class into // a top-level query which can be resolved remotely by calling GitHub's API. RemoteResolver.fromFunction((r: ResolveRequest[Repository]) => r.field.copy( name = "repository", arguments = Map( "owner" -> Value.StringValue(r.args.owner), "name" -> Value.StringValue(r.args.name) ) ) ) >>> RemoteResolver.fromUrl(GITHUB_API) ) .provide(sttpClient) } ``` -------------------------------- ### Add Caliban Client Dependency (Scala) Source: https://ghostdogpr.github.io/caliban/docs/client.html Include this dependency in your build.sbt for standard Scala projects. ```scala "com.github.ghostdogpr" %% "caliban-client" % "3.0.0" ```