Akka HTTP with OverLayer
#
Akka HTTP SetupThis part will be focusing on setting up an Akka HTTP Server. The code will be somewhat identical to the Sangria's official docs.
#
Akka HTTP no OverLayer setupWe will start with a simple Akka HTTP setup not taking account OverLayer at the moment.
Main.scala
import akka.http.scaladsl.Httpimport akka.http.scaladsl.server.Directives._import akka.http.scaladsl.server.Routeimport akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupportimport sangria.marshalling.sprayJson._import spray.json.JsValueimport /* path to file */.Schemaimport /* path to file */.Contextimport /* path to file */.Implicits._
object Main extends SprayJsonSupport {
implicit val executionContext = system.executionContext
// Simple Routing DSL val route: Route = { (post & path("graphql") & entity(as[JsValue])) { req => handle(req, Schema.t ???) } ~ path(Remaining) { _ => getFromResource("assets/playground.html") } }
// Run the server def main(args: Array[String]): Unit = { Http() .newServerAt("localhost", 4000) .bind(route) }
def handle(js: JsValue, schema: Schema[Context, Unit], ctx: Ctx): Route = ???}
#
Adding the SingletonsLet's add the missing singletons used for the Context.
Main.scala
import ...import /* path to file */.Channelimport /* path to file */.MessageStore
object Main extends SprayJsonSupport { // ...
val channel = new Channel()
val store = new MessageStore()
val route: Route = { (post & path("graphql") & entity(as[JsValue])) { req => handle(req, schema ???) } ~ path(Remaining) { _ => getFromResource("assets/playground.html") } }
// ...
}
#
Akka Websocket SetupNow let's setup the proper subscription through websocket.
#
Using the required ActorSystem.OverLayer required a specific behavior for the main actor to allow spawning its own actors. This can be done easily by using OverTransportLayer
's
.makeSystem
or .behavior
.
Implicits.scala (using makeSystem)
Implicits.scala
import akka.actor.typed.{ActorSystem, SpawnProtocol}import akka.stream.Materializerimport akka.actor.typed.scaladsl.Behaviorsimport io.github.dexclaimation.overlayer.OverTransportLayer
object Implicits { implicit val actorSystem: ActorSystem[SpawnProtocol.Command] = OverTransportLayer.makeSystem("MainActorSystem")
implicit val materializer: Materializer = Materializer.createMaterializer(system)}
#
Adding OverLayerMain.scala
import ...import io.github.dexclaimation.overlayer.OverTransportLayer
object Main extends SprayJsonSupport { // ...
val transport = OverTransportLayer(Schema.t, ()) // <- default to use `subscriptions-transport-ws`
val route: Route = { (post & path("graphql") & entity(as[JsValue])) { req => handle(req, schema, Context(store, channel)) } ~ path("graphql" / "websocket") { transport.applyMiddleware(Context(store, channel)) } ~ path(Remaining) { _ => getFromResource("assets/playground.html") } }
// ...}
That's it. That few lines added was all that it takes to add in OverLayer into an existing Akka HTTP GraphQL Server.
#
Example requestYou can immediately try this out on a GraphQL Browser IDE like graphql-playground
and Apollo Sandbox.
Here is an example:
- subscription
- mutation
subscription { roomLobby { id content authorName }}
{ "data": { "roomLobby": { "id": "c9e6e890-9850-4dee-a7b3-685423272c86", "content": "Hello Friends!!", "authorName": "Me" } }}
Still Listening...
Websocket Messages
- {"type": "connection_init", "payload": {}}- {"type": "connection_ack"}- {"type": "ka"}- {"type": "start", "id": "1", "payload": {"query": "subscription { roomLobby { id content authorName } }" }- {"type": "data", "id": "1", "payload": {"data": { "roomLobby": { "id": "c9e6e890-9850-4dee-a7b3-685423272c86", "content": "Hello Friends!!", "authorName": "Me" } } }
mutation { send(msg: "Hello Friends!!", by: "Me") { id }}
{ "data": { "send": { "id": "c9e6e890-9850-4dee-a7b3-685423272c86" } }}