Integrations
#
On existing schema and serverOverLayer was built to be very easy to integrate to existing akka-http server.
#
Existing serverLet's say, you have a server similar to this.
object Main { implicit val system = ActorSystem(Behaviors.empty, "main-actor-system")
val route: Route = (path("graphql") & post & entity(as[JsValue])) { json => // ... }
def main(args: Array[String]): Unit = { Http() .newServerAt("localhost", 4000) .bind(route) }}
#
Integrating OverLayerSimply plug in the OverTransportLayer, the new system behavior, and the appropriate route.
object Main { implicit val system = OverTransportLayer.makeSystem("main-actor-system")
val transport = OverTransportLayer(...)
val route: Route = (path("graphql") & post & entity(as[JsValue])) { json => // ... } ~ (path("graphql" / "websocket")) { transport.applyMiddleware(...) }
def main(args: Array[String]): Unit = { Http() .newServerAt("localhost", 4000) .bind(route) }}
#
Integrating request specific contextOverLayer was built with request based context and avoid a central context object. With this approach, you are forced to specify a context on the route handler which allow for request specific context. This is useful to gather more information from the request to identity and perform different behavior on subscriptions.
Explicit upgrade to get the on connected headers
val requestHandler: HttpRequest => HttpResponse = { case req @ HttpRequest(GET, Uri.Path("/<path>"), _, _, _) =>
val token = req .getHeader("Authorization") .map(_.value()) .flatMap { case s"Bearer $tokenValue" => Some(tokenValue) case _ => None }
req.getAttribute(AttributeKeys.webSocketUpgrade) match { case Some(upgrade) => upgrade.handleMessages(transport.applyMiddleware(Ctx(token))) case None => HttpResponse(400, entity = "Not a valid websocket request!") } case r: HttpRequest => r.discardEntityBytes() // important to drain incoming HTTP Entity stream HttpResponse(404, entity = "Unknown resource!")}
#
Queries, Mutations, Introspections over WebsocketFrom v0.2.0
, OverLayer will no longer reject Query
and Mutation
and now have capabilities to resolve them. This also include introspection
queries, thus you have your entire operations be handled through websocket on a single open connection.
Only allow operation over websocket
object Main { implicit val system = OverTransportLayer.makeSystem("main-actor-system")
val transport = OverTransportLayer(...)
val route: Route = (path("graphql" / "websocket")) { transport.applyMiddleware(...) }
def main(args: Array[String]): Unit = { Http() .newServerAt("localhost", 4000) .bind(route) }}
#
Integrating with AhqlAhql is a minimal library to perform GraphQL over HTTP Request and Response (Server and Client) on akka-http and spray-json. While Ahql and OverLayer has no specific integration features, Ahql can be great tool used combined with this package.
Handle both HTTP and Websocket request
val route: Route = path("graphql") { Ahql.applyMiddleware(schema, context, ()) } ~ path("graphql" / "websocket") { transport.applyMiddleware(context) }
Combining pathPrefix
and other directives, you can easily make Ahql and OverLayer work nicely together.
More complex example
val gqlServer = Ahql.createServer(schema, (), deferredResolver = deferredResolver)val gqlTransport = OverTransportLayer(schema, (), deferredResolver = deferredResolver)
// CORS for Apollo Sandboxval corsConfig = CorsSettings .defaultSettings .withAllowedOrigins(HttpOriginMatcher(HttpOrigin("https://studio.apollographql.com"))) .withAllowCredentials(true)
val route: Route = cors(corsConfig) { // Route for "__endpoint__/graphql/**" pathPrefix("graphql") { // Get header for a specific value: "Authorization" optionalHeaderValueByName("Authorization") { auth => val context = Ctx(..., auth)
pathEndOrSingleSlash { // Route for "__endpoint__/graphql" gqlServer.applyMiddleware(context) } ~ path("websocket") { // Route for "__endpoint__/graphql/websocket" gqlTransport.applyMiddleware(context) } } } ~ path(Remaining) { _ => // Any other route goes through Apollo Sandbox redirect( uri = s"http://sandbox.apollo.dev/?endpoint=${__endpoint__}", StatusCodes.PermanentRedirect ) }}
Additional redirect and cors for allowing Apollo Sandbox.