diff --git a/src/Trasa/Codec.hs b/src/Trasa/Codec.hs
new file mode 100644
--- /dev/null
+++ b/src/Trasa/Codec.hs
@@ -0,0 +1,74 @@
+{-# LANGUAGE OverloadedStrings #-}
+module Trasa.Codec
+  (
+  -- * Capture Codecs
+    CaptureEncoding(..)
+  , CaptureDecoding(..)
+  , CaptureCodec(..)
+  , captureCodecToCaptureEncoding
+  , captureCodecToCaptureDecoding
+  -- * Body Codecs
+  , BodyEncoding(..)
+  , BodyDecoding(..)
+  , BodyCodec(..)
+  , bodyCodecToBodyEncoding
+  , bodyCodecToBodyDecoding
+  -- * Type Class Based Codecs
+  , showReadCaptureCodec
+  , showReadBodyCodec
+  ) where
+
+import Text.Read (readMaybe,readEither)
+import Data.Bifunctor (first)
+import qualified Data.ByteString.Lazy as LBS
+import qualified Data.ByteString.Lazy.Char8 as LBC
+import qualified Data.Text as T
+import Data.List.NonEmpty (NonEmpty)
+import qualified Network.HTTP.Media.MediaType as N
+
+newtype CaptureEncoding a = CaptureEncoding { appCaptureEncoding :: a -> T.Text }
+
+newtype CaptureDecoding a = CaptureDecoding { appCaptureDecoding :: T.Text -> Maybe a }
+
+data CaptureCodec a = CaptureCodec
+  { captureCodecEncode :: a -> T.Text
+  , captureCodecDecode :: T.Text -> Maybe a
+  }
+
+captureCodecToCaptureEncoding :: CaptureCodec a -> CaptureEncoding a
+captureCodecToCaptureEncoding (CaptureCodec enc _) = CaptureEncoding enc
+
+captureCodecToCaptureDecoding :: CaptureCodec a -> CaptureDecoding a
+captureCodecToCaptureDecoding (CaptureCodec _ dec) = CaptureDecoding dec
+
+showReadCaptureCodec :: (Show a, Read a) => CaptureCodec a
+showReadCaptureCodec = CaptureCodec (T.pack . show) (readMaybe . T.unpack)
+
+data BodyEncoding a = BodyEncoding
+  { bodyEncodingNames :: NonEmpty N.MediaType
+  , bodyEncodingFunction :: a -> LBS.ByteString
+  }
+
+data BodyDecoding a = BodyDecoding
+  { bodyDecodingNames :: NonEmpty N.MediaType
+  , bodyDecodingFunction :: LBS.ByteString -> Either T.Text a
+  }
+
+data BodyCodec a = BodyCodec
+  { bodyCodecNames :: NonEmpty N.MediaType
+  , bodyCodecEncode :: a -> LBS.ByteString
+  , bodyCodecDecode :: LBS.ByteString -> Either T.Text a
+  }
+
+bodyCodecToBodyEncoding :: BodyCodec a -> BodyEncoding a
+bodyCodecToBodyEncoding (BodyCodec names enc _) = BodyEncoding names enc
+
+bodyCodecToBodyDecoding :: BodyCodec a -> BodyDecoding a
+bodyCodecToBodyDecoding (BodyCodec names _ dec) = BodyDecoding names dec
+
+showReadBodyCodec :: (Show a, Read a) => BodyCodec a
+showReadBodyCodec = BodyCodec
+  (pure "text/haskell")
+  (LBC.pack . show)
+  (first T.pack . readEither . LBC.unpack)
+
diff --git a/src/Trasa/Core.hs b/src/Trasa/Core.hs
--- a/src/Trasa/Core.hs
+++ b/src/Trasa/Core.hs
@@ -15,36 +15,31 @@
   (
   -- * Types
     Bodiedness(..)
-  , Path(..)
-  , ResponseBody(..)
-  , RequestBody(..)
-  , Param(..)
-  , Query(..)
-  , Parameter(..)
-  , Rec(..)
-  , BodyCodec(..)
-  , BodyDecoding(..)
-  , BodyEncoding(..)
-  , Many(..)
-  , CaptureCodec(..)
-  , CaptureEncoding(..)
-  , CaptureDecoding(..)
   , Content(..)
-  , QueryString(..)
-  , Url(..)
   , Payload(..)
-  , TrasaErr(..)
   , Router
   -- ** Existential
   , Prepared(..)
   , Concealed(..)
   , Constructed(..)
-  -- * Queries
+  , conceal
+  , mapConstructed
+  -- * Request Types
+  -- ** Method
+  , Method
+  , encodeMethod
+  , decodeMethod
+  -- ** Queries
+  , QueryString(..)
   , encodeQuery
   , decodeQuery
-  -- * Url
+  -- ** Url
+  , Url(..)
   , encodeUrl
   , decodeUrl
+  -- ** Errors
+  , TrasaErr(..)
+  , status
   -- * Using Routes
   , prepareWith
   , dispatchWith
@@ -53,20 +48,20 @@
   , payloadWith
   , requestWith
   , routerWith
-  , handler
   -- * Defining Routes
   -- ** Path
+  , Path(..)
   , match
   , capture
   , end
   , (./)
+  , mapPath
   , appendPath
-  -- ** Request Body
-  , body
-  , bodyless
-  -- ** Response Body
-  , resp
   -- ** Query
+  , Param(..)
+  , Query(..)
+  , Parameter(..)
+  , Rec(..)
   , demoteParameter
   , flag
   , optional
@@ -74,73 +69,109 @@
   , qend
   , (.&)
   , mapQuery
-  -- * Converting Route Metadata
-  , one
-  , mapMany
-  , mapPath
+  -- ** Request Body
+  , RequestBody(..)
+  , body
+  , bodyless
+  , encodeRequestBody
+  , decodeRequestBody
   , mapRequestBody
+  -- ** Response Body
+  , ResponseBody(..)
+  , resp
+  , encodeResponseBody
+  , decodeResponseBody
   , mapResponseBody
-  , mapConstructed
-  -- * Converting Codecs
-  , bodyCodecToBodyEncoding
-  , bodyCodecToBodyDecoding
+  -- ** Many
+  , Many(..)
+  , one
+  , mapMany
+  -- * Codecs
+  , CaptureEncoding(..)
+  , CaptureDecoding(..)
+  , CaptureCodec(..)
+  , BodyEncoding(..)
+  , BodyDecoding(..)
+  , BodyCodec(..)
+    -- ** Converting Codecs
   , captureCodecToCaptureEncoding
   , captureCodecToCaptureDecoding
-  -- * Errors
-  , status
+  , bodyCodecToBodyEncoding
+  , bodyCodecToBodyDecoding
+  -- ** Type Class based Codecs
+  , showReadCaptureCodec
+  , showReadBodyCodec
   -- * Argument Currying
   , ParamBase
   , Arguments
-  -- * Random Stuff
-  , conceal
-  , encodeRequestBody
-  , decodeResponseBody
+  , handler
+  -- * Helpers
   , prettyRouter
-  -- * Show/Read Codecs
-  , showReadBodyCodec
-  , showReadCaptureCodec
   ) where
 
 import Data.Kind (Type)
 import Data.Functor.Identity (Identity(..))
-import Control.Exception (Exception(..))
 import Control.Applicative (liftA2)
 import Data.Maybe (mapMaybe,listToMaybe,isJust)
-import Data.Semigroup (Semigroup(..))
 import Data.List.NonEmpty (NonEmpty)
 import qualified Data.List as L
 import qualified Data.List.NonEmpty as NE
 import Data.Foldable (toList)
-import Data.Bifunctor (first)
-import Text.Read (readEither,readMaybe)
+import Data.Bifunctor (first,bimap)
 
 import qualified Data.ByteString.Lazy as LBS
-import qualified Data.ByteString.Lazy.Char8 as LBC
-import qualified Data.Binary.Builder as LBS
 import qualified Data.Text as T
 import qualified Data.Text.Encoding as T
 import qualified Network.HTTP.Types.Status as N
-import qualified Network.HTTP.Types.URI as N
+import qualified Network.HTTP.Media.MediaType as N
+import qualified Network.HTTP.Media.Accept as N
 import qualified Data.HashMap.Strict as HM
 import Data.HashMap.Strict (HashMap)
 import Data.Vinyl (Rec(..),rmap)
 import Data.Vinyl.TypeLevel (type (++))
 
+import Trasa.Method
+import Trasa.Url
+import Trasa.Error
+import Trasa.Codec
+
 -- $setup
 -- >>> :set -XTypeInType
 
+newtype Many f a = Many { getMany :: NonEmpty (f a) }
+  deriving (Functor)
+
+instance Applicative f => Applicative (Many f) where
+  pure = Many . pure . pure
+  Many mf <*> Many mx = Many $ liftA2 (<*>) mf mx
+
+one :: f a -> Many f a
+one = Many . pure
+
+mapMany :: (forall x. f x -> g x) -> Many f a -> Many g a
+mapMany eta (Many m) = Many (fmap eta m)
+
 data Bodiedness = forall a. Body a | Bodyless
 
 data RequestBody :: (Type -> Type) -> Bodiedness -> Type where
   RequestBodyPresent :: f a -> RequestBody f ('Body a)
   RequestBodyAbsent :: RequestBody f 'Bodyless
 
+body :: rqf req -> RequestBody rqf ('Body req)
+body = RequestBodyPresent
+
+bodyless :: RequestBody rqf 'Bodyless
+bodyless = RequestBodyAbsent
+
 mapRequestBody :: (forall x. rqf x -> rqf' x) -> RequestBody rqf request -> RequestBody rqf' request
 mapRequestBody _ RequestBodyAbsent = RequestBodyAbsent
 mapRequestBody f (RequestBodyPresent reqBod) = RequestBodyPresent (f reqBod)
 
 newtype ResponseBody rpf response = ResponseBody { getResponseBody :: rpf response }
 
+resp :: rpf resp -> ResponseBody rpf resp
+resp = ResponseBody
+
 mapResponseBody :: (forall x. rpf x -> rpf' x) -> ResponseBody rpf request -> ResponseBody rpf' request
 mapResponseBody f (ResponseBody resBod) = ResponseBody (f resBod)
 
@@ -149,6 +180,20 @@
   PathConsCapture :: cap a -> Path cap as -> Path cap (a ': as)
   PathConsMatch :: T.Text -> Path cap as -> Path cap as
 
+infixr 7 ./
+
+(./) :: (a -> b) -> a -> b
+(./) f a = f a
+
+match :: T.Text -> Path cpf caps -> Path cpf caps
+match = PathConsMatch
+
+capture :: cpf cap -> Path cpf caps -> Path cpf (cap ': caps)
+capture = PathConsCapture
+
+end :: Path cpf '[]
+end = PathNil
+
 mapPath :: (forall x. cf x -> cf' x) -> Path cf ps -> Path cf' ps
 mapPath _ PathNil = PathNil
 mapPath f (PathConsMatch s pnext) = PathConsMatch s (mapPath f pnext)
@@ -159,103 +204,20 @@
 appendPath (PathConsMatch a as) bs = PathConsMatch a (appendPath as bs)
 appendPath (PathConsCapture cas as) bs = PathConsCapture cas (appendPath as bs)
 
-newtype Many f a = Many { getMany :: NonEmpty (f a) }
-  deriving (Functor)
-
-instance Applicative f => Applicative (Many f) where
-  pure = Many . pure . pure
-  Many mf <*> Many mx = Many $ liftA2 (<*>) mf mx
-
-one :: f a -> Many f a
-one = Many . pure
-
-mapMany :: (forall x. f x -> g x) -> Many f a -> Many g a
-mapMany eta (Many m) = Many (fmap eta m)
-
-data BodyDecoding a = BodyDecoding
-  { bodyDecodingNames :: NonEmpty T.Text
-  , bodyDecodingFunction :: LBS.ByteString -> Either T.Text a
-  }
-
-data BodyEncoding a = BodyEncoding
-  { bodyEncodingNames :: NonEmpty T.Text
-  , bodyEncodingFunction :: a -> LBS.ByteString
-  }
-
--- Note to self, we maybe should change this to use list instead of
--- non-empty list. When encoding unit, we actually want
--- to omit the Content-Type header.
-data BodyCodec a = BodyCodec
-  { bodyCodecNames :: NonEmpty T.Text
-  , bodyCodecEncode :: a -> LBS.ByteString
-  , bodyCodecDecode :: LBS.ByteString -> Either T.Text a
-  }
-
-bodyCodecToBodyEncoding :: BodyCodec a -> BodyEncoding a
-bodyCodecToBodyEncoding (BodyCodec names enc _) = BodyEncoding names enc
-
-bodyCodecToBodyDecoding :: BodyCodec a -> BodyDecoding a
-bodyCodecToBodyDecoding (BodyCodec names _ dec) = BodyDecoding names dec
-
 data Param
   = Flag
   | forall a. Optional a
   | forall a. List a
 
-data Query :: (Type -> Type) -> Param -> Type where
-  QueryFlag :: T.Text -> Query cap Flag
-  QueryOptional :: T.Text -> cap a -> Query cap (Optional a)
-  QueryList :: T.Text -> cap a -> Query cap (List a)
-
 data Parameter :: Param -> Type where
   ParameterFlag :: Bool -> Parameter Flag
   ParameterOptional :: Maybe a -> Parameter (Optional a)
   ParameterList :: [a] -> Parameter (List a)
 
-data QueryParam
-  = QueryParamFlag
-  | QueryParamSingle T.Text
-  | QueryParamList [T.Text]
-  deriving Eq
-
-instance Semigroup QueryParam where
-  QueryParamFlag <> q = q
-  q <> QueryParamFlag = q
-  QueryParamSingle q1 <> QueryParamSingle q2 = QueryParamList [q1,q2]
-  QueryParamSingle q1 <> QueryParamList l1 = QueryParamList (q1:l1)
-  QueryParamList l1 <> QueryParamSingle q1 = QueryParamList (l1 ++ [q1]) -- Change list to a set
-  QueryParamList l1 <> QueryParamList l2 = QueryParamList (l1 ++ l2)
-
-instance Monoid QueryParam where
-  mempty = QueryParamFlag
-  mappend = (<>)
-
-newtype QueryString = QueryString
-  { unQueryString :: HM.HashMap T.Text QueryParam
-  } deriving Eq
-
-infixr 7 ./
-
-(./) :: (a -> b) -> a -> b
-(./) f a = f a
-
-match :: T.Text -> Path cpf caps -> Path cpf caps
-match = PathConsMatch
-
-capture :: cpf cap -> Path cpf caps -> Path cpf (cap ': caps)
-capture = PathConsCapture
-
-end :: Path cpf '[]
-end = PathNil
-
-body :: rqf req -> RequestBody rqf ('Body req)
-body = RequestBodyPresent
-
-bodyless :: RequestBody rqf 'Bodyless
-bodyless = RequestBodyAbsent
-
-resp :: rpf resp -> ResponseBody rpf resp
-resp = ResponseBody
+data Query :: (Type -> Type) -> Param -> Type where
+  QueryFlag :: T.Text -> Query cap Flag
+  QueryOptional :: T.Text -> cap a -> Query cap (Optional a)
+  QueryList :: T.Text -> cap a -> Query cap (List a)
 
 flag :: T.Text -> Query cpf Flag
 flag = QueryFlag
@@ -280,62 +242,6 @@
   QueryOptional key query -> QueryOptional key (eta query)
   QueryList key query -> QueryList key (eta query)
 
-data CaptureCodec a = CaptureCodec
-  { captureCodecEncode :: a -> T.Text
-  , captureCodecDecode :: T.Text -> Maybe a
-  }
-
-newtype CaptureEncoding a = CaptureEncoding { appCaptureEncoding :: a -> T.Text }
-newtype CaptureDecoding a = CaptureDecoding { appCaptureDecoding :: T.Text -> Maybe a }
-
-captureCodecToCaptureEncoding :: CaptureCodec a -> CaptureEncoding a
-captureCodecToCaptureEncoding (CaptureCodec enc _) = CaptureEncoding enc
-
-captureCodecToCaptureDecoding :: CaptureCodec a -> CaptureDecoding a
-captureCodecToCaptureDecoding (CaptureCodec _ dec) = CaptureDecoding dec
-
-data Url = Url
-  { urlPath :: ![T.Text]
-  , urlQueryString :: !QueryString
-  } deriving Eq
-
-instance Show Url where
-  show = show . encodeUrl
-
-encodeQuery :: QueryString -> N.Query
-encodeQuery = HM.foldrWithKey (\key param items -> toQueryItem key param ++ items) [] . unQueryString
-  where
-    toQueryItem :: T.Text -> QueryParam -> N.Query
-    toQueryItem key = \case
-      QueryParamFlag -> [(T.encodeUtf8 key, Nothing)]
-      QueryParamSingle value -> [(T.encodeUtf8 key, Just (T.encodeUtf8 value))]
-      QueryParamList values ->
-        flip fmap values $ \value -> (T.encodeUtf8 key, Just (T.encodeUtf8 value))
-
-encodeUrl :: Url -> T.Text
-encodeUrl (Url path querys) =
-  ( T.decodeUtf8
-  . LBS.toStrict
-  . LBS.toLazyByteString
-  . encode
-  . encodeQuery ) querys
-  where
-    encode qs = case path of
-      [] -> "/" <> N.encodePath path qs
-      _  -> N.encodePath path qs
-
-decodeQuery :: N.Query -> QueryString
-decodeQuery = QueryString . HM.fromListWith (<>) . fmap decode
-  where
-    decode (key,mval) = case mval of
-      Nothing  -> (tkey,QueryParamFlag)
-      Just val -> (tkey,QueryParamSingle (T.decodeUtf8 val))
-      where tkey = T.decodeUtf8 key
-
-decodeUrl :: T.Text -> Url
-decodeUrl txt = Url path (decodeQuery querys)
-  where (path,querys) = N.decodePath (T.encodeUtf8 txt)
-
 -- | Generate a @Url@ for use in hyperlinks.
 linkWith :: forall route response.
      (forall caps qrys req resp. route caps qrys req resp -> Path CaptureEncoding caps)
@@ -351,7 +257,7 @@
 data Payload = Payload
   { payloadUrl :: !Url
   , payloadContent :: !(Maybe Content)
-  , payloadAccepts :: !(NonEmpty T.Text)
+  , payloadAccepts :: !(NonEmpty N.MediaType)
   }
 
 -- | Only useful for library authors
@@ -377,12 +283,12 @@
 
 -- Only useful to implement packages like 'trasa-client'
 requestWith :: Functor m
-  => (forall caps querys req resp. route caps querys req resp -> T.Text)
+  => (forall caps querys req resp. route caps querys req resp -> Method)
   -> (forall caps querys req resp. route caps querys req resp -> Path CaptureEncoding caps)
   -> (forall caps querys req resp. route caps querys req resp -> Rec (Query CaptureEncoding) querys)
   -> (forall caps querys req resp. route caps querys req resp -> RequestBody (Many BodyEncoding) req)
   -> (forall caps querys req resp. route caps querys req resp -> ResponseBody (Many BodyDecoding) resp)
-  -> (T.Text -> Url -> Maybe Content -> [T.Text] -> m (Either TrasaErr Content))
+  -> (Method -> Url -> Maybe Content -> NonEmpty N.MediaType -> m (Either TrasaErr Content))
   -- ^ method, url, content, accepts -> response
   -> Prepared route response
   -> m (Either TrasaErr response)
@@ -392,9 +298,8 @@
       content = encodeRequestBody (toReqBody route) reqBody
       respBodyDecs = toRespBody route
       ResponseBody (Many decodings) = respBodyDecs
-      accepts = toList (bodyDecodingNames =<< decodings)
-      decode = note (TrasaErr N.status400 "Could not decode response") . decodeResponseBody respBodyDecs
-   in fmap (\c -> c >>= decode) (run method url content accepts)
+      accepts = bodyDecodingNames =<< decodings
+   in fmap (\c -> c >>= decodeResponseBody respBodyDecs) (run method url content accepts)
 
 encodeRequestBody :: RequestBody (Many BodyEncoding) request -> RequestBody Identity request -> Maybe Content
 encodeRequestBody RequestBodyAbsent RequestBodyAbsent = Nothing
@@ -402,11 +307,55 @@
   case NE.head encodings of
     BodyEncoding names encoding -> Just (Content (NE.head names) (encoding rq))
 
-decodeResponseBody :: ResponseBody (Many BodyDecoding) response -> Content -> Maybe response
-decodeResponseBody (ResponseBody (Many decodings)) (Content name content) =
-  flip mapFind decodings $ \(BodyDecoding names decode) ->
-    if elem name names then hush (decode content) else Nothing
+decodeRequestBody
+  :: RequestBody (Many BodyDecoding) req
+  -> Maybe Content
+  -> Either TrasaErr (RequestBody Identity req)
+decodeRequestBody reqDec mcontent = case reqDec of
+  RequestBodyPresent decs -> case mcontent of
+    Nothing -> wrongBody
+    Just (Content media bod) -> go (toList (getMany decs)) media bod
+  RequestBodyAbsent -> case mcontent of
+    Nothing -> Right RequestBodyAbsent
+    Just _  -> wrongBody
+  where
+    wrongBody = Left (status N.status415)
+    go :: [BodyDecoding a] -> N.MediaType -> LBS.ByteString -> Either TrasaErr (RequestBody Identity (Body a))
+    go [] _ _ = Left (status N.status400)
+    go (BodyDecoding medias dec:decs) media bod = case any (flip N.matches media) medias of
+      True -> bimap (TrasaErr N.status415 . LBS.fromStrict . T.encodeUtf8)
+                    (RequestBodyPresent . Identity)
+                    (dec bod)
+      False -> go decs media bod
 
+encodeResponseBody
+  :: forall response
+  .  [N.MediaType]
+  -> ResponseBody (Many BodyEncoding) response
+  -> response
+  -> Either TrasaErr Content
+encodeResponseBody medias (ResponseBody encs) res = go (toList (getMany encs))
+  where
+    go :: [BodyEncoding response] -> Either TrasaErr Content
+    go [] = Left (status N.status406)
+    go (BodyEncoding accepts e:es) = case acceptable (toList accepts) medias of
+      Just typ -> Right (Content typ (e res))
+      Nothing  -> go es
+    acceptable :: [N.MediaType] -> [N.MediaType] -> Maybe N.MediaType
+    acceptable [] _ = Nothing
+    acceptable (a:as) ms = case any (N.matches a) ms of
+      True  -> Just a
+      False -> acceptable as ms
+
+decodeResponseBody :: ResponseBody (Many BodyDecoding) response -> Content -> Either TrasaErr response
+decodeResponseBody (ResponseBody (Many decodings)) (Content name content) = go (toList decodings)
+  where
+    go :: [BodyDecoding response] -> Either TrasaErr response
+    go [] = Left (status N.status415)
+    go (BodyDecoding names dec:decs) = case any (N.matches name) names of
+      True -> first (TrasaErr N.status400 . LBS.fromStrict . T.encodeUtf8) (dec content)
+      False -> go decs
+
 encodePieces
   :: Path CaptureEncoding captures
   -> Rec (Query CaptureEncoding) querys
@@ -439,19 +388,6 @@
     encodeQueries (QueryList key (CaptureEncoding enc) :& encs) (ParameterList vals :& qs) =
        HM.insert key (QueryParamList (fmap enc vals)) (encodeQueries encs qs)
 
-data TrasaErr = TrasaErr
-  { trasaErrStatus :: N.Status
-  , trasaErrBody :: LBS.ByteString
-  } deriving (Eq,Ord)
-
-instance Show TrasaErr where
-  show (TrasaErr s b) = "Trasa Error with status: " ++ show s ++ " and body: " ++ LBC.unpack b
-
-instance Exception TrasaErr where
-
-status :: N.Status -> TrasaErr
-status s = TrasaErr s ""
-
 -- | Only useful to implement packages like 'trasa-server'
 dispatchWith :: forall route m.
      Applicative m
@@ -460,28 +396,20 @@
   -> (forall caps qrys req resp. route caps qrys req resp -> ResponseBody (Many BodyEncoding) resp)
   -> (forall caps qrys req resp. route caps qrys req resp -> Rec Identity caps -> Rec Parameter qrys -> RequestBody Identity req -> m resp)
   -> Router route -- ^ Router
-  -> T.Text -- ^ Method
-  -> [T.Text] -- ^ Accept headers
+  -> Method -- ^ Method
+  -> [N.MediaType] -- ^ Accept headers
   -> Url -- ^ Everything after the authority
   -> Maybe Content -- ^ Content type and request body
   -> m (Either TrasaErr Content) -- ^ Encoded response
 dispatchWith toQueries toReqBody toRespBody makeResponse router method accepts url mcontent =
-  sequenceA $ do
-    Concealed route decodedPathPieces decodedQueries decodedRequestBody <-
-      parseWith toQueries toReqBody router method url mcontent
-    let response = makeResponse route decodedPathPieces decodedQueries decodedRequestBody
-        ResponseBody (Many encodings) = toRespBody route
-    (encode,typ) <- mapFindE (status N.status406)
-      (\(BodyEncoding names encode) -> case mapFind (\x -> if elem x accepts then Just x else Nothing) names of
-        Just name -> Just (encode,name)
-        Nothing -> Nothing
-      )
-      encodings
-    Right (fmap (Content typ . encode) response)
+  case parseWith toQueries toReqBody router method url mcontent of
+    Left err -> pure (Left err)
+    Right (Concealed route path querys reqBody) ->
+      encodeResponseBody accepts (toRespBody route) <$> makeResponse route path querys reqBody
 
 -- | Build a router from all the possible routes, and methods to turn routes into needed metadata
 routerWith ::
-     (forall caps querys req resp. route caps querys req resp -> T.Text)
+     (forall caps querys req resp. route caps querys req resp -> Method)
   -- ^ Get the method from a route
   -> (forall caps querys req resp. route caps querys req resp -> Path CaptureDecoding caps)
   -- ^ How to decode path pieces of a route
@@ -496,7 +424,7 @@
      (forall caps qrys req resp. route caps qrys req resp -> Rec (Query CaptureDecoding) qrys)
   -> (forall caps qrys req resp. route caps qrys req resp -> RequestBody (Many BodyDecoding) req)
   -> Router route -- ^ Router
-  -> T.Text -- ^ Request Method
+  -> Method -- ^ Request Method
   -> Url -- ^ Everything after the authority
   -> Maybe Content -- ^ Request content type and body
   -> Either TrasaErr (Concealed route)
@@ -504,24 +432,13 @@
   Pathed route captures <- maybe (Left (status N.status404)) Right
     $ parsePathWith router method encodedPath
   querys <- parseQueryWith (toQueries route) encodedQuery
-  decodedRequestBody <- case toReqBody route of
-    RequestBodyPresent (Many decodings) -> case mcontent of
-      Just (Content typ encodedRequest) -> do
-        decode <- mapFindE (status N.status415) (\(BodyDecoding names decode) -> if elem typ names then Just decode else Nothing) decodings
-        reqVal <- badReq (decode encodedRequest)
-        Right (RequestBodyPresent (Identity reqVal))
-      Nothing -> Left (status N.status415)
-    RequestBodyAbsent -> case mcontent of
-      Just _ -> Left (status N.status415)
-      Nothing -> Right RequestBodyAbsent
-  return (Concealed route captures querys decodedRequestBody)
-  where badReq :: Either T.Text b -> Either TrasaErr b
-        badReq = first (TrasaErr N.status400 . LBS.fromStrict . T.encodeUtf8)
+  reqBody <- decodeRequestBody (toReqBody route) mcontent
+  return (Concealed route captures querys reqBody)
 
 -- | Parses only the path.
 parsePathWith :: forall route.
      Router route
-  -> T.Text -- ^ Method
+  -> Method -- ^ Method
   -> [T.Text] -- ^ Path Pieces
   -> Maybe (Pathed route)
 parsePathWith (Router r0) method pieces0 =
@@ -533,7 +450,7 @@
      -> IxedRouter route n -- router fragment
      -> [Pathed route]
   go captures ps (IxedRouter matches mcapture responders) = case ps of
-    [] -> case HM.lookup method responders of
+    [] -> case HM.lookup (encodeMethod method) responders of
       Nothing -> []
       Just respondersAtMethod ->
         mapMaybe (\(IxedResponder route capDecs) ->
@@ -720,35 +637,10 @@
 
 -- | The HTTP content type and body.
 data Content = Content
-  { contentType :: T.Text
+  { contentType :: N.MediaType
   , contentData :: LBS.ByteString
   } deriving (Show,Eq,Ord)
 
-hush :: Either e a -> Maybe a
-hush (Left _)  = Nothing
-hush (Right a) = Just a
-
-note :: e -> Maybe a -> Either e a
-note e Nothing  = Left e
-note _ (Just a) = Right a
-
-mapFind :: Foldable f => (a -> Maybe b) -> f a -> Maybe b
-mapFind f = listToMaybe . mapMaybe f . toList
-
-mapFindE :: Foldable f => e -> (a -> Maybe b) -> f a -> Either e b
-mapFindE e f = listToEither . mapMaybe f . toList
-  where listToEither [] = Left e
-        listToEither (x:_) = Right x
-
-showReadBodyCodec :: (Show a, Read a) => BodyCodec a
-showReadBodyCodec = BodyCodec
-  (pure "text/haskell")
-  (LBC.pack . show)
-  (first T.pack . readEither . LBC.unpack)
-
-showReadCaptureCodec :: (Show a, Read a) => CaptureCodec a
-showReadCaptureCodec = CaptureCodec (T.pack . show) (readMaybe . T.unpack)
-
 -- | Only promoted version used.
 data Nat = S !Nat | Z
 
@@ -855,7 +747,7 @@
   go (LenPathCapture pnext) = snocLenPathCapture (go pnext)
 
 singletonIxedRouter ::
-  route captures querys request response -> T.Text -> Path CaptureDecoding captures -> IxedRouter route 'Z
+  route captures querys request response -> Method -> Path CaptureDecoding captures -> IxedRouter route 'Z
 singletonIxedRouter route method capDecs = case pathToIxedPath capDecs of
   HideIx ixedCapDecs ->
     let ixedCapDecsRec = ixedPathToIxedRec ixedCapDecs
@@ -864,9 +756,9 @@
      in singletonIxedRouterHelper responder method lenPath
 
 singletonIxedRouterHelper ::
-  IxedResponder route n -> T.Text -> LenPath n -> IxedRouter route 'Z
+  IxedResponder route n -> Method -> LenPath n -> IxedRouter route 'Z
 singletonIxedRouterHelper responder method path =
-  let r = IxedRouter HM.empty Nothing (HM.singleton method [responder])
+  let r = IxedRouter HM.empty Nothing (HM.singleton (encodeMethod method) [responder])
    in singletonIxedRouterGo r path
 
 singletonIxedRouterGo ::
diff --git a/src/Trasa/Error.hs b/src/Trasa/Error.hs
new file mode 100644
--- /dev/null
+++ b/src/Trasa/Error.hs
@@ -0,0 +1,29 @@
+{-# LANGUAGE OverloadedStrings #-}
+module Trasa.Error
+  (
+  -- * Types
+    TrasaErr(..)
+  -- * Simple Errors
+  , status
+  ) where
+
+import Control.Exception (Exception(..))
+import qualified Data.ByteString.Lazy.Char8 as LBC
+import qualified Network.HTTP.Types as N
+
+data TrasaErr = TrasaErr
+  { trasaErrStatus :: N.Status
+  , trasaErrBody :: LBC.ByteString
+  } deriving (Eq,Ord)
+
+instance Show TrasaErr where
+  show (TrasaErr s b) =
+    "Trasa Error with status: " ++
+    show s ++
+    if LBC.null b then "" else " and body: " ++ LBC.unpack b
+
+instance Exception TrasaErr where
+
+status :: N.Status -> TrasaErr
+status s = TrasaErr s ""
+
diff --git a/src/Trasa/Method.hs b/src/Trasa/Method.hs
new file mode 100644
--- /dev/null
+++ b/src/Trasa/Method.hs
@@ -0,0 +1,69 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+-- | This module exports symbols that will conflict with the standard prelude.
+-- It is recommended to be import qualified or just import 'Method' and use its 'IsString' instance.
+module Trasa.Method
+  (
+  -- * Method
+    Method
+  , encodeMethod
+  , decodeMethod
+  -- * Convenience pre defined methods
+  , get
+  , post
+  , head
+  , put
+  , delete
+  , trace
+  , connect
+  , options
+  , patch
+  ) where
+
+import Prelude hiding (head)
+import Data.Hashable(Hashable(..))
+import Data.String (IsString(..))
+import qualified Data.Text as T
+
+newtype Method = Method T.Text
+  deriving (Hashable,Eq,Ord)
+
+instance Show Method where
+  show = show . encodeMethod
+
+instance IsString Method where
+  fromString = decodeMethod . T.pack
+
+encodeMethod :: Method -> T.Text
+encodeMethod (Method txt) = txt
+
+decodeMethod :: T.Text -> Method
+decodeMethod = Method . T.toUpper
+
+get :: Method
+get = "GET"
+
+post :: Method
+post = "POST"
+
+head :: Method
+head = "HEAD"
+
+put :: Method
+put = "PUT"
+
+delete :: Method
+delete = "DELETE"
+
+trace :: Method
+trace = "TRACE"
+
+connect :: Method
+connect = "CONNECT"
+
+options :: Method
+options = "OPTIONS"
+
+patch :: Method
+patch = "PATCH"
+
diff --git a/src/Trasa/Tutorial.hs b/src/Trasa/Tutorial.hs
--- a/src/Trasa/Tutorial.hs
+++ b/src/Trasa/Tutorial.hs
@@ -11,6 +11,7 @@
   ) where
 
 import Trasa.Core
+import qualified Trasa.Method as M
 import Data.Vinyl (Rec)
 import Data.Kind (Type)
 import Data.Text (Text)
@@ -40,7 +41,7 @@
 --   , metaQuery :: Rec (Query CaptureCodec) querys
 --   , metaRequestBody :: RequestBody BodyCodec request
 --   , metaResponseBody :: ResponseBody BodyCodec response
---   , metaMethod :: Text
+--   , metaMethod :: Method
 --   }
 -- int :: CaptureCodec Int
 -- int = showReadCaptureCodec
@@ -55,19 +56,19 @@
 --   AssignR -> Meta
 --     (match "assign" ./ capture counter ./ match "to" ./ capture int ./ end)
 --     qend
---     bodyless (resp bodyUnit) "post"
+--     bodyless (resp bodyUnit) M.post
 --   IncrementR -> Meta
 --     (match "increment" ./ capture counter ./ end)
 --     qend
---     bodyless (resp bodyInt) "post"
+--     bodyless (resp bodyInt) M.post
 --   QueryR -> Meta
 --     (match "query" ./ capture counter ./ end)
 --     qend
---     bodyless (resp bodyInt) "get"
+--     bodyless (resp bodyInt) M.get
 --   TotalR -> Meta
 --     (match "total" ./ end)
 --     qend
---     bodyless (resp bodyInt) "get"
+--     bodyless (resp bodyInt) M.get
 -- :}
 --
 -- Now, we can start using our routes. To do this, we take functions that
diff --git a/src/Trasa/Url.hs b/src/Trasa/Url.hs
new file mode 100644
--- /dev/null
+++ b/src/Trasa/Url.hs
@@ -0,0 +1,87 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE LambdaCase #-}
+{-# OPTIONS_GHC -Wall -Werror #-}
+module Trasa.Url
+  (
+  -- * Untyped Query Parameters
+    QueryParam(..)
+  , QueryString(..)
+  , encodeQuery
+  , decodeQuery
+  -- * Urls (path + query string)
+  , Url(..)
+  , encodeUrl
+  , decodeUrl
+  ) where
+
+import Data.Semigroup (Semigroup(..))
+import qualified Data.ByteString.Lazy as LBS
+import qualified Data.ByteString.Builder as LBS
+import qualified Data.Text as T
+import qualified Data.Text.Encoding as T
+import qualified Data.HashMap.Strict as HM
+import qualified Network.HTTP.Types as N
+
+data QueryParam
+  = QueryParamFlag
+  | QueryParamSingle T.Text
+  | QueryParamList [T.Text]
+  deriving Eq
+
+instance Semigroup QueryParam where
+  QueryParamFlag <> q = q
+  q <> QueryParamFlag = q
+  QueryParamSingle q1 <> QueryParamSingle q2 = QueryParamList [q1,q2]
+  QueryParamSingle q1 <> QueryParamList l1 = QueryParamList (q1:l1)
+  QueryParamList l1 <> QueryParamSingle q1 = QueryParamList (l1 ++ [q1]) -- O(n^2)...
+  QueryParamList l1 <> QueryParamList l2 = QueryParamList (l1 ++ l2)
+
+instance Monoid QueryParam where
+  mempty = QueryParamFlag
+  mappend = (<>)
+
+newtype QueryString = QueryString
+  { unQueryString :: HM.HashMap T.Text QueryParam
+  } deriving Eq
+
+encodeQuery :: QueryString -> N.Query
+encodeQuery = HM.foldrWithKey (\key param items -> toQueryItem key param ++ items) [] . unQueryString
+  where
+    toQueryItem :: T.Text -> QueryParam -> N.Query
+    toQueryItem key = \case
+      QueryParamFlag -> [(T.encodeUtf8 key, Nothing)]
+      QueryParamSingle value -> [(T.encodeUtf8 key, Just (T.encodeUtf8 value))]
+      QueryParamList values ->
+        flip fmap values $ \value -> (T.encodeUtf8 key, Just (T.encodeUtf8 value))
+
+decodeQuery :: N.Query -> QueryString
+decodeQuery = QueryString . HM.fromListWith (<>) . fmap decode
+  where
+    decode (key,mval) = case mval of
+      Nothing  -> (tkey,QueryParamFlag)
+      Just val -> (tkey,QueryParamSingle (T.decodeUtf8 val))
+      where tkey = T.decodeUtf8 key
+
+data Url = Url
+  { urlPath :: ![T.Text]
+  , urlQueryString :: !QueryString
+  } deriving Eq
+
+instance Show Url where
+  show = show . encodeUrl
+
+encodeUrl :: Url -> T.Text
+encodeUrl (Url path querys) =
+  ( T.decodeUtf8
+  . LBS.toStrict
+  . LBS.toLazyByteString
+  . encode
+  . encodeQuery ) querys
+  where
+    encode qs = case path of
+      [] -> "/" <> N.encodePath path qs
+      _  -> N.encodePath path qs
+
+decodeUrl :: T.Text -> Url
+decodeUrl txt = Url path (decodeQuery querys)
+  where (path,querys) = N.decodePath (T.encodeUtf8 txt)
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -8,6 +8,7 @@
 
 import Text.Read (readMaybe)
 import Trasa.Core
+import qualified Trasa.Method as M
 import Data.Vinyl
 import Data.Kind (Type)
 
@@ -26,8 +27,7 @@
 main = do
   putStrLn "\nRUNNING DOCTESTS"
   doctest
-    [ "src/Trasa/Core.hs"
-    , "src/Trasa/Tutorial.hs"
+    [ "src/Trasa"
     ]
   putStrLn "\nPRETTY ROUTER"
   putStrLn (prettyRouter router)
@@ -103,7 +103,7 @@
   , metaQuery :: Rec (Query CaptureCodec) qs
   , metaRequestBody :: RequestBody BodyCodec rq
   , metaResponseBody :: ResponseBody BodyCodec rp
-  , metaMethod :: T.Text
+  , metaMethod :: Method
   }
 
 meta :: Route ps qs rq rp -> Meta ps qs rq rp
@@ -111,31 +111,31 @@
   EmptyR -> Meta
     end
     qend
-    bodyless (resp bodyInt) "get"
+    bodyless (resp bodyInt) M.get
   HelloR -> Meta
     (match "hello" ./ end)
     qend
-    bodyless (resp bodyInt) "get"
+    bodyless (resp bodyInt) M.get
   AdditionR -> Meta
     (match "add" ./ capture int ./ capture int ./ end)
     (optional "more" int .& qend)
-    bodyless (resp bodyInt) "get"
+    bodyless (resp bodyInt) M.get
   IdentityR -> Meta
     (match "identity" ./ capture string ./ end)
     qend
-    bodyless (resp bodyString) "get"
+    bodyless (resp bodyString) M.get
   LeftPadR -> Meta
     (match "pad" ./ match "left" ./ capture int ./ end)
     qend
-    (body bodyString) (resp bodyString) "get"
+    (body bodyString) (resp bodyString) M.get
   TrickyOneR -> Meta
     (match "tricky" ./ capture int ./ match "one" ./ end)
     qend
-    bodyless (resp bodyString) "get"
+    bodyless (resp bodyString) M.get
   TrickyTwoR -> Meta
     (capture int ./ capture int ./ match "two" ./ end)
     qend
-    bodyless (resp bodyString) "get"
+    bodyless (resp bodyString) M.get
 
 int :: CaptureCodec Int
 int = CaptureCodec (T.pack . show) (readMaybe . T.unpack)
diff --git a/trasa.cabal b/trasa.cabal
--- a/trasa.cabal
+++ b/trasa.cabal
@@ -1,5 +1,5 @@
 name: trasa
-version: 0.1
+version: 0.2
 synopsis: Type Safe Web Routing
 homepage: https://github.com/haskell-trasa/trasa#readme
 license: BSD3
@@ -21,6 +21,10 @@
 library
   hs-source-dirs:      src
   exposed-modules:
+    Trasa.Method
+    Trasa.Url
+    Trasa.Codec
+    Trasa.Error
     Trasa.Core
     Trasa.Tutorial
   build-depends:
@@ -29,7 +33,9 @@
     , binary == 0.8.*
     , text == 1.2.*
     , vinyl == 0.5.*
+    , hashable == 1.2.*
     , http-types == 0.9.*
+    , http-media == 0.6.*
     , unordered-containers >= 0.2 && < 0.3
   default-language:    Haskell2010
 
