diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Author name here (c) 2017
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Author name here nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,1 @@
+# servant-side-loaded
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import           Distribution.Simple
+main = defaultMain
diff --git a/servant-zeppelin-client.cabal b/servant-zeppelin-client.cabal
new file mode 100644
--- /dev/null
+++ b/servant-zeppelin-client.cabal
@@ -0,0 +1,71 @@
+name:                servant-zeppelin-client
+version:             0.1.0.0
+homepage:            https://github.com/martyall/servant-zeppelin#readme
+license:             BSD3
+license-file:        LICENSE
+author:              Martin Allen, Ben Weitzman
+maintainer:          martin[dot]allen26[at]gmail.com
+synopsis:            Client library for servant-zeppelin combinators.
+copyright:           2017 Martin Allen, Ben Weitzman
+category:            Web
+build-type:          Simple
+extra-source-files:  README.md
+cabal-version:       >=1.10
+
+library
+  hs-source-dirs:      src
+  exposed-modules:     Servant.Zeppelin.Client
+  build-depends:       base >= 4.7 && < 5
+                     , aeson
+                     , singletons
+                     , servant
+                     , servant-client >= 0.10
+                     , servant-zeppelin
+                     , text
+  default-language:    Haskell2010
+  default-extensions:  GADTs
+                     , KindSignatures
+                     , FlexibleInstances
+                     , FlexibleContexts
+                     , OverloadedStrings
+                     , PolyKinds
+                     , RankNTypes
+                     , DataKinds
+                     , TypeOperators
+                     , TypeFamilies
+                     , TypeApplications
+                     , TypeInType
+                     , MultiParamTypeClasses
+                     , ScopedTypeVariables
+                     , ConstraintKinds
+                     
+  ghc-options: -Wall
+
+test-suite servant-zeppelin-cient-test
+  type:                exitcode-stdio-1.0
+  hs-source-dirs:      test
+  main-is:             Spec.hs
+  build-depends:       base
+                     , aeson
+                     , data-default-class
+                     , hspec
+                     , http-client
+                     , mtl
+                     , QuickCheck
+                     , servant
+                     , servant-server
+                     , servant-client
+                     , servant-zeppelin
+                     , servant-zeppelin-server
+                     , servant-zeppelin-client
+                     , singletons
+                     , string-conversions
+                     , warp
+                     , wai-extra
+  ghc-options:         -Wall -threaded -rtsopts -with-rtsopts=-N
+  default-language:    Haskell2010
+  other-modules:       Servant.Zeppelin.ClientSpec
+
+source-repository head
+  type:     git
+  location: https://github.com/martyall/servant-zeppelin
diff --git a/src/Servant/Zeppelin/Client.hs b/src/Servant/Zeppelin/Client.hs
new file mode 100644
--- /dev/null
+++ b/src/Servant/Zeppelin/Client.hs
@@ -0,0 +1,151 @@
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+{-# OPTIONS_GHC -fno-warn-unused-binds #-}
+{-# LANGUAGE ExplicitNamespaces   #-}
+{-# LANGUAGE UndecidableInstances #-}
+
+module Servant.Zeppelin.Client
+  ( projectDependency
+  , DepClient(..)
+  -- * Re-exports
+  , SideLoaded(..)
+  , SideLoad
+  , SBool
+  ) where
+
+import           Data.Aeson
+import           Data.Functor.Identity
+import           Data.Kind
+import           Data.Proxy
+import           Data.Singletons.Prelude hiding ((:>))
+import           Data.Singletons.TypeLits
+import qualified Data.Text                as T
+import           Servant.API
+import           Servant.Client
+import           Servant.Common.Req
+
+import           Servant.Zeppelin
+import           Servant.Zeppelin.Internal.Types
+
+--------------------------------------------------------------------------------
+-- FromJSON Instances
+--------------------------------------------------------------------------------
+
+instance FromJSON (DependencyList Identity '[] '[]) where
+  parseJSON (Object _) = return NilDeps
+  -- this can't actually happen.
+  parseJSON _ = fail "Nil dependency list should be the empty object."
+
+instance ( FromJSON (DependencyList Identity ds ds)
+         , KnownSymbol (NamedDependency d)
+         , FromJSON d
+         ) => FromJSON (DependencyList Identity (d : ds) (d : ds)) where
+  parseJSON o@(Object v) = do
+    d <- v .: (T.pack . symbolVal $ Proxy @(NamedDependency d))
+    ds <- parseJSON o
+    return $ d :&: ds
+  parseJSON _  = fail "Failed to parse a dependency."
+
+instance ( FromJSON (DependencyList Identity ds ds)
+         , FromJSON a
+         ) => FromJSON (SideLoaded a ds) where
+  parseJSON (Object v') = do
+        a <- v' .: "data"
+        ds <- v' .: "dependencies"
+        return $ SideLoaded a ds
+  parseJSON _ = fail "Could not parse dependencies."
+
+--------------------------------------------------------------------------------
+-- HList Accessors
+--------------------------------------------------------------------------------
+
+-- | 'ProjectDependency' @bs b' allows you to project to a dependency type from
+-- a 'DependencyList'. If 'b' in 'bs', type inference is used to project to 'b'.
+-- For example:
+--
+-- > let (SideLoaded a deps) = s :: SideLoaded Album '[Person, [Photo]]
+-- > personId . projectDependency $ deps :: PersonId
+class ProjectDependency bs b where
+  projectDependency :: forall fs m . DependencyList m bs fs -> b
+
+instance {-# OVERLAPPING #-} ProjectDependency (b : bs) b where
+  projectDependency (b :&: _) = b
+
+instance {-# OVERLAPPABLE #-} ProjectDependency bs b =>  ProjectDependency (a : bs) b where
+  projectDependency (_ :&: bs ) = projectDependency bs
+
+--------------------------------------------------------------------------------
+-- Dependent Client
+--------------------------------------------------------------------------------
+
+-- | 'DependentClient' is a wrapper around a dependently typed function that when
+-- given a singleton 'STrue' has return type 'SideLoaded' @a deps@, and
+-- when given 'SFalse' has return type @a@. For example:
+--
+-- > data Person =
+-- >   Person { personId   :: PersonId
+-- >          , personName :: String
+-- >          } deriving (Eq, Show, Generic)
+-- >
+-- > instance FromJSON Person
+-- >
+-- > data Photo =
+-- >   Photo { photoId      :: PhotoId
+-- >         , photoCaption :: String
+-- >         , artistId     :: PersonId
+-- >         } deriving (Eq, Show, Generic)
+-- >
+-- > instance FromJSON Photo
+-- >
+-- > data Album =
+-- >   Album { albumId     :: AlbumId
+-- >         , albumName   :: String
+-- >         , albumOwner  :: PersonId
+-- >         , albumPhotos :: [PhotoId]
+-- >         } deriving (Eq, Show, Generic)
+-- >
+-- > instance FromJSON Album
+-- >
+-- > type API = "albums" :> Capture "albumId" AlbumId
+-- >                     :> Get '[JSON, PlainText] Album
+-- >                     :> SideLoad '[Person, [Photo]]
+-- >
+-- > type AlbumDeps =  '[Person, [Photo]]
+-- >
+-- > getAlbumClientFull :: Manager
+-- >                    -> BaseUrl
+-- >                    -> AlbumId
+-- >                    -> IO (Either ServantError (SideLoaded Album AlbumDeps))
+-- > getAlbumClientFull m burl aid =
+-- >   flip runClientM (ClientEnv m burl) $
+-- >     runDepClient (client api aid) STrue
+-- >
+-- > getAlbumClient :: Manager
+-- >                -> BaseUrl
+-- >                -> AlbumId
+-- >                -> IO (Either ServantError Album)
+-- > getAlbumClient m burl aid =
+-- >   flip runClientM (ClientEnv m burl) $
+-- >     runDepClient (client api aid) SFalse
+
+newtype DepClient (ix :: Bool -> *) (f :: Bool ~> Type) =
+    DepClient {runDepClient :: forall (b :: Bool) . ix b -> Client (Apply f b)}
+
+data SideLoadTerminal :: method -> status -> cts -> a -> deps -> (Bool ~> Type) where
+  SideLoadTerminal :: SideLoadTerminal method status cts a deps b
+
+type instance Apply (SideLoadTerminal method status cts a deps) b =
+  If b (Verb method status cts (SideLoaded a deps)) (Verb method status cts a)
+
+instance {-# OVERLAPPABLE #-}
+         ( MimeUnrender JSON a
+         , MimeUnrender JSON (SideLoaded a deps)
+         , ReflectMethod method
+         ) => HasClient (Verb method status cts a :> SideLoad deps) where
+
+  type Client (Verb method status cts a :> SideLoad deps) = DepClient SBool (SideLoadTerminal method status cts a deps)
+  clientWithRoute Proxy req = DepClient $ \sb ->
+    case sb of
+      STrue -> let req' = appendToQueryString "sideload" (Just "true") req
+               in snd <$> performRequestCT (Proxy @JSON) method req'
+      SFalse -> snd <$> performRequestCT (Proxy @JSON) method req
+    where method = reflectMethod (Proxy @method)
diff --git a/test/Servant/Zeppelin/ClientSpec.hs b/test/Servant/Zeppelin/ClientSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Servant/Zeppelin/ClientSpec.hs
@@ -0,0 +1,234 @@
+{-# LANGUAGE DataKinds                  #-}
+{-# LANGUAGE DeriveGeneric              #-}
+{-# LANGUAGE FlexibleContexts           #-}
+{-# LANGUAGE FlexibleInstances          #-}
+{-# LANGUAGE GADTs                      #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE MultiParamTypeClasses      #-}
+{-# LANGUAGE OverloadedStrings          #-}
+{-# LANGUAGE ScopedTypeVariables        #-}
+{-# LANGUAGE TypeApplications           #-}
+{-# LANGUAGE TypeFamilies               #-}
+{-# LANGUAGE TypeOperators              #-}
+
+{-# OPTIONS_GHC -fno-warn-unused-binds#-}
+
+module Servant.Zeppelin.ClientSpec (spec) where
+
+import           Control.Monad.Except
+import           Data.Aeson
+import           Data.Either
+import qualified Data.List                as L
+import           Data.Singletons.Prelude hiding ((:>))
+import           Data.String.Conversions
+import           GHC.Generics
+import           Servant.Server
+import           Servant.Zeppelin
+import           Servant.Zeppelin.Client
+import           Servant.Zeppelin.Server
+
+import           Network.Wai.Handler.Warp (testWithApplication)
+import           System.IO.Unsafe         (unsafePerformIO)
+
+
+import           Network.HTTP.Client      (Manager, defaultManagerSettings,
+                                           newManager)
+import           Servant
+import           Servant.Client
+import           Test.Hspec
+import           Test.QuickCheck
+
+
+spec :: Spec
+spec = do
+  hasClientSpec
+  return ()
+
+hasClientSpec :: Spec
+hasClientSpec = describe "HasClient" $ around (testWithApplication $ return app) $ do
+
+  it "succeeds when we ask for the inflated data" $ \port -> quickCheckWith qcArgs $ property
+                                                  $ \aid -> do
+    ealbum <- getAlbumClientFull mgr (BaseUrl Http "localhost" port "") aid
+    let Just (album, person, photos) = do
+          a <- getAlbumById aid
+          p <- getPersonById (albumOwner a)
+          let phs = getPhotosByIds (albumPhotos a)
+          return (a, p, phs)
+    ealbum `shouldSatisfy` isRight
+    let Right (SideLoaded a deps) = ealbum
+    person `shouldBe` projectDependency deps
+    photos `shouldBe` projectDependency deps
+    a `shouldBe` album
+
+  it "succeeds when we ask for the uninflated data" $ \port -> quickCheckWith qcArgs $ property
+                                                    $ \aid -> do
+    ealbum <- getAlbumClient mgr (BaseUrl Http "localhost" port "") aid
+    ealbum `shouldSatisfy` isRight
+    let Right album = ealbum
+        Just album' = getAlbumById aid
+    album `shouldBe` album'
+
+qcArgs :: Args
+qcArgs = stdArgs {maxSuccess = 20}
+
+--------------------------------------------------------------------------------
+-- | Client
+--------------------------------------------------------------------------------
+
+mgr :: Manager
+mgr = unsafePerformIO $ newManager defaultManagerSettings
+{-# NOINLINE mgr #-}
+
+type AlbumDeps =  '[Person, [Photo]]
+
+getAlbumClientFull :: Manager
+                   -> BaseUrl
+                   -> AlbumId
+                   -> IO (Either ServantError (SideLoaded Album AlbumDeps))
+getAlbumClientFull m burl aid =
+  flip runClientM (ClientEnv m burl) $
+    runDepClient (client api aid) STrue
+
+getAlbumClient :: Manager
+               -> BaseUrl
+               -> AlbumId
+               -> IO (Either ServantError Album)
+getAlbumClient m burl aid =
+  flip runClientM (ClientEnv m burl) $
+    runDepClient (client api aid) SFalse
+
+--------------------------------------------------------------------------------
+-- | Application
+--------------------------------------------------------------------------------
+
+type API = "albums" :> Capture "albumId" AlbumId
+                    :> Get '[JSON, PlainText] Album
+                    :> SideLoad '[Person, [Photo]]
+
+api :: Proxy API
+api = Proxy @API
+
+newtype QueryError = LookupError String
+
+newtype DB a = DB { runDB :: ExceptT QueryError IO a }
+  deriving (Functor, Applicative, Monad, MonadIO, MonadError QueryError)
+
+server :: ServerT API Handler
+server = albumHandler
+
+albumHandler :: AlbumId -> Handler Album
+albumHandler aid =
+  case L.find (\album -> albumId album == aid) albumTable of
+    Nothing    -> throwError err404
+    Just album -> return album
+
+phi :: DB :~> Handler
+phi = NT $ \ha -> do
+  ea <- liftIO . runExceptT . runDB $ ha
+  case ea of
+    Left (LookupError msg) -> throwError err404 {errBody = cs msg}
+    Right a                -> return a
+
+app :: Application
+app = serveWithContext api ctxt server
+  where
+    ctxt :: Context '[DB :~> Handler]
+    ctxt = phi :. EmptyContext
+
+--------------------------------------------------------------------------------
+-- | Data
+--------------------------------------------------------------------------------
+-- | Photo
+
+newtype PhotoId = PhotoId Int
+  deriving (Eq, Show, Num, Generic, ToJSON, FromJSON)
+
+type instance NamedDependency [Photo] = "photos"
+type instance NamedDependency [PhotoId] = "photoIds"
+
+data Photo =
+  Photo { photoId      :: PhotoId
+        , photoCaption :: String
+        , artistId     :: PersonId
+        } deriving (Eq, Show, Generic)
+
+instance ToJSON Photo
+instance FromJSON Photo
+
+photosTable :: [Photo]
+photosTable = [ Photo 1 "At the Beach." 1
+              , Photo 2 "At the Mountain." 1
+              , Photo 3 "With Friends." 1
+              , Photo 4 "Bow Wow." 2
+              ]
+
+getPhotosByIds :: [PhotoId] -> [Photo]
+getPhotosByIds pids = filter (\photo -> photoId photo `elem` pids) photosTable
+
+instance Inflatable DB [PhotoId] where
+  type Full DB [PhotoId] = [Photo]
+  inflator = return . getPhotosByIds
+
+-- | Person
+
+newtype PersonId = PersonId Int
+  deriving (Eq, Show, Num, Generic, ToJSON, FromJSON)
+
+type instance NamedDependency Person = "person"
+
+data Person =
+  Person { personId   :: PersonId
+         , personName :: String
+         } deriving (Eq, Show, Generic)
+
+instance ToJSON Person
+instance FromJSON Person
+
+personTable :: [Person]
+personTable = [ Person 1 "Alice"
+              , Person 2 "Fido"
+              ]
+
+getPersonById :: PersonId -> Maybe Person
+getPersonById pid = L.find (\person -> personId person == pid) personTable
+
+instance Inflatable DB PersonId where
+  type Full DB PersonId = Person
+  inflator pid =
+    case getPersonById pid of
+      Nothing -> throwError . LookupError $ "Could not find person with id: " <> show pid
+      Just person -> return person
+
+-- | Albums
+
+newtype AlbumId = AlbumId Int
+  deriving (Eq, Show, Num, ToJSON, FromJSON, FromHttpApiData, ToHttpApiData)
+
+instance Arbitrary AlbumId where
+  arbitrary = elements $ map AlbumId [1..3]
+
+data Album =
+  Album { albumId     :: AlbumId
+        , albumName   :: String
+        , albumOwner  :: PersonId
+        , albumPhotos :: [PhotoId]
+        } deriving (Eq, Show, Generic)
+
+instance ToJSON Album
+instance FromJSON Album
+
+albumTable :: [Album]
+albumTable = [ Album 1 "Vacations" 1 [1,2]
+             , Album 2 "In the City" 1 [3]
+             , Album 3 "Howl" 2 [4]
+             ]
+
+getAlbumById :: AlbumId -> Maybe Album
+getAlbumById aid = L.find (\album -> albumId album == aid) albumTable
+
+instance HasDependencies DB Album '[PersonId, [PhotoId]] where
+  getDependencies (Album _ _ owner pIds) = owner :&: pIds :&: NilDeps
+
+instance MimeRender PlainText Album where
+  mimeRender _ _ = "Album"
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,2 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+
