HORIZON HASKELLDocslts/ghc-9.10.xc74966e2026-09-27Search names, modules, packages, or :: a typeCtrl K

GHC 9.10.3 · lts/ghc-9.10.x · c74966e · 2026-09-27

Modulehttp-api-data-0.6.1Haskell2010

Web.FormUrlEncoded

Convert Haskell values to and from application/xxx-form-urlencoded format.

  • 2 types
  • 4 classes
  • 21 values
  • Packagehttp-api-data-0.6.1
  • Exports27
  • LanguageHaskell2010
  • LicenceBSD-3-Clause
  • SourceFormUrlEncoded.hs

Classes

2 declarations
classclass ToForm a where
#

Convert a value into Form.

An example type and instance:

{-# LANGUAGE OverloadedLists #-}

data Person = Person
  { name :: String
  , age  :: Int }

instance ToForm Person where
  toForm person =
    [ ("name", toQueryParam (name person))
    , ("age", toQueryParam (age person)) ]

Instead of manually writing ToForm instances you can use a default generic implementation of toForm.

To do that, simply add deriving Generic clause to your datatype and declare a ToForm instance for your datatype without giving definition for toForm.

For instance, the previous example can be simplified into this:

data Person = Person
  { name :: String
  , age  :: Int
  } deriving (Generic)

instance ToForm Person

The default implementation of toForm is genericToForm.

Methods

Instances5ToForm
classclass FromForm a where
#

Parse Form into a value.

An example type and instance:

data Person = Person
  { name :: String
  , age  :: Int }

instance FromForm Person where
  fromForm f = Person
    <$> parseUnique "name" f
    <*> parseUnique "age"  f

Instead of manually writing FromForm instances you can use a default generic implementation of fromForm.

To do that, simply add deriving Generic clause to your datatype and declare a FromForm instance for your datatype without giving definition for fromForm.

For instance, the previous example can be simplified into this:

data Person = Person
  { name :: String
  , age  :: Int
  } deriving (Generic)

instance FromForm Person

The default implementation of fromForm is genericFromForm. It only works for records and it will use parseQueryParam for each field's value.

Methods

Instances5FromForm

Keys for Form entries

classclass ToFormKey k where
#

Typeclass for types that can be used as keys in a Form-like container (like Map).

Methods

Instances42ToFormKey, …
classclass FromFormKey k where
#

Typeclass for types that can be parsed from keys of a Form. This is the reverse of ToFormKey.

Methods

Instances42FromFormKey, …

Form type

1 declaration
newtypenewtype Form
#

The contents of a form, not yet URL-encoded.

Form can be URL-encoded with urlEncodeForm and URL-decoded with urlDecodeForm.

Constructors

Instances11IsList, Eq, Read, Show, Generic, Semigroup, …

Encoding and decoding Forms

6 declarations

Encode a Form to an application/x-www-form-urlencoded ByteString.

For an unstable (but faster) encoding see urlEncodeForm.

Key-value pairs get encoded to key=value and separated by &:

Example1 expression
urlEncodeFormStable [("name", "Julian"), ("lastname", "Arni")]"lastname=Arni&name=Julian"

Keys with empty values get encoded to just key (without the = sign):

Example1 expression
urlEncodeFormStable [("is_test", "")]"is_test"

Empty keys are allowed too:

Example1 expression
urlEncodeFormStable [("", "foobar")]"=foobar"

However, if both key and value are empty, the key-value pair is ignored. (This prevents urlDecodeForm . urlEncodeFormStable from being a true isomorphism).

Example1 expression
urlEncodeFormStable [("", "")]""

Everything is escaped with escapeURIString isUnreserved:

Example1 expression
urlEncodeFormStable [("fullname", "Andres Löh")]"fullname=Andres%20L%C3%B6h"

Decode an application/x-www-form-urlencoded ByteString to a Form.

Key-value pairs get decoded normally:

Example1 expression
urlDecodeForm "name=Greg&lastname=Weber"Right (fromList [("lastname","Weber"),("name","Greg")])

Keys with no values get decoded to pairs with empty values.

Example1 expression
urlDecodeForm "is_test"Right (fromList [("is_test","")])

Empty keys are allowed:

Example1 expression
urlDecodeForm "=foobar"Right (fromList [("","foobar")])

The empty string gets decoded into an empty Form:

Example1 expression
urlDecodeForm ""Right (fromList [])

Everything is un-escaped with unEscapeString:

Example1 expression
urlDecodeForm "fullname=Andres%20L%C3%B6h"Right (fromList [("fullname","Andres L\246h")])

Improperly formed strings result in an error:

Example1 expression
urlDecodeForm "this=has=too=many=equals"Left "not a valid pair: this=has=too=many=equals"

Generics

2 declarations
valuegenericToForm :: (Generic a, GToForm a (Rep a)) => FormOptions -> a -> Form
#

A Generic-based implementation of toForm. This is used as a default implementation in ToForm.

Note that this only works for records (i.e. product data types with named fields):

data Person = Person
  { name :: String
  , age  :: Int
  } deriving (Generic)

In this implementation each field's value gets encoded using toQueryParam. Two field types are exceptions:

  • for values of type Maybe a an entry is added to the Form only when it is Just x and the encoded value is toQueryParam x; Nothing values are omitted from the Form;

  • for values of type [a] (except [Char]) an entry is added for every item in the list; if the list is empty no entries are added to the Form;

Here's an example:

data Post = Post
  { title    :: String
  , subtitle :: Maybe String
  , comments :: [String]
  } deriving (Generic, Show)

instance ToForm Post
Example1 expression
urlEncodeAsFormStable Post { title = "Test", subtitle = Nothing, comments = ["Nice post!", "+1"] }"comments=Nice%20post%21&comments=%2B1&title=Test"

A Generic-based implementation of fromForm. This is used as a default implementation in FromForm.

Note that this only works for records (i.e. product data types with named fields):

data Person = Person
  { name :: String
  , age  :: Int
  } deriving (Generic)

In this implementation each field's value gets decoded using parseQueryParam. Two field types are exceptions:

  • for values of type Maybe a an entry is parsed if present in the Form and the is decoded with parseQueryParam; if no entry is present result is Nothing;

  • for values of type [a] (except [Char]) all entries are parsed to produce a list of parsed values;

Here's an example:

data Post = Post
  { title    :: String
  , subtitle :: Maybe String
  , comments :: [String]
  } deriving (Generic, Show)

instance FromForm Post
Example1 expression
urlDecodeAsForm "comments=Nice%20post%21&comments=%2B1&title=Test" :: Either Text PostRight (Post {title = "Test", subtitle = Nothing, comments = ["Nice post!","+1"]})

Encoding options

datadata FormOptions
#

Generic-based deriving options for ToForm and FromForm.

A common use case for non-default FormOptions is to strip a prefix off of field labels:

data Project = Project
  { projectName :: String
  , projectSize :: Int
  } deriving (Generic, Show)

myOptions :: FormOptions
myOptions = FormOptions
 { fieldLabelModifier = map toLower . drop (length "project") }

instance ToForm Project where
  toForm = genericToForm myOptions

instance FromForm Project where
  fromForm = genericFromForm myOptions
Example2 expressions
urlEncodeAsFormStable Project { projectName = "http-api-data", projectSize = 172 }"name=http-api-data&size=172"urlDecodeAsForm "name=http-api-data&size=172" :: Either Text ProjectRight (Project {projectName = "http-api-data", projectSize = 172})

Constructors

Helpers

12 declarations
valuefromEntriesByKey :: (ToFormKey k, ToHttpApiData v) => [(k, [v])] -> Form
#

Convert a list of entries groupped by key into a Form.

Example1 expression
fromEntriesByKey [("name",["Nick"]),("color",["red","blue"])]fromList [("color","red"),("color","blue"),("name","Nick")]
valuelookupAll :: Text -> Form -> [Text]
#

Find all values corresponding to a given key in a Form.

Example3 expressions
lookupAll "name" [][]lookupAll "name" [("name", "Oleg")]["Oleg"]lookupAll "name" [("name", "Oleg"), ("name", "David")]["Oleg","David"]
valuelookupMaybe :: Text -> Form -> Either Text (Maybe Text)
#

Lookup an optional value for a key. Fail if there is more than one value.

Example3 expressions
lookupMaybe "name" []Right NothinglookupMaybe "name" [("name", "Oleg")]Right (Just "Oleg")lookupMaybe "name" [("name", "Oleg"), ("name", "David")]Left "Duplicate key \"name\""
valuelookupUnique :: Text -> Form -> Either Text Text
#

Lookup a unique value for a key. Fail if there is zero or more than one value.

Example3 expressions
lookupUnique "name" []Left "Could not find key \"name\""lookupUnique "name" [("name", "Oleg")]Right "Oleg"lookupUnique "name" [("name", "Oleg"), ("name", "David")]Left "Duplicate key \"name\""
valueparseAll :: FromHttpApiData v => Text -> Form -> Either Text [v]
#

Lookup all values for a given key in a Form and parse them with parseQueryParams.

Example4 expressions
parseAll "age" [] :: Either Text [Word8]Right []parseAll "age" [("age", "8"), ("age", "seven")] :: Either Text [Word8]Left "could not parse: `seven' (input does not start with a digit)"parseAll "age" [("age", "8"), ("age", "777")] :: Either Text [Word8]Left "out of bounds: `777' (should be between 0 and 255)"parseAll "age" [("age", "12"), ("age", "25")] :: Either Text [Word8]Right [12,25]
valueparseMaybe :: FromHttpApiData v => Text -> Form -> Either Text (Maybe v)
#

Lookup an optional value for a given key and parse it with parseQueryParam. Fail if there is more than one value for the key.

Example5 expressions
parseMaybe "age" [] :: Either Text (Maybe Word8)Right NothingparseMaybe "age" [("age", "12"), ("age", "25")] :: Either Text (Maybe Word8)Left "Duplicate key \"age\""parseMaybe "age" [("age", "seven")] :: Either Text (Maybe Word8)Left "could not parse: `seven' (input does not start with a digit)"parseMaybe "age" [("age", "777")] :: Either Text (Maybe Word8)Left "out of bounds: `777' (should be between 0 and 255)"parseMaybe "age" [("age", "7")] :: Either Text (Maybe Word8)Right (Just 7)
valueparseUnique :: FromHttpApiData v => Text -> Form -> Either Text v
#

Lookup a unique value for a given key and parse it with parseQueryParam. Fail if there is zero or more than one value for the key.

Example5 expressions
parseUnique "age" [] :: Either Text Word8Left "Could not find key \"age\""parseUnique "age" [("age", "12"), ("age", "25")] :: Either Text Word8Left "Duplicate key \"age\""parseUnique "age" [("age", "seven")] :: Either Text Word8Left "could not parse: `seven' (input does not start with a digit)"parseUnique "age" [("age", "777")] :: Either Text Word8Left "out of bounds: `777' (should be between 0 and 255)"parseUnique "age" [("age", "7")] :: Either Text Word8Right 7