Implementing Type‑Safe Request‑Response with Akka Typed Actors and the Ask Pattern
Learn how to define a type‑safe protocol, implement a Typed Actor, and use the Ask pattern for request‑response with Akka in Scala, including verification and recovery techniques.
22 Jun 2026, 03:28 UTC

Desired outcome
Enable compile‑time‑checked request‑response messaging between actors so that each request type is paired with a specific response type, eliminating mismatched messages and reducing boilerplate.
Prerequisites
- Scala 2.13 or 2.12
- Akka 2.6.x (includes the typed module)
- Build tool: sbt or Maven
- Basic familiarity with Scala and the actor model
Procedure
- Define the protocol – create a sealed trait that enumerates all request messages and their corresponding response types.
// src/main/scala/example/Protocol.scala package example import akka.actor.typed.scaladsl.ActorContext import akka.actor.typed.{ActorRef, Behavior} sealed trait Command final case class GetUser(id: String, replyTo: ActorRef[UserResponse]) extends Command final case class UserResponse(id: String, name: String) - Implement the actor behavior – use Behaviors.receiveMessage to pattern‑match each request and reply via context.reply.
// src/main/scala/example/UserActor.scala package example import akka.actor.typed.scaladsl.Behaviors import akka.actor.typed.{ActorRef, Behavior} object UserActor { def apply(): Behavior[Command] = Behaviors.receiveMessage { case GetUser(id, replyTo) => // Simulate a lookup; replace with real service call val user = UserResponse(id, s\"User-$id\") replyTo ! user Behaviors.same } } - Spawn the actor – obtain an ActorRef[Command] from the typed ActorSystem.
// src/main/scala/example/Main.scala package example import akka.actor.typed.ActorSystem import akka.actor.typed.scaladsl.Behaviors import scala.concurrent.duration._ import scala.concurrent.{Await, ExecutionContext, Future} import akka.util.Timeout object Main extends App { val system: ActorSystem[Nothing] = ActorSystem(Behaviors.empty, \"TypedDemo\") implicit val ec: ExecutionContext = system.executionContext val userActor: ActorRef[Command] = system.spawn(UserActor(), \"user-actor\") // Ask pattern – returns a Future[UserResponse] implicit val timeout: Timeout = 3.seconds val future: Future[UserResponse] = userActor ? (GetUser(\"42\", _)) future.map { resp => println(s\"Received: $resp\") system.terminate() } } - Handle the response – the ? operator (ask) yields a Future[Response] that you can compose with standard Scala Futures or pipe to another actor.
Expected checks
- Compile‑time: the compiler verifies that every GetUser message includes a replyTo: ActorRef[UserResponse]; sending a request with a mismatched reply type fails to compile.
- Runtime: log incoming requests (e.g., add a println or logging framework inside the message handler) to confirm the actor is processing messages.
- Monitor the Future returned by the ask: ensure it completes with a UserResponse or fails with a timeout.
Recovery options
- Supervise the actor with a restart strategy (e.g., Behaviors.supervise wrapping the behavior) to recover from unexpected exceptions.
- Use askWithStatus or pipeToSelf to transform ask failures into internal messages that the actor can handle gracefully.
- Tune the ask timeout and the underlying dispatcher’s mailbox size if you observe high request rates causing mailbox overflow.
Limitations and practical verification
The Ask pattern creates a temporary internal actor for each request. Under very high throughput this can increase garbage collection pressure and mailbox usage. To verify that overhead stays acceptable:
- Run a sustained load test (e.g., using Gatling or a simple loop that sends thousands of asks) while monitoring JVM metrics such as GC pause time and mailbox size via Akka’s monitoring extensions.
- If mailbox growth exceeds a threshold (e.g., > 1000 messages), consider batching requests or switching to a tell‑based pattern with explicit correlation IDs.
No state‑changing rollback is required for the steps above; stopping the actor system (system.terminate()) cleans up resources.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.