diff --git a/src/Trasa/Codec.hs b/src/Trasa/Codec.hs
--- a/src/Trasa/Codec.hs
+++ b/src/Trasa/Codec.hs
@@ -3,14 +3,20 @@
   (
   -- * Capture Codecs
     CaptureEncoding(..)
+  , HasCaptureEncoding(..)
   , CaptureDecoding(..)
+  , HasCaptureDecoding(..)
   , CaptureCodec(..)
+  , HasCaptureCodec(..)
   , captureCodecToCaptureEncoding
   , captureCodecToCaptureDecoding
   -- * Body Codecs
   , BodyEncoding(..)
+  , HasBodyEncoding(..)
   , BodyDecoding(..)
+  , HasBodyDecoding(..)
   , BodyCodec(..)
+  , HasBodyCodec(..)
   , bodyCodecToBodyEncoding
   , bodyCodecToBodyDecoding
   -- * Type Class Based Codecs
@@ -28,13 +34,37 @@
 
 newtype CaptureEncoding a = CaptureEncoding { appCaptureEncoding :: a -> T.Text }
 
+class HasCaptureEncoding capStrategy where
+  captureEncoding :: capStrategy a -> CaptureEncoding a
+
+instance HasCaptureEncoding CaptureEncoding where
+  captureEncoding = id
+
 newtype CaptureDecoding a = CaptureDecoding { appCaptureDecoding :: T.Text -> Maybe a }
 
+class HasCaptureDecoding capStrategy where
+  captureDecoding :: capStrategy a -> CaptureDecoding a
+
+instance HasCaptureDecoding CaptureDecoding where
+  captureDecoding = id
+
 data CaptureCodec a = CaptureCodec
   { captureCodecEncode :: a -> T.Text
   , captureCodecDecode :: T.Text -> Maybe a
   }
 
+class HasCaptureCodec capStrategy where
+  captureCodec :: capStrategy a -> CaptureCodec a
+
+instance HasCaptureEncoding CaptureCodec where
+  captureEncoding = captureCodecToCaptureEncoding
+
+instance HasCaptureDecoding CaptureCodec where
+  captureDecoding = captureCodecToCaptureDecoding
+
+instance HasCaptureCodec CaptureCodec where
+  captureCodec = id
+
 captureCodecToCaptureEncoding :: CaptureCodec a -> CaptureEncoding a
 captureCodecToCaptureEncoding (CaptureCodec enc _) = CaptureEncoding enc
 
@@ -49,16 +79,40 @@
   , bodyEncodingFunction :: a -> LBS.ByteString
   }
 
+class HasBodyEncoding bodyStrategy where
+  bodyEncoding :: bodyStrategy a -> BodyEncoding a
+
+instance HasBodyEncoding BodyEncoding where
+  bodyEncoding = id
+
 data BodyDecoding a = BodyDecoding
   { bodyDecodingNames :: NonEmpty N.MediaType
   , bodyDecodingFunction :: LBS.ByteString -> Either T.Text a
   }
 
+class HasBodyDecoding bodyStrategy where
+  bodyDecoding :: bodyStrategy a -> BodyDecoding a
+
+instance HasBodyDecoding BodyDecoding where
+  bodyDecoding = id
+
 data BodyCodec a = BodyCodec
   { bodyCodecNames :: NonEmpty N.MediaType
   , bodyCodecEncode :: a -> LBS.ByteString
   , bodyCodecDecode :: LBS.ByteString -> Either T.Text a
   }
+
+class HasBodyCodec bodyStrategy where
+  bodyCodec :: bodyStrategy a -> BodyCodec a
+
+instance HasBodyEncoding BodyCodec where
+  bodyEncoding = bodyCodecToBodyEncoding
+
+instance HasBodyDecoding BodyCodec where
+  bodyDecoding = bodyCodecToBodyDecoding
+
+instance HasBodyCodec BodyCodec where
+  bodyCodec = id
 
 bodyCodecToBodyEncoding :: BodyCodec a -> BodyEncoding a
 bodyCodecToBodyEncoding (BodyCodec names enc _) = BodyEncoding names enc
diff --git a/src/Trasa/Core.hs b/src/Trasa/Core.hs
--- a/src/Trasa/Core.hs
+++ b/src/Trasa/Core.hs
@@ -23,6 +23,7 @@
   , Concealed(..)
   , Constructed(..)
   , conceal
+  , concealedToPrepared
   , mapConstructed
   -- * Request Types
   -- ** Method
@@ -42,9 +43,9 @@
   , status
   -- * Using Routes
   , prepareWith
+  , linkWith
   , dispatchWith
   , parseWith
-  , linkWith
   , payloadWith
   , requestWith
   , routerWith
@@ -86,13 +87,33 @@
   , Many(..)
   , one
   , mapMany
+  -- ** Meta
+  , Meta(..)
+  , MetaBuilder
+  , metaBuilderToMetaCodec
+  , MetaCodec
+  , MetaClient
+  , metaCodecToMetaClient
+  , MetaServer
+  , metaCodecToMetaServer
+  , mapMetaPath
+  , mapMetaQuery
+  , mapMetaRequestBody
+  , mapMetaResponseBody
+  , mapMeta
   -- * Codecs
   , CaptureEncoding(..)
+  , HasCaptureEncoding(..)
   , CaptureDecoding(..)
+  , HasCaptureDecoding(..)
   , CaptureCodec(..)
+  , HasCaptureCodec(..)
   , BodyEncoding(..)
+  , HasBodyEncoding(..)
   , BodyDecoding(..)
+  , HasBodyDecoding(..)
   , BodyCodec(..)
