Extracts fields from POST requests generated by HTML forms.


def formFields(field: <FieldDef[T]>): Directive1[T]
def formFields(fields: <FieldDef[T_i]>*): Directive[T_0 :: ... T_i ... :: HNil]
def formFields(fields: <FieldDef[T_0]> :: ... <FieldDef[T_i]> ... :: HNil): Directive[T_0 :: ... T_i ... :: HNil]

The signature shown is simplified and written in pseudo-syntax, the real signature uses magnets. [1] The type <FieldDef> doesn’t really exist but consists of the syntactic variants as shown in the description and the examples.

[1]See The Magnet Pattern for an explanation of magnet-based overloading.


Form fields can be either extracted as a String or can be converted to another type. The parameter name can be supplied either as a String or as a Symbol. Form field extraction can be modified to mark a field as required or optional or to filter requests where a form field has a certain value:

extract value of field “color” as String
extract optional value of field “color” as Option[String]
"color" ? "red"
extract optional value of field “color” as String with default value "red"
"color" ! "blue"
require value of field “color” to be "blue" and extract nothing
extract value of field “amount” as Int, you need a matching Deserializer in scope for that to work (see also Unmarshalling)
extract value of field “amount” with an explicit Deserializer

You can use Case Class Extraction to group several extracted values together into a case-class instance.

Requests missing a required field or field value will be rejected with an appropriate rejection.

There’s also a singular version, formField. Query parameters can be handled in a similar way, see parameters. If you want unified handling for both query parameters and form fields, see anyParams.


Data POSTed from HTML forms is either of type application/x-www-form-urlencoded or of type multipart/form-data. The value of an url-encoded field is a String while the value of a multipart/form-data-encoded field is a “body part” containing an entity. This means that different kind of deserializers are needed depending on what the Content-Type of the request is:

  • A application/x-www-form-urlencoded encoded field needs an implicit Deserializer[Option[String], T]
  • A multipart/form-data encoded field needs an implicit Deserializer[Option[BodyPart], T]

For common data-types, these implicits are predefined so that you usually don’t need to care. For custom data-types it should usually suffice to create a Deserializer[String, T] if the value will be encoded as a String. This should be valid for all values generated by HTML forms apart from file uploads.


It should only be necessary to read and understand this paragraph if you have very special needs and need to process arbitrary forms, especially ones not generated by HTML forms.

The formFields directive contains this logic to find and decide how to deserialize a POSTed form field:

  • It tries to find implicits of both types at the definition site if possible or otherwise at least one of both. If none is available compilation will fail with an “implicit not found” error.
  • Depending on the Content-Type of the incoming request it first tries the matching (see above) one if available.
  • If only a Deserializer[Option[String], T] is available when a request of type multipart/form-data is received, this deserializer will be tried to deserialize the body part for a field if the entity is of type text/plain or unspecified.
  • If only a Deserializer[Option[BodyPart], T] is available when a request of type application/x-www-form-urlencoded is received, this deserializer will be tried to deserialize the field value by packing the field value into a body part with an entity of type text/plain. Deserializing will only succeed if the deserializer accepts entities of type text/plain.

If you need to handle encoded fields of a multipart/form-data-encoded request for a custom type, you therefore need to provide a Deserializer[Option[BodyPart], T].


val route =
  formFields('color, '[Int]) { (color, age) =>
    complete(s"The color is '$color' and the age ten years ago was ${age - 10}")

Post("/", FormData(Seq("color" -> "blue", "age" -> "68"))) ~> route ~> check {
  responseAs[String] === "The color is 'blue' and the age ten years ago was 58"

Get("/") ~> sealRoute(route) ~> check {
  status === StatusCodes.BadRequest
  responseAs[String] === "Request is missing required form field 'color'"

For more examples about the way how fields can specified see the examples for the parameters directive.