Basic Directives
Basic directives are foundational low-level building blocks for constructing custom directives. They provide mechanisms for extracting values from requests, transforming requests and responses, and manipulating route results and rejections.
Extraction
extract
Extracts a single value from the RequestContext using the given function.
val extractHost: Directive1[String] = extract(_.request.uri.host.map(_.value).getOrElse("unknown"))
val route: Route = extractHost { host =>
complete(s"Host: $host")
}
extractRequest
Extracts the current Request[IO] instance.
val route: Route = extractRequest { request =>
complete(s"Method: ${request.method}, URI: ${request.uri}")
}
extractRequestContext
Extracts the full RequestContext.
val route: Route = extractRequestContext { ctx =>
complete(s"Unmatched path: ${ctx.unmatchedPath}")
}
extractUri
Extracts the complete request URI.
val route: Route = extractUri { uri =>
complete(s"Full URI: $uri")
}
extractRequestEntity
Extracts the request entity body as an EntityBody[IO] (alias for Stream[IO, Byte]).
val route: Route = extractRequestEntity { body =>
complete(Status.Ok -> body)
}
extractDataBytes
Extracts the request body as a Stream[IO, Byte]. Functionally equivalent to extractRequestEntity.
val route: Route = extractDataBytes { bytes =>
val collected = bytes.through(fs2.text.utf8.decode).compile.string
onSuccess(collected) { text =>
complete(s"Received: $text")
}
}
extractUnmatchedPath
Extracts the yet-unmatched portion of the request path.
val route: Route = pathPrefix("api") {
extractUnmatchedPath { remaining =>
complete(s"Remaining path: $remaining")
}
}
extractMatchedPath
Extracts the already-matched portion of the request path.
val route: Route = pathPrefix("api" / "v1") {
extractMatchedPath { matched =>
complete(s"Matched: $matched")
}
}
Providing Values
provide
Injects a single value into a directive, making it available to the inner route.
val route: Route = provide("injected-value") { value =>
complete(value)
}
tprovide
Injects a tuple of values into a directive.
val route: Route = tprovide(("hello", 42)) { (greeting, number) =>
complete(s"$greeting $number")
}
textract
Extracts a tuple of values from the RequestContext.
val extractMethodAndPath = textract(ctx => (ctx.request.method, ctx.request.uri.path))
pass
A no-op directive that always passes the request to its inner route unchanged. Useful as an identity element in directive composition.
val maybeAuth: Directive0 = if (authEnabled) authenticateBasic("realm", auth) else pass
val route: Route = maybeAuth {
complete("OK")
}
Request Transformation
mapRequest
Transforms the incoming Request[IO] before passing it to the inner route.
import org.http4s.Header
import org.typelevel.ci.CIString
val route: Route = mapRequest(_.putHeaders(Header.Raw(CIString("X-Modified"), "true"))) {
extractRequest { req =>
complete(s"Header present: ${req.headers.get(CIString("X-Modified")).isDefined}")
}
}
mapRequestContext
Transforms the RequestContext before passing it to the inner route.
val route: Route = mapRequestContext(ctx => ctx.copy(unmatchedPath = ctx.unmatchedPath)) {
complete("OK")
}
Response Transformation
mapResponse
Transforms the Response[IO] produced by the inner route.
import org.http4s.Header
import org.typelevel.ci.CIString
val route: Route = mapResponse(_.putHeaders(Header.Raw(CIString("X-Served-By"), "stir"))) {
complete("Hello")
}
mapResponseEntity
Transforms the response entity body (EntityBody[IO]) produced by the inner route.
val route: Route = mapResponseEntity(_.through(fs2.text.utf8.decode).through(fs2.text.utf8.encode)) {
complete("Re-encoded content")
}
mapResponseHeaders
Transforms the response Headers produced by the inner route.
import org.typelevel.ci._
val route: Route = mapResponseHeaders(_.removeHeader(ci"X-Internal")) {
complete("Cleaned headers")
}
Route Transformation
mapInnerRoute
Transforms the inner route itself, allowing you to wrap or modify route behavior.
val route: Route = mapInnerRoute { inner =>
ctx => inner(ctx) // pass through unchanged
} {
complete("OK")
}
Route Result Transformation
These directives operate on RouteResult values produced by inner routes. They are primarily used when building custom directives.
mapRouteResult(f: RouteResult => RouteResult)-- transforms the route result synchronously.mapRouteResultIO(f: IO[RouteResult] => IO[RouteResult])-- transforms the entireIO[RouteResult]effect, useful for error handling.mapRouteResultWith(f: RouteResult => IO[RouteResult])-- transforms the route result with an effectful function.mapRouteResultPF(f: PartialFunction[RouteResult, RouteResult])-- transforms matching route results; non-matching results pass through unchanged.mapRouteResultWithPF(f: PartialFunction[RouteResult, IO[RouteResult]])-- effectful variant ofmapRouteResultPF.
Path Manipulation
mapUnmatchedPath
Transforms the unmatched path of the RequestContext before passing it to the inner route.
val route: Route = mapUnmatchedPath(path => Uri.Path.unsafeFromString(path.toString.toLowerCase)) {
path("api") {
complete("Case-insensitive match")
}
}
Rejection Manipulation
mapRejections
Transforms the list of rejections produced by the inner route.
val route: Route = mapRejections(_.filter(!_.isInstanceOf[MethodRejection])) {
get {
complete("GET only")
}
}
recoverRejections
Converts rejections into a RouteResult, allowing rejected requests to be recovered.
val route: Route = recoverRejections { rejections =>
RouteResult.Complete(Response[IO](Status.NotFound))
} {
reject
}
recoverRejectionsWith
Effectful variant of recoverRejections that returns IO[RouteResult].
cancelRejection
Cancels a specific rejection from the list of rejections produced by the inner route.
val route: Route = cancelRejection(MethodRejection(Method.POST)) {
get {
complete("OK")
}
}
cancelRejections
Cancels rejections matching the given classes or filter function.
val route: Route = cancelRejections(classOf[MethodRejection]) {
get {
complete("OK")
}
}