+  , HasBodyCodec(..)
     -- ** Converting Codecs
   , captureCodecToCaptureEncoding
   , captureCodecToCaptureDecoding
@@ -126,8 +147,9 @@
 import qualified Network.HTTP.Media.MediaType as N
 import qualified Network.HTTP.Media.Accept as N
 import qualified Data.HashMap.Strict as HM
+import qualified Data.Semigroup as SG
 import Data.HashMap.Strict (HashMap)
-import Data.Vinyl (Rec(..),rmap)
+import Data.Vinyl (Rec(..),rmap,rtraverse)
 import Data.Vinyl.TypeLevel (type (++))
 
 import Trasa.Method
@@ -154,7 +176,7 @@
 data Bodiedness = forall a. Body a | Bodyless
 
 data RequestBody :: (Type -> Type) -> Bodiedness -> Type where
-  RequestBodyPresent :: f a -> RequestBody f ('Body a)
+  RequestBodyPresent :: !(f a) -> RequestBody f ('Body a)
   RequestBodyAbsent :: RequestBody f 'Bodyless
 
 body :: rqf req -> RequestBody rqf ('Body req)
@@ -177,8 +199,8 @@
 
 data Path :: (Type -> Type) -> [Type] -> Type where
   PathNil :: Path cap '[]
-  PathConsCapture :: cap a -> Path cap as -> Path cap (a ': as)
-  PathConsMatch :: T.Text -> Path cap as -> Path cap as
+  PathConsCapture :: !(cap a) -> !(Path cap as) -> Path cap (a ': as)
+  PathConsMatch :: !T.Text -> !(Path cap as) -> Path cap as
 
 infixr 7 ./
 
@@ -210,14 +232,14 @@
   | forall a. List a
 
 data Parameter :: Param -> Type where
-  ParameterFlag :: Bool -> Parameter Flag
-  ParameterOptional :: Maybe a -> Parameter (Optional a)
-  ParameterList :: [a] -> Parameter (List a)
+  ParameterFlag :: !Bool -> Parameter Flag
+  ParameterOptional :: !(Maybe a) -> Parameter (Optional a)
+  ParameterList :: ![a] -> Parameter (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)
+  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
@@ -242,17 +264,90 @@
   QueryOptional key query -> QueryOptional key (eta query)
   QueryList key query -> QueryList key (eta query)
 
+data Meta capCodec qryCodec reqCodec respCodec caps qrys req resp = Meta
+  { metaPath :: !(Path capCodec caps)
+  , metaQuery :: !(Rec (Query qryCodec) qrys)
+  , metaRequestBody :: !(RequestBody reqCodec req)
+  , metaResponseBody :: !(ResponseBody respCodec resp)
+  , metaMethod :: !Method
+  }
+
+mapMetaPath
+  :: (forall x. cf x -> cg x)
+  -> Meta cf qryCodec reqCodec respCodec caps qrys req resp
+  -> Meta cg qryCodec reqCodec respCodec caps qrys req resp
+mapMetaPath eta m = m { metaPath = mapPath eta (metaPath m) }
+
+mapMetaQuery
+  :: (forall x. qf x -> qg x)
+  -> Meta capCodec qf reqCodec respCodec caps qrys req resp
+  -> Meta capCodec qg reqCodec respCodec caps qrys req resp
+mapMetaQuery eta m = m { metaQuery = mapQuery eta (metaQuery m) }
+
+mapMetaRequestBody
+  :: (forall x. rf x -> rg x)
+  -> Meta capCodec qryCodec rf respCodec caps qrys req resp
+  -> Meta capCodec qryCodec rg respCodec caps qrys req resp
+mapMetaRequestBody eta m = m { metaRequestBody = mapRequestBody eta (metaRequestBody m) }
+
+mapMetaResponseBody
+  :: (forall x. rf x -> rg x)
+  -> Meta capCodec qryCodec reqCodec rf caps qrys req resp
+  -> Meta capCodec qryCodec reqCodec rg caps qrys req resp
+mapMetaResponseBody eta m = m { metaResponseBody = mapResponseBody eta (metaResponseBody m)}
+
+mapMeta
+  :: (forall x. capCodec1 x -> capCodec2 x)
+  -> (forall x. qryCodec1 x -> qryCodec2 x)
+  -> (forall x. reqCodec1 x -> reqCodec2 x)
+  -> (forall x. respCodec1 x -> respCodec2 x)
+  -> Meta capCodec1 qryCodec1 reqCodec1 respCodec1 caps qrys req resp
+  -> Meta capCodec2 qryCodec2 reqCodec2 respCodec2 caps qrys req resp
+mapMeta mapCaps mapQrys mapReq mapResp (Meta caps qrys req res method) = Meta
+  (mapPath mapCaps caps)
+  (mapQuery mapQrys qrys)
+  (mapRequestBody mapReq req)
+  (mapResponseBody mapResp res)
+  method
+
+type MetaBuilder = Meta CaptureCodec CaptureCodec BodyCodec BodyCodec
+
+
+-- | This function is a more general way to transform 'MetaBuilder' into 'MetaCodec'.
+--
+--   It wraps the req and resp codecs in Many.
+metaBuilderToMetaCodec
+  :: Meta capCodec qryCodec reqCodec respCodec caps qrys req resp
+  -> Meta capCodec qryCodec (Many reqCodec) (Many respCodec) caps qrys req resp
+metaBuilderToMetaCodec (Meta path query reqBody respBody method) = Meta
+  path
+  query
+  (mapRequestBody one reqBody)
+  (mapResponseBody one respBody)
+  method
+
+type MetaCodec = Meta CaptureCodec CaptureCodec (Many BodyCodec) (Many BodyCodec)
+
+type MetaClient = Meta CaptureEncoding CaptureEncoding (Many BodyEncoding) (Many BodyDecoding)
+
+metaCodecToMetaClient :: MetaCodec caps qrys req resp -> MetaClient caps qrys req resp
+metaCodecToMetaClient = mapMeta captureEncoding captureEncoding (mapMany bodyEncoding) (mapMany bodyDecoding)
+
+type MetaServer = Meta CaptureDecoding CaptureDecoding (Many BodyDecoding) (Many BodyEncoding)
+
+metaCodecToMetaServer :: MetaCodec caps qrys req resp -> MetaServer caps qrys req resp
+metaCodecToMetaServer = mapMeta captureDecoding captureDecoding (mapMany bodyDecoding) (mapMany bodyEncoding)
+
 -- | Generate a @Url@ for use in hyperlinks.
-linkWith :: forall route response.
-     (forall caps qrys req resp. route caps qrys req resp -> Path CaptureEncoding caps)
-  -- ^ How to encode the path pieces of a route
-  -> (forall caps qrys req resp. route caps qrys req resp -> Rec (Query CaptureEncoding) qrys)
-  -- ^ How to encode the query parameters of a route
+linkWith
+  :: forall route response reqCodec respCodec
+  .  (forall caps qrys req resp. route caps qrys req resp -> Meta CaptureEncoding CaptureEncoding reqCodec respCodec caps qrys req resp)
   -> Prepared route response
   -- ^ The route to encode
   -> Url
-linkWith toCapEncs toQueries (Prepared route captures querys _) =
-  encodePieces (toCapEncs route) (toQueries route) captures querys
+linkWith toMeta (Prepared route captures querys _) =
+  encodePieces (metaPath m) (metaQuery m) captures querys
+  where m = toMeta route
 
 data Payload = Payload
   { payloadUrl :: !Url
@@ -261,43 +356,35 @@
   }
 
 -- | Only useful for library authors
-payloadWith :: forall route response.
-     (forall caps qrys req resp. route caps qrys req resp -> Path CaptureEncoding caps)
-  -- ^ How to encode the path pieces of a route
-  -> (forall caps qrys req resp. route caps qrys req resp -> Rec (Query CaptureEncoding) qrys)
-  -- ^ How to encode the query parameters of a route
-  -> (forall caps qrys req resp. route caps qrys req resp -> RequestBody (Many BodyEncoding) req)
-  -- ^ How to encode the request body of a route
-  -> (forall caps qrys req resp. route caps qrys req resp -> ResponseBody (Many BodyDecoding) resp)
-  -- ^ How to decode the response body from a route
+payloadWith
+  :: forall route response
+  .  (forall caps qrys req resp. route caps qrys req resp -> MetaClient caps qrys req resp)
   -> Prepared route response
   -- ^ The route to be payload encoded
   -> Payload
-payloadWith toCapEncs toQueries toReqBody toRespBody p@(Prepared route _ _ reqBody) =
+payloadWith toMeta p@(Prepared route _ _ reqBody) =
   Payload url content accepts
   where
-    url = linkWith toCapEncs toQueries p
-    content = encodeRequestBody (toReqBody route) reqBody
-    ResponseBody (Many decodings) = toRespBody route
+    url = linkWith toMeta p
+    m = toMeta route
+    content = encodeRequestBody (metaRequestBody m) reqBody
+    ResponseBody (Many decodings) = metaResponseBody m
     accepts = bodyDecodingNames =<< decodings
 
 -- Only useful to implement packages like 'trasa-client'
-requestWith :: Functor m
-  => (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)
+requestWith
+  :: Functor m
+  => (forall caps qrys req resp. route caps qrys req resp -> MetaClient caps qrys req resp)
   -> (Method -> Url -> Maybe Content -> NonEmpty N.MediaType -> m (Either TrasaErr Content))
   -- ^ method, url, content, accepts -> response
   -> Prepared route response
   -> m (Either TrasaErr response)
-requestWith toMethod toCapEncs toQueries toReqBody toRespBody run (Prepared route captures querys reqBody) =
-  let method = toMethod route
-      url = encodePieces (toCapEncs route) (toQueries route) captures querys
-      content = encodeRequestBody (toReqBody route) reqBody
-      respBodyDecs = toRespBody route
-      ResponseBody (Many decodings) = respBodyDecs
+requestWith toMeta run (Prepared route captures querys reqBody) =
+  let m = toMeta route
+      method = metaMethod m
+      url = encodePieces (metaPath m) (metaQuery m) captures querys
+      content = encodeRequestBody (metaRequestBody m) reqBody
+      respBodyDecs@(ResponseBody (Many decodings)) = metaResponseBody m
       accepts = bodyDecodingNames =<< decodings
    in fmap (\c -> c >>= decodeResponseBody respBodyDecs) (run method url content accepts)
 
@@ -321,7 +408,7 @@
   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 [] _ _ = Left (status N.status415)
     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)
@@ -389,11 +476,10 @@
        HM.insert key (QueryParamList (fmap enc vals)) (encodeQueries encs qs)
 
 -- | Only useful to implement packages like 'trasa-server'
-dispatchWith :: forall route m.
-     Applicative m
-  => (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)
-  -> (forall caps qrys req resp. route caps qrys req resp -> ResponseBody (Many BodyEncoding) resp)
+dispatchWith
+  :: forall route m
+  .  Applicative m
+  => (forall caps qrys req resp. route caps qrys req resp -> MetaServer caps qrys req 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
   -> Method -- ^ Method
@@ -401,38 +487,41 @@
   -> 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 =
-  case parseWith toQueries toReqBody router method url mcontent of
+dispatchWith toMeta makeResponse madeRouter method accepts url mcontent =
+  case parseWith toMeta madeRouter method url mcontent of
     Left err -> pure (Left err)
     Right (Concealed route path querys reqBody) ->
-      encodeResponseBody accepts (toRespBody route) <$> makeResponse route path querys reqBody
+      encodeResponseBody accepts (metaResponseBody (toMeta 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 -> 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
+routerWith
+  :: forall route qryCodec reqCodec respCodec
+  .  (forall caps qrys req resp. route caps qrys req resp -> Meta CaptureDecoding qryCodec reqCodec respCodec caps qrys req resp)
   -> [Constructed route]
   -> Router route
-routerWith toMethod toCapDec enumeratedRoutes = Router $ foldMap
-  (\(Constructed route) -> singletonIxedRouter route (toMethod route) (toCapDec route))
-  enumeratedRoutes
+routerWith toMeta = Router . foldMap buildRouter
+  where
+    buildRouter :: Constructed route -> IxedRouter route Z
+    buildRouter (Constructed route) = singletonIxedRouter route (metaMethod m) (metaPath m)
+      where m = toMeta route
 
+
 -- | Parses the path, the querystring, and the request body.
-parseWith :: forall route.
-     (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)
+parseWith
+  :: forall route capCodec respCodec
+  .  (forall caps qrys req resp. route caps qrys req resp -> Meta capCodec CaptureDecoding (Many BodyDecoding) respCodec caps qrys req resp)
   -> Router route -- ^ Router
   -> Method -- ^ Request Method
   -> Url -- ^ Everything after the authority
   -> Maybe Content -- ^ Request content type and body
   -> Either TrasaErr (Concealed route)
-parseWith toQueries toReqBody router method (Url encodedPath encodedQuery) mcontent = do
+parseWith toMeta madeRouter method (Url encodedPath encodedQuery) mcontent = do
   Pathed route captures <- maybe (Left (status N.status404)) Right
-    $ parsePathWith router method encodedPath
-  querys <- parseQueryWith (toQueries route) encodedQuery
-  reqBody <- decodeRequestBody (toReqBody route) mcontent
+    $ parsePathWith madeRouter method encodedPath
+  let m = toMeta route
+  querys <- parseQueryWith (metaQuery m) encodedQuery
+  reqBody <- decodeRequestBody (metaRequestBody m) mcontent
   return (Concealed route captures querys reqBody)
 
 -- | Parses only the path.
@@ -468,26 +557,23 @@
        in res1 ++ res2
 
 parseQueryWith :: Rec (Query CaptureDecoding) querys -> QueryString -> Either TrasaErr (Rec Parameter querys)
-parseQueryWith decoding (QueryString querys) = go decoding
+parseQueryWith decoding (QueryString querys) = rtraverse param decoding
   where
-    go :: Rec (Query CaptureDecoding) qrys -> Either TrasaErr (Rec Parameter qrys)
-    go RNil = Right RNil
-    go (q :& qs) = (:&) <$> param <*> go qs
-      where
-        param = case q of
-          QueryFlag key -> Right (ParameterFlag (HM.member key querys))
-          QueryOptional key (CaptureDecoding dec) -> case HM.lookup key querys of
-            Nothing -> Right (ParameterOptional Nothing)
-            Just query -> case query of
-              QueryParamFlag -> Left (TrasaErr N.status400 "query flag given when key-value expected")
-              QueryParamSingle txt -> Right (ParameterOptional (dec txt))
-              QueryParamList _ -> Left (TrasaErr N.status400 "query param list given when key-value expected")
-          QueryList key (CaptureDecoding dec) -> case HM.lookup key querys of
-            Nothing -> Right (ParameterList [])
-            Just query -> case query of
-              QueryParamFlag -> Left (TrasaErr N.status400 "query flag given when list expected")
-              QueryParamSingle txt -> Right (ParameterList (maybe [] (:[]) (dec txt)))
-              QueryParamList txts -> Right (ParameterList (mapMaybe dec txts))
+    param :: forall qry. Query CaptureDecoding qry -> Either TrasaErr (Parameter qry)
+    param = \case
+      QueryFlag key -> Right (ParameterFlag (HM.member key querys))
+      QueryOptional key (CaptureDecoding dec) -> case HM.lookup key querys of
+        Nothing -> Right (ParameterOptional Nothing)
+        Just query -> case query of
+          QueryParamFlag -> Left (TrasaErr N.status400 "query flag given when key-value expected")
+          QueryParamSingle txt -> Right (ParameterOptional (dec txt))
+          QueryParamList _ -> Left (TrasaErr N.status400 "query param list given when key-value expected")
+      QueryList key (CaptureDecoding dec) -> case HM.lookup key querys of
+        Nothing -> Right (ParameterList [])
+        Just query -> case query of
+          QueryParamFlag -> Left (TrasaErr N.status400 "query flag given when list expected")
+          QueryParamSingle txt -> Right (ParameterList (maybe [] (:[]) (dec txt)))
+          QueryParamList txts -> Right (ParameterList (mapMaybe dec txts))
 
 decodeCaptureVector ::
      IxedRec CaptureDecoding n xs
@@ -525,26 +611,23 @@
   Arguments '[] (q ': qs) r b = ParamBase q -> Arguments '[] qs r b
   Arguments (c ': cs) qs b r = c -> Arguments cs qs b r
 
+
 -- | Used my users to define a function called prepare, see tutorial
-prepareWith ::
-     (forall caps qry req resp. route caps qry req resp -> Path pf caps)
-  -- ^ Extract the path codec from a route
-  -> (forall caps qry req resp. route caps qry req resp -> Rec (Query qf) qry)
-  -- ^ Extract the query parameter codec from a route
-  -> (forall caps qry req resp. route caps qry req resp -> RequestBody rqf req)
-  -- ^ Extract the request body codec from a route
+prepareWith
+  :: (forall caps qrys req resp. route caps qrys req resp -> Meta capCodec qryCodec reqCodec respCodec caps qrys req resp)
   -> route captures query request response
   -- ^ The route to prepare
   -> Arguments captures query request (Prepared route response)
-prepareWith toPath toQuery toReqBody route =
-  prepareExplicit route (toPath route) (toQuery route) (toReqBody route)
+prepareWith toMeta route =
+  prepareExplicit route (metaPath m) (metaQuery m) (metaRequestBody m)
+  where m = toMeta route
 
-prepareExplicit :: forall route captures querys request response rqf pf qf.
-     route captures querys request response
+prepareExplicit :: forall route captures queries request response rqf pf qf.
+     route captures queries request response
   -> Path pf captures
-  -> Rec (Query qf) querys
+  -> Rec (Query qf) queries
   -> RequestBody rqf request
-  -> Arguments captures querys request (Prepared route response)
+  -> Arguments captures queries request (Prepared route response)
 prepareExplicit route = go (Prepared route)
   where
   -- Adopted from: https://www.reddit.com/r/haskell/comments/67l9so/currying_a_typelevel_list/dgrghxz/
@@ -593,31 +676,27 @@
 --   and the response body. This is needed so that users can
 --   enumerate over all the routes.
 data Constructed :: ([Type] -> [Param] -> Bodiedness -> Type -> Type) -> Type where
-  Constructed :: route captures querys request response -> Constructed route
+  Constructed :: !(route captures querys request response) -> Constructed route
 -- I dont really like the name Constructed, but I don't want to call it
 -- Some or Any since these get used a lot and a conflict would be likely.
 -- Think, think, think.
 
 mapConstructed ::
-     (forall caps qrys req resp. sub caps qrys req resp -> route cap qrys req resp)
+     (forall caps qrys req resp. sub caps qrys req resp -> route caps qrys req resp)
   -> Constructed sub
   -> Constructed route
 mapConstructed f (Constructed sub) = Constructed (f sub)
 
--- | Only includes the path. Once querystring params get added
---   to this library, this data type should not have them. This
---   type is only used internally and should not be exported.
 data Pathed :: ([Type] -> [Param] -> Bodiedness -> Type -> Type) -> Type  where
-  Pathed :: route captures querys request response -> Rec Identity captures -> Pathed route
+  Pathed :: !(route captures querys request response) -> !(Rec Identity captures) -> Pathed route
 
--- | Includes the path and the request body (and the querystring
---   params after they get added to this library).
+-- | Includes the route, path, query parameters, and request body.
 data Prepared :: ([Type] -> [Param] -> Bodiedness -> Type -> Type) -> Type -> Type where
   Prepared ::
-       route captures querys request response
-    -> Rec Identity captures
-    -> Rec Parameter querys
-    -> RequestBody Identity request
+       !(route captures querys request response)
+    -> !(Rec Identity captures)
+    -> !(Rec Parameter querys)
+    -> !(RequestBody Identity request)
     -> Prepared route response
 
 -- | Only needed to implement 'parseWith'. Most users do not need this.
@@ -625,20 +704,27 @@
 --   then you will need this.
 data Concealed :: ([Type] -> [Param] -> Bodiedness -> Type -> Type) -> Type where
   Concealed ::
-       route captures querys request response
-    -> Rec Identity captures
-    -> Rec Parameter querys
-    -> RequestBody Identity request
+       !(route captures querys request response)
+    -> !(Rec Identity captures)
+    -> !(Rec Parameter querys)
+    -> !(RequestBody Identity request)
     -> Concealed route
 
 -- | Conceal the response type.
 conceal :: Prepared route response -> Concealed route
 conceal (Prepared route caps querys req) = Concealed route caps querys req
 
+concealedToPrepared
+  :: forall route a
+  .  Concealed route
+  -> (forall resp. Prepared route resp -> a)
+  -> a
+concealedToPrepared (Concealed route caps qrys req) f = f (Prepared route caps qrys req)
+
 -- | The HTTP content type and body.
 data Content = Content
-  { contentType :: N.MediaType
-  , contentData :: LBS.ByteString
+  { contentType :: !N.MediaType
+  , contentData :: !LBS.ByteString
   } deriving (Show,Eq,Ord)
 
 -- | Only promoted version used.
@@ -648,9 +734,9 @@
 
 data IxedRouter :: ([Type] -> [Param] -> Bodiedness -> Type -> Type) -> Nat -> Type where
   IxedRouter ::
-       HashMap T.Text (IxedRouter route n)
-    -> Maybe (IxedRouter route ('S n))
-    -> HashMap T.Text [IxedResponder route n] -- Should be either zero or one, more than one means that there are trivially overlapped routes
+       !(HashMap T.Text (IxedRouter route n))
+    -> !(Maybe (IxedRouter route ('S n)))
+    -> !(HashMap T.Text [IxedResponder route n]) -- Should be either zero or one, more than one means that there are trivially overlapped routes
     -> IxedRouter route n
 
 -- | This monoid instance is provided so that we can
@@ -662,35 +748,38 @@
 --   routes were overlapped.
 instance Monoid (IxedRouter route n) where
   mempty = IxedRouter HM.empty Nothing HM.empty
-  mappend = unionIxedRouter
+  mappend = (SG.<>)
 
+instance SG.Semigroup (IxedRouter route n) where
+  (<>) = unionIxedRouter
+
 data IxedResponder :: ([Type] -> [Param] -> Bodiedness -> Type -> Type) -> Nat -> Type where
   IxedResponder ::
-       route captures query request response
-    -> IxedRec CaptureDecoding n captures
+       !(route captures query request response)
+    -> !(IxedRec CaptureDecoding n captures)
     -> IxedResponder route n
 
 data IxedRec :: (k -> Type) -> Nat -> [k] -> Type where
   IxedRecNil :: IxedRec f 'Z '[]
-  IxedRecCons :: !(f r) -> IxedRec f n rs -> IxedRec f ('S n) (r ': rs)
+  IxedRecCons :: !(f r) -> !(IxedRec f n rs) -> IxedRec f ('S n) (r ': rs)
 
 data Vec :: Nat -> Type -> Type where
   VecNil :: Vec 'Z a
-  VecCons :: !a -> Vec n a -> Vec ('S n) a
+  VecCons :: !a -> !(Vec n a) -> Vec ('S n) a
 
 data IxedPath :: (Type -> Type) -> Nat -> [Type] -> Type where
   IxedPathNil :: IxedPath f 'Z '[]
-  IxedPathCapture :: f a -> IxedPath f n as -> IxedPath f ('S n) (a ': as)
-  IxedPathMatch :: T.Text -> IxedPath f n a -> IxedPath f n a
+  IxedPathCapture :: !(f a) -> !(IxedPath f n as) -> IxedPath f ('S n) (a ': as)
+  IxedPathMatch :: !T.Text -> !(IxedPath f n a) -> IxedPath f n a
 
 data LenPath :: Nat -> Type where
   LenPathNil :: LenPath 'Z
-  LenPathCapture :: LenPath n -> LenPath ('S n)
-  LenPathMatch :: T.Text -> LenPath n -> LenPath n
+  LenPathCapture :: !(LenPath n) -> LenPath ('S n)
+  LenPathMatch :: !T.Text -> !(LenPath n) -> LenPath n
 
 -- Assumes length is in penultimate position.
 data HideIx :: (Nat -> k -> Type) -> k -> Type where
-  HideIx :: f n a -> HideIx f a
+  HideIx :: !(f n a) -> HideIx f a
 
 -- toIxedRec :: Rec f xs -> HideIx (IxedRec f) xs
 -- toIxedRec RNil = HideIx IxedRecNil
diff --git a/src/Trasa/Core/Implicit.hs b/src/Trasa/Core/Implicit.hs
new file mode 100644
--- /dev/null
+++ b/src/Trasa/Core/Implicit.hs
@@ -0,0 +1,67 @@
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE TypeFamilies #-}
+module Trasa.Core.Implicit
+  (
+    HasMeta(..)
+  , prepare
+  , link
+  , parse
+  , EnumerableRoute(..)
+  , router
+  ) where
+
+import Data.Kind (Type)
+
+import Trasa.Core
+
+class HasMeta route where
+  type CaptureStrategy route :: Type -> Type
+  type CaptureStrategy route = CaptureCodec
+  type QueryStrategy route :: Type -> Type
+  type QueryStrategy route = CaptureCodec
+  type RequestBodyStrategy route :: Type -> Type
+  type RequestBodyStrategy route = Many BodyCodec
+  type ResponseBodyStrategy route :: Type -> Type
+  type ResponseBodyStrategy route = Many BodyCodec
+  meta
+    :: route caps qrys req resp
+    -> Meta (CaptureStrategy route) (QueryStrategy route) (RequestBodyStrategy route) (ResponseBodyStrategy route) caps qrys req resp
+
+prepare
+  :: HasMeta route
+  => route captures queries request response
+  -> Arguments captures queries request (Prepared route response)
+prepare = prepareWith meta
+
+link
+  :: (HasMeta route, HasCaptureEncoding (CaptureStrategy route), HasCaptureEncoding (QueryStrategy route))
+  => Prepared route response
+  -> Url
+link = linkWith toMeta
+  where
+    toMeta route = m
+      { metaPath = mapPath captureEncoding (metaPath m)
+      , metaQuery = mapQuery captureEncoding (metaQuery m)
+      }
+      where m = meta route
+
+parse
+  :: ( HasMeta route
+     , HasCaptureDecoding (CaptureStrategy route)
+     , HasCaptureDecoding (QueryStrategy route)
+     , RequestBodyStrategy route ~ Many strat
+     , HasBodyDecoding strat
+     , EnumerableRoute route )
+  => Method -- ^ Request Method
+  -> Url -- ^ Everything after the authority
+  -> Maybe Content -- ^ Request content type and body
+  -> Either TrasaErr (Concealed route)
+parse = parseWith (mapMetaQuery captureDecoding . mapMetaRequestBody (mapMany bodyDecoding) . meta) router
+
+class EnumerableRoute route where
+  enumerateRoutes :: [Constructed route]
+
+router
+  :: (HasMeta route, HasCaptureDecoding (CaptureStrategy route), EnumerableRoute route)
+  => Router route
+router = routerWith (mapMetaPath captureDecoding . meta) enumerateRoutes
diff --git a/src/Trasa/Tutorial.hs b/src/Trasa/Tutorial.hs
--- a/src/Trasa/Tutorial.hs
+++ b/src/Trasa/Tutorial.hs
@@ -36,13 +36,6 @@
 --   IncrementR :: Route '[Counter] '[] 'Bodyless Int
 --   QueryR :: Route '[Counter] '[]Bodyless Int
 --   TotalR :: Route '[] '[] 'Bodyless Int
--- data Meta captures querys request response = Meta
---   { metaPath :: Path CaptureCodec captures
---   , metaQuery :: Rec (Query CaptureCodec) querys
---   , metaRequestBody :: RequestBody BodyCodec request
---   , metaResponseBody :: ResponseBody BodyCodec response
---   , metaMethod :: Method
---   }
 -- int :: CaptureCodec Int
 -- int = showReadCaptureCodec
 -- counter :: CaptureCodec Counter
@@ -51,8 +44,8 @@
 -- bodyUnit = BodyCodec (pure "text/plain") (const "") (const (Right ()))
 -- bodyInt :: BodyCodec Int
 -- bodyInt = showReadBodyCodec
--- meta :: Route captures querys request response -> Meta captures querys request response
--- meta x = case x of
+-- meta :: Route captures querys request response -> MetaCodec captures querys request response
+-- meta x = metaBuilderToMetaCodec $ case x of
 --   AssignR -> Meta
 --     (match "assign" ./ capture counter ./ match "to" ./ capture int ./ end)
 --     qend
@@ -75,14 +68,13 @@
 -- @trasa@ exports and partially apply them to the route metadata that
 -- we have created. We can start with prepare and link:
 --
--- >>> prepare = prepareWith (metaPath . meta) (metaQuery . meta) (metaRequestBody . meta)
+-- >>> prepare = prepareWith meta
 -- >>> :t prepare
 -- prepare
 --   :: Route captures query request response
 --      -> Arguments captures query request (Prepared Route response)
 -- >>> :{
--- link = linkWith (mapPath captureCodecToCaptureEncoding . metaPath . meta)
---                 (mapQuery captureCodecToCaptureEncoding . metaQuery . meta)
+-- link = linkWith (metaCodecToMetaClient . meta)
 -- :}
 --
 -- >>> :t link
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -1,13 +1,17 @@
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE InstanceSigs #-}
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE KindSignatures #-}
 {-# LANGUAGE GADTs #-}
+{-# LANGUAGE TypeFamilies #-}
 {-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE TypeApplications #-}
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE FlexibleContexts #-}
 
 import Text.Read (readMaybe)
 import Trasa.Core
+import Trasa.Core.Implicit
 import qualified Trasa.Method as M
 import Data.Vinyl
 import Data.Kind (Type)
@@ -27,10 +31,10 @@
 main = do
   putStrLn "\nRUNNING DOCTESTS"
   doctest
-    [ "src/Trasa"
+    [ "src"
     ]
   putStrLn "\nPRETTY ROUTER"
-  putStrLn (prettyRouter router)
+  putStrLn (prettyRouter (router @Route))
   putStrLn "\nRUNNING OTHER TESTS"
   defaultMain tests
 
@@ -51,11 +55,14 @@
   , testCase "link left pad route"
       $ link (prepare LeftPadR 5 "foo") @?= decodeUrl "/pad/left/5"
   , testCase "parse hello route"
-      $ parse "/hello" Nothing @?= Right (conceal (prepare HelloR))
+      $ parseUrl "/hello" @?= Right (conceal (prepare HelloR))
   , testCase "parse addition route"
-      $ parse "/add/6/3"  Nothing @?= Right (conceal (prepare AdditionR 6 3 Nothing))
+      $ parseUrl "/add/6/3" @?= Right (conceal (prepare AdditionR 6 3 Nothing))
   ]
 
+parseUrl :: T.Text -> Either TrasaErr (Concealed Route)
+parseUrl url = parse "GET" (decodeUrl url) Nothing
+
 data Route :: [Type] -> [Param] -> Bodiedness -> Type -> Type where
   EmptyR :: Route '[] '[] Bodyless Int
   HelloR :: Route '[] '[] Bodyless Int
@@ -65,77 +72,52 @@
   TrickyOneR :: Route '[Int] '[] Bodyless String
   TrickyTwoR :: Route '[Int,Int] '[] Bodyless String
 
-prepare :: Route cs qs rq rp -> Arguments cs qs rq (Prepared Route rp)
-prepare = prepareWith (metaPath . meta) (metaQuery . meta) (metaRequestBody . meta)
-
-link :: Prepared Route rp -> Url
-link = linkWith
-  (mapPath (CaptureEncoding . captureCodecEncode) . metaPath . meta)
-  (mapQuery captureCodecToCaptureEncoding . metaQuery . meta)
-
-parse :: T.Text -> Maybe Content -> Either TrasaErr (Concealed Route)
-parse url = parseWith
-  (mapQuery captureCodecToCaptureDecoding . metaQuery . meta)
-  (mapRequestBody (Many . pure . bodyCodecToBodyDecoding) . metaRequestBody . meta)
-  router
-  "get"
-  (decodeUrl url)
-
-allRoutes :: [Constructed Route]
-allRoutes =
-  [ Constructed HelloR
-  , Constructed AdditionR
-  , Constructed IdentityR
-  , Constructed LeftPadR
-  , Constructed TrickyOneR
-  , Constructed TrickyTwoR
-  , Constructed EmptyR
-  ]
-
-router :: Router Route
-router = routerWith
-  (metaMethod . meta)
-  (mapPath (CaptureDecoding . captureCodecDecode) . metaPath . meta)
-  allRoutes
-
-data Meta ps qs rq rp = Meta
-  { metaPath :: Path CaptureCodec ps
-  , metaQuery :: Rec (Query CaptureCodec) qs
-  , metaRequestBody :: RequestBody BodyCodec rq
-  , metaResponseBody :: ResponseBody BodyCodec rp
-  , metaMethod :: Method
-  }
+instance EnumerableRoute Route where
+  enumerateRoutes =
+    [ Constructed HelloR
+    , Constructed AdditionR
+    , Constructed IdentityR
+    , Constructed LeftPadR
+    , Constructed TrickyOneR
+    , Constructed TrickyTwoR
+    , Constructed EmptyR
+    ]
 
-meta :: Route ps qs rq rp -> Meta ps qs rq rp
-meta x = case x of
-  EmptyR -> Meta
-    end
-    qend
-    bodyless (resp bodyInt) M.get
-  HelloR -> Meta
-    (match "hello" ./ end)
-    qend
-    bodyless (resp bodyInt) M.get
-  AdditionR -> Meta
-    (match "add" ./ capture int ./ capture int ./ end)
-    (optional "more" int .& qend)
-    bodyless (resp bodyInt) M.get
-  IdentityR -> Meta
-    (match "identity" ./ capture string ./ end)
-    qend
-    bodyless (resp bodyString) M.get
-  LeftPadR -> Meta
-    (match "pad" ./ match "left" ./ capture int ./ end)
-    qend
-    (body bodyString) (resp bodyString) M.get
-  TrickyOneR -> Meta
-    (match "tricky" ./ capture int ./ match "one" ./ end)
-    qend
-    bodyless (resp bodyString) M.get
-  TrickyTwoR -> Meta
-    (capture int ./ capture int ./ match "two" ./ end)
-    qend
-    bodyless (resp bodyString) M.get
+instance HasMeta Route where
+  type CaptureStrategy Route = CaptureCodec
+  type QueryStrategy Route = CaptureCodec
+  type RequestBodyStrategy Route = Many BodyCodec
+  type ResponseBodyStrategy Route = Many BodyCodec
+  meta :: Route ps qs rq rp -> MetaCodec ps qs rq rp
+  meta x = metaBuilderToMetaCodec $ case x of
+    EmptyR -> Meta
+      end
+      qend
+      bodyless (resp bodyInt) M.get
+    HelloR -> Meta
+      (match "hello" ./ end)
+      qend
+      bodyless (resp bodyInt) M.get
+    AdditionR -> Meta
+      (match "add" ./ capture int ./ capture int ./ end)
+      (optional "more" int .& qend)
+      bodyless (resp bodyInt) M.get
+    IdentityR -> Meta
+      (match "identity" ./ capture string ./ end)
+      qend
+      bodyless (resp bodyString) M.get
+    LeftPadR -> Meta
+      (match "pad" ./ match "left" ./ capture int ./ end)
+      qend
+      (body bodyString) (resp bodyString) M.get
+    TrickyOneR -> Meta
+      (match "tricky" ./ capture int ./ match "one" ./ end)
+      qend
+      bodyless (resp bodyString) M.get
+    TrickyTwoR -> Meta
+      (capture int ./ capture int ./ match "two" ./ end)
+      qend
+      bodyless (resp bodyString) M.get
 
 int :: CaptureCodec Int
 int = CaptureCodec (T.pack . show) (readMaybe . T.unpack)
@@ -164,7 +146,7 @@
     RequestBodyAbsent -> True
   )
   ==>
-  Right c == parse (encodeUrl (link (Prepared route captures querys reqBody))) Nothing
+  Right c == parseUrl (encodeUrl (link (Prepared route captures querys reqBody)))
 
 -- This instance is defined only so that the test suite can do
 -- its job. It not not neccessary or recommended to write this
diff --git a/trasa.cabal b/trasa.cabal
--- a/trasa.cabal
+++ b/trasa.cabal
@@ -1,21 +1,20 @@
 name: trasa
-version: 0.2
+version: 0.3
 synopsis: Type Safe Web Routing
 homepage: https://github.com/haskell-trasa/trasa#readme
 license: BSD3
 license-file: LICENSE
-author: Andrew Martin
-maintainer: andrew.thaddeus@gmail.com
-copyright: 2017 Andrew Martin
+author: Kyle McKean
+maintainer: mckean.kylej@gmail.com
+copyright: 2017 Kyle McKean
 category: Web
 build-type: Simple
 cabal-version: >=1.10
 description:
-  This library is a solution for http-based routing and dispatch. It's
-  goals are similar to the goals of `servant`. However, `trasa` relies
-  on very different mechanisms to accomplish this. There are no typeclasses
-  in this library, and there is a single closed type family that is provided
-  as a convenience. All of the real work is accomplish with GADTs, 
+  This library is a solution for http-based routing and dispatch. Its
+  goals are similar to the goals of `servant`, however, `trasa` relies
+  on very different mechanisms to accomplish those goals. All typeclasses
+  in this library are optional. All of the real work is accomplished with GADTs, 
   universal quantification, and plain old haskell data types.
 
 library
@@ -26,16 +25,17 @@
     Trasa.Codec
     Trasa.Error
     Trasa.Core
+    Trasa.Core.Implicit
     Trasa.Tutorial
   build-depends:
       base >= 4.7 && < 5
     , bytestring == 0.10.*
     , binary == 0.8.*
     , text == 1.2.*
-    , vinyl == 0.5.*
+    , vinyl >= 0.5 && < 0.9
     , hashable == 1.2.*
-    , http-types == 0.9.*
-    , http-media == 0.6.*
+    , http-types >= 0.9
+    , http-media >= 0.6 && < 0.8
     , unordered-containers >= 0.2 && < 0.3
   default-language:    Haskell2010
 
