diff --git a/src/Gargantext/API.hs b/src/Gargantext/API.hs
index 0f939f59a9752fff57a8876e15b67bf2d3d540c9..7764535cedfd1bb61e2e79f8417c656d9665afc3 100644
--- a/src/Gargantext/API.hs
+++ b/src/Gargantext/API.hs
@@ -24,6 +24,7 @@ Thanks @yannEsposito for this.
 {-# LANGUAGE NoImplicitPrelude    #-}
 {-# LANGUAGE DataKinds            #-}
 {-# LANGUAGE DeriveGeneric        #-}
+{-# LANGUAGE FlexibleContexts     #-}
 {-# LANGUAGE FlexibleInstances    #-}
 {-# LANGUAGE OverloadedStrings    #-}
 {-# LANGUAGE TemplateHaskell      #-}
@@ -37,14 +38,14 @@ module Gargantext.API
       where
 ---------------------------------------------------------------------
 
-import           Database.PostgreSQL.Simple (Connection)
 import           System.IO (FilePath)
 
 import           GHC.Generics (D1, Meta (..), Rep)
 import           GHC.TypeLits (AppendSymbol, Symbol)
 
 import           Control.Lens
-import Control.Monad.IO.Class (liftIO)
+import           Control.Monad.IO.Class (liftIO)
+import           Control.Monad.Reader (runReaderT)
 import           Data.Aeson.Encode.Pretty (encodePretty)
 import qualified Data.ByteString.Lazy.Char8 as BL8
 import           Data.Swagger
@@ -70,8 +71,9 @@ import           Text.Blaze.Html (Html)
 import Gargantext.Prelude
 import Gargantext.API.FrontEnd (FrontEndAPI, frontEndServer)
 
-import Gargantext.API.Auth (AuthRequest, AuthResponse, auth')
-import Gargantext.API.Node ( Roots    , roots
+import Gargantext.API.Auth (AuthRequest, AuthResponse, auth)
+import Gargantext.API.Node ( GargServer
+                           , Roots    , roots
                            , NodeAPI  , nodeAPI
                            , NodesAPI , nodesAPI
                            , GraphAPI , graphAPI
@@ -208,9 +210,6 @@ type GargAPI = "api" :> Summary "API " :> GargAPIVersion
 
 type GargAPIVersion = "v1.0" :> Summary "v1.0: " :> GargAPI'
 
-auth :: Connection -> AuthRequest -> Handler AuthResponse
-auth conn ar = liftIO $ auth' conn ar
-
 type GargAPI' =
            -- Auth endpoint
                 "auth"  :> Summary "AUTH API"
@@ -277,27 +276,24 @@ type API = SwaggerFrontAPI :<|> GargAPI :<|> Get '[HTML] Html
 
 server :: Env -> IO (Server API)
 server env = do
-  gargAPI <- serverGargAPI env
+  -- orchestrator <- scrapyOrchestrator env
   pure $  swaggerFront
-     :<|> gargAPI
+     :<|> hoistServer (Proxy :: Proxy GargAPI) (`runReaderT` env) serverGargAPI
      :<|> serverIndex
 
-serverGargAPI :: Env -> IO (Server GargAPI)
-serverGargAPI env = do
-  -- orchestrator <- scrapyOrchestrator env
-  pure $  auth     conn
-     :<|> roots    conn
-     :<|> nodeAPI  conn (Proxy :: Proxy HyperdataAny)
-     :<|> nodeAPI  conn (Proxy :: Proxy HyperdataCorpus)
-     :<|> nodeAPI  conn (Proxy :: Proxy HyperdataAnnuaire)
-     :<|> nodesAPI conn
+serverGargAPI :: GargServer GargAPI
+serverGargAPI -- orchestrator
+       =  auth
+     :<|> roots
+     :<|> nodeAPI  (Proxy :: Proxy HyperdataAny)
+     :<|> nodeAPI  (Proxy :: Proxy HyperdataCorpus)
+     :<|> nodeAPI  (Proxy :: Proxy HyperdataAnnuaire)
+     :<|> nodesAPI
      :<|> count -- TODO: undefined
-     :<|> search   conn
-     :<|> graphAPI conn -- TODO: mock
-     :<|> treeAPI  conn
+     :<|> search
+     :<|> graphAPI -- TODO: mock
+     :<|> treeAPI
   --   :<|> orchestrator
-  where
-    conn = env ^. env_conn
 
 serverIndex :: Server (Get '[HTML] Html)
 serverIndex = $(do (Just s) <- liftIO (fileTypeToFileTree (FileTypeFile "purescript-gargantext/dist/index.html"))
diff --git a/src/Gargantext/API/Auth.hs b/src/Gargantext/API/Auth.hs
index 46d8306d465b87a938d2f1b8c2da070d43e81b94..a720d2f2bae09f1cdbed89c80ce215f24d3337fc 100644
--- a/src/Gargantext/API/Auth.hs
+++ b/src/Gargantext/API/Auth.hs
@@ -22,6 +22,7 @@ Main authorisation of Gargantext are managed in this module
 {-# LANGUAGE DeriveGeneric     #-}
 {-# LANGUAGE DataKinds         #-}
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RankNTypes        #-}
 {-# LANGUAGE TemplateHaskell   #-}
 
 module Gargantext.API.Auth
@@ -31,11 +32,11 @@ import Data.Aeson.TH (deriveJSON)
 import Data.List (elem)
 import Data.Swagger
 import Data.Text (Text, reverse)
-import Database.PostgreSQL.Simple (Connection)
 import GHC.Generics (Generic)
 import Gargantext.Core.Utils.Prefix (unPrefix)
 import Gargantext.Database.Root (getRoot)
 import Gargantext.Database.Types.Node (NodePoly(_node_id))
+import Gargantext.Database.Utils (Cmd)
 import Gargantext.Prelude hiding (reverse)
 import Test.QuickCheck (elements, oneof)
 import Test.QuickCheck.Arbitrary (Arbitrary, arbitrary)
@@ -81,17 +82,17 @@ arbitraryUsername = ["gargantua", "user1", "user2"]
 arbitraryPassword :: [Password]
 arbitraryPassword = map reverse arbitraryUsername
 
-checkAuthRequest :: Username -> Password -> Connection -> IO CheckAuth
-checkAuthRequest u p c
+checkAuthRequest :: Username -> Password -> Cmd err CheckAuth
+checkAuthRequest u p
   | not (u `elem` arbitraryUsername) = pure InvalidUser
   | u /= reverse p = pure InvalidPassword
   | otherwise = do
-      muId <- getRoot u c
+      muId <- getRoot u
       pure $ maybe InvalidUser (Valid "token" . _node_id) $ head muId
 
-auth' :: Connection -> AuthRequest -> IO AuthResponse
-auth' c (AuthRequest u p) = do
-  checkAuthRequest' <- checkAuthRequest u p c
+auth :: AuthRequest -> Cmd err AuthResponse
+auth (AuthRequest u p) = do
+  checkAuthRequest' <- checkAuthRequest u p
   case checkAuthRequest' of
     InvalidUser     -> pure $ AuthResponse Nothing (Just $ AuthInvalid "Invalid user")
     InvalidPassword -> pure $ AuthResponse Nothing (Just $ AuthInvalid "Invalid password")
diff --git a/src/Gargantext/API/Count.hs b/src/Gargantext/API/Count.hs
index cca261095a9201027f8530dca78afaa8d630e273..3fddf7f2bb7e165ba0f5f9c54d03f0f62f89b585 100644
--- a/src/Gargantext/API/Count.hs
+++ b/src/Gargantext/API/Count.hs
@@ -146,5 +146,5 @@ instance ToSchema Count
 --    arbitrary = Count <$> arbitrary <*> arbitrary <*> arbitrary
 
 -----------------------------------------------------------------------
-count :: Query -> Handler Counts
+count :: Monad m => Query -> m Counts
 count _ = undefined
diff --git a/src/Gargantext/API/Ngrams.hs b/src/Gargantext/API/Ngrams.hs
index fdd31af68ae777efe521c667a5d9d4edc1d6fafb..b53301fff2f7e033d35196d9a6661b4ed98cf3e7 100644
--- a/src/Gargantext/API/Ngrams.hs
+++ b/src/Gargantext/API/Ngrams.hs
@@ -28,6 +28,7 @@ add get
 {-# LANGUAGE TypeOperators     #-}
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE RankNTypes        #-}
 {-# OPTIONS -fno-warn-orphans #-}
 
 module Gargantext.API.Ngrams
@@ -35,6 +36,7 @@ module Gargantext.API.Ngrams
 
 import Prelude (round)
 -- import Gargantext.Database.Schema.User  (UserId)
+import Data.Functor (($>))
 import Data.Patch.Class (Replace, replace)
 --import qualified Data.Map.Strict.Patch as PM
 import Data.Monoid
@@ -42,24 +44,26 @@ import Data.Monoid
 import Data.Set (Set)
 import qualified Data.Set as Set
 --import Data.Maybe (catMaybes)
---import qualified Data.Map.Strict as DM
+-- import qualified Data.Map.Strict as DM
+import Data.Map.Strict (Map)
 --import qualified Data.Set as Set
-import Control.Lens ((.~))
-import Control.Monad.IO.Class (MonadIO, liftIO)
+import Control.Lens (Prism', prism', (.~), (#))
+import Control.Monad (guard)
+import Control.Monad.Error.Class (MonadError, throwError)
 import Data.Aeson
 import Data.Aeson.TH (deriveJSON)
 import Data.Either(Either(Left))
 import Data.Map (lookup)
 import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap
-import Data.Swagger
+import Data.Swagger hiding (version)
 import Data.Text (Text)
-import Database.PostgreSQL.Simple (Connection)
 import GHC.Generics (Generic)
 --import Gargantext.Core.Types.Main (Tree(..))
 import Gargantext.Core.Utils.Prefix (unPrefix)
 import Gargantext.Database.Types.Node (NodeType(..))
-import Gargantext.Database.Schema.Node (defaultList)
+import Gargantext.Database.Schema.Node (defaultList, HasNodeError)
 import qualified Gargantext.Database.Schema.Ngrams as Ngrams
+import Gargantext.Database.Utils (Cmd)
 import Gargantext.Prelude
 import Gargantext.Core.Types (ListType(..), ListId, CorpusId, Limit, Offset)
 import Prelude (Enum, Bounded, minBound, maxBound)
@@ -184,27 +188,18 @@ instance ToSchema  NgramsPatch
 instance Arbitrary NgramsPatch where
   arbitrary = NgramsPatch <$> arbitrary <*> (replace <$> arbitrary <*> arbitrary)
 
-data NgramsIdPatch =
-     NgramsIdPatch { _nip_ngrams      :: NgramsTerm
-                   , _nip_ngramsPatch :: NgramsPatch
-                   }
-      deriving (Ord, Eq, Show, Generic)
-$(deriveJSON (unPrefix "_nip_") ''NgramsIdPatch)
-
-instance ToSchema  NgramsIdPatch
-
-instance Arbitrary NgramsIdPatch where
-  arbitrary = NgramsIdPatch <$> arbitrary <*> arbitrary
-
-                       --
 -- TODO:
 -- * This should be a Map NgramsId NgramsPatch
 -- * Patchs -> Patches
-newtype NgramsIdPatchs =
-     NgramsIdPatchs { _nip_ngramsIdPatchs :: [NgramsIdPatch] }
+newtype NgramsTablePatch =
+     NgramsTablePatch { _nip_ngramsIdPatchs :: Map NgramsTerm NgramsPatch }
       deriving (Ord, Eq, Show, Generic, Arbitrary)
-$(deriveJSON (unPrefix "_nip_") ''NgramsIdPatchs)
-instance ToSchema  NgramsIdPatchs
+$(deriveJSON (unPrefix "_nip_") ''NgramsTablePatch)
+instance ToSchema  NgramsTablePatch
+
+-- TODO: replace by mempty once we have the Monoid instance
+emptyNgramsTablePatch :: NgramsTablePatch
+emptyNgramsTablePatch = NgramsTablePatch mempty
 
 ------------------------------------------------------------------------
 ------------------------------------------------------------------------
@@ -246,22 +241,34 @@ type TableNgramsApiGet = Summary " Table Ngrams API Get"
 
 type TableNgramsApi = Summary " Table Ngrams API Change"
                       :> QueryParam "list"   ListId
-                      :> ReqBody '[JSON] NgramsIdPatchsFeed -- Versioned ...
-                      :> Put     '[JSON] NgramsIdPatchsBack -- Versioned ...
+                      :> ReqBody '[JSON] NgramsTablePatch -- (Versioned NgramsTablePatch)
+                      :> Put     '[JSON] NgramsTablePatch -- (Versioned NgramsTablePatch)
 
-type NgramsIdPatchsFeed = NgramsIdPatchs
-type NgramsIdPatchsBack = NgramsIdPatchs
+data NgramError = UnsupportedVersion
+  deriving (Show)
 
+class HasNgramError e where
+  _NgramError :: Prism' e NgramError
+
+instance HasNgramError ServantErr where
+  _NgramError = prism' make match
+    where
+      err = err500 { errBody = "NgramError: Unsupported version" }
+      make UnsupportedVersion = err
+      match e = guard (e == err) $> UnsupportedVersion
+
+ngramError :: (MonadError e m, HasNgramError e) => NgramError -> m a
+ngramError nne = throwError $ _NgramError # nne
 
 {-
-toLists :: ListId -> NgramsIdPatchs -> [(ListId, NgramsId, ListTypeId)]
+toLists :: ListId -> NgramsTablePatch -> [(ListId, NgramsId, ListTypeId)]
 -- toLists = undefined
 toLists lId np = [ (lId,ngId,listTypeId lt) | map (toList lId) (_nip_ngramsIdPatchs np) ]
 
 toList :: ListId -> NgramsIdPatch -> (ListId, NgramsId, ListTypeId)
 toList = undefined
 
-toGroups :: ListId -> (NgramsPatch -> Set NgramsId) -> NgramsIdPatchs -> [NodeNgramsNgrams]
+toGroups :: ListId -> (NgramsPatch -> Set NgramsId) -> NgramsTablePatch -> [NodeNgramsNgrams]
 toGroups lId addOrRem ps = concat $ map (toGroup lId addOrRem) $ _nip_ngramsIdPatchs ps
 
 toGroup :: ListId -> (NgramsPatch -> Set NgramsId) -> NgramsIdPatch -> [NodeNgramsNgrams]
@@ -271,26 +278,37 @@ toGroup lId addOrRem (NgramsIdPatch ngId patch)  =
 
 -}
 
-tableNgramsPatch :: Connection -> CorpusId -> Maybe ListId -> NgramsIdPatchsFeed -> IO NgramsIdPatchsBack
-tableNgramsPatch = undefined 
+-- Apply the given patch to the DB and returns the patch to be applied on the
+-- cilent.
+-- TODO:
+-- In this perliminary version the OT aspect is missing, therefore the version
+-- number is always 1 and the returned patch is always empty.
+tableNgramsPatch :: (HasNgramError err, HasNodeError err)
+                 => CorpusId -> Maybe ListId
+                 -- -> Versioned NgramsTablePatch
+                 -- -> Cmd err (Versioned NgramsTablePatch)
+                 -> any
+                 -> Cmd err any
+tableNgramsPatch _ _ _ = undefined
 {-
-tableNgramsPatch conn corpusId maybeList patchs = do
-  listId <- case maybeList of
-              Nothing      -> defaultList conn corpusId
-              Just listId' -> pure listId'
-  _ <- ngramsGroup' conn Add $ toGroups listId _np_add_children patchs
-  _ <- ngramsGroup' conn Del $ toGroups listId _np_rem_children patchs
-  _ <- updateNodeNgrams conn (toLists listId patchs)
-  pure (NgramsIdPatchs [])
+tableNgramsPatch corpusId maybeList (Versioned version _patch) = do
+  when (version /= 1) $ ngramError UnsupportedVersion
+  _listId <- maybe (defaultList corpusId) pure maybeList
+{-
+  _ <- ngramsGroup' Add $ toGroups listId _np_add_children patch
+  _ <- ngramsGroup' Del $ toGroups listId _np_rem_children patch
+  _ <- updateNodeNgrams (toLists listId patch)
+-}
+  pure $ Versioned 1 emptyNgramsTablePatch
 -}
 
 -- | TODO Errors management
 --  TODO: polymorphic for Annuaire or Corpus or ...
-getTableNgrams :: MonadIO m
-               => Connection -> CorpusId -> Maybe TabType
+getTableNgrams :: HasNodeError err
+               => CorpusId -> Maybe TabType
                -> Maybe ListId -> Maybe Limit -> Maybe Offset
-               -> m NgramsTable
-getTableNgrams c cId maybeTabType maybeListId mlimit moffset = liftIO $ do
+               -> Cmd err NgramsTable
+getTableNgrams cId maybeTabType maybeListId mlimit moffset = do
   let lieu = "Garg.API.Ngrams: " :: Text
   let ngramsType = case maybeTabType of
         Nothing  -> Ngrams.Sources -- panic (lieu <> "Indicate the Table")
@@ -301,9 +319,7 @@ getTableNgrams c cId maybeTabType maybeListId mlimit moffset = liftIO $ do
             Terms      -> Ngrams.NgramsTerms
             _          -> panic $ lieu <> "No Ngrams for this tab"
 
-  listId <- case maybeListId of
-      Nothing -> defaultList c cId
-      Just lId -> pure lId
+  listId <- maybe (defaultList cId) pure maybeListId
 
   let
     defaultLimit = 10 -- TODO
@@ -311,7 +327,7 @@ getTableNgrams c cId maybeTabType maybeListId mlimit moffset = liftIO $ do
     offset_ = maybe 0 identity moffset
 
   (ngramsTableDatas, mapToParent, mapToChildren) <-
-    Ngrams.getNgramsTableDb c NodeDocument ngramsType (Ngrams.NgramsTableParam listId cId) limit_ offset_
+    Ngrams.getNgramsTableDb NodeDocument ngramsType (Ngrams.NgramsTableParam listId cId) limit_ offset_
 
   -- printDebug "ngramsTableDatas" ngramsTableDatas
 
diff --git a/src/Gargantext/API/Node.hs b/src/Gargantext/API/Node.hs
index e0f80cde0f6e889cb7e2f521b7cfc882b1169eee..c5713e174509445eea86bbf9aa08bc017d983a67 100644
--- a/src/Gargantext/API/Node.hs
+++ b/src/Gargantext/API/Node.hs
@@ -17,6 +17,7 @@ Node API
 {-# LANGUAGE FlexibleContexts   #-}
 {-# LANGUAGE NoImplicitPrelude  #-}
 {-# LANGUAGE OverloadedStrings  #-}
+{-# LANGUAGE RankNTypes         #-}
 {-# LANGUAGE TemplateHaskell    #-}
 {-# LANGUAGE TypeOperators      #-}
 
@@ -34,28 +35,27 @@ module Gargantext.API.Node
 -------------------------------------------------------------------
 import Control.Lens (prism', set)
 import Control.Monad.IO.Class (liftIO)
-import Control.Monad ((>>))
+import Control.Monad ((>>), guard)
 --import System.IO (putStrLn, readFile)
 
 import Data.Aeson (FromJSON, ToJSON)
+import Data.Functor (($>))
 --import Data.Text (Text(), pack)
 import Data.Text (Text())
 import Data.Swagger
 import Data.Time (UTCTime)
 
-import Database.PostgreSQL.Simple (Connection)
-
 import GHC.Generics (Generic)
 import Servant
 
-import Gargantext.API.Ngrams (TabType(..), TableNgramsApi, TableNgramsApiGet, tableNgramsPatch, getTableNgrams, NgramsIdPatchsFeed, NgramsIdPatchsBack)
+import Gargantext.API.Ngrams (TabType(..), TableNgramsApi, TableNgramsApiGet, tableNgramsPatch, getTableNgrams)
 import Gargantext.Prelude
 import Gargantext.Database.Types.Node
-import Gargantext.Database.Utils (runCmd)
-import Gargantext.Database.Schema.Node ( getNodesWithParentId, getNode, deleteNode, deleteNodes, mk, JSONB)
+import Gargantext.Database.Utils (Cmd, CmdM)
+import Gargantext.Database.Schema.Node ( getNodesWithParentId, getNode, deleteNode, deleteNodes, mk, JSONB, NodeError(..), HasNodeError(..))
 import Gargantext.Database.Node.Children (getChildren)
 import qualified Gargantext.Database.Node.Update as U (update, Update(..))
-import Gargantext.Database.Facet (FacetDoc , runViewDocuments', OrderBy(..),FacetChart,runViewAuthorsDoc)
+import Gargantext.Database.Facet (FacetDoc , runViewDocuments, OrderBy(..),FacetChart,runViewAuthorsDoc)
 import Gargantext.Database.Tree (treeDB, HasTreeError(..), TreeError(..))
 import Gargantext.Database.Schema.NodeNode (nodesToFavorite, nodesToTrash)
 import Gargantext.API.Search ( SearchAPI, searchIn, SearchInQuery)
@@ -64,12 +64,14 @@ import Gargantext.API.Search ( SearchAPI, searchIn, SearchInQuery)
 import Gargantext.Viz.Graph hiding (Node)-- (Graph(_graph_metadata),LegendField(..), GraphMetadata(..),readGraphFromJson,defaultGraph)
 -- import Gargantext.Core (Lang(..))
 import Gargantext.Core.Types (Offset, Limit)
-import Gargantext.Core.Types.Main (Tree, NodeTree, ListId, CorpusId, ContactId)
+import Gargantext.Core.Types.Main (Tree, NodeTree, CorpusId, ContactId)
 -- import Gargantext.Text.Terms (TermType(..))
 
 import Test.QuickCheck (elements)
 import Test.QuickCheck.Arbitrary (Arbitrary, arbitrary)
 
+type GargServer api = forall env m. CmdM env ServantErr m => ServerT api m
+
 -------------------------------------------------------------------
 -- | TODO : access by admin only
 type NodesAPI  = Delete '[JSON] Int
@@ -77,8 +79,8 @@ type NodesAPI  = Delete '[JSON] Int
 -- | Delete Nodes
 -- Be careful: really delete nodes
 -- Access by admin only
-nodesAPI :: Connection -> [NodeId] -> Server NodesAPI
-nodesAPI conn ids = deleteNodes' conn ids
+nodesAPI :: [NodeId] -> GargServer NodesAPI
+nodesAPI ids = deleteNodes ids
 
 ------------------------------------------------------------------------
 -- | TODO: access by admin only
@@ -89,11 +91,11 @@ type Roots =  Get    '[JSON] [NodeAny]
          :<|> Delete '[JSON] Int -- TODO
 
 -- | TODO: access by admin only
-roots :: Connection -> Server Roots
-roots conn = liftIO (putStrLn ( "/user" :: Text) >> getNodesWithParentId 0 Nothing conn)
-          :<|> pure (panic "not implemented yet") -- TODO
-          :<|> pure (panic "not implemented yet") -- TODO
-          :<|> pure (panic "not implemented yet") -- TODO
+roots :: GargServer Roots
+roots = (liftIO (putStrLn ( "/user" :: Text)) >> getNodesWithParentId 0 Nothing)
+   :<|> pure (panic "not implemented yet") -- TODO
+   :<|> pure (panic "not implemented yet") -- TODO
+   :<|> pure (panic "not implemented yet") -- TODO
 
 -------------------------------------------------------------------
 -- | Node API Types management
@@ -136,29 +138,27 @@ type ChildrenApi a = Summary " Summary children"
                  :> Get '[JSON] [Node a]
 ------------------------------------------------------------------------
 -- TODO: make the NodeId type indexed by `a`, then we no longer need the proxy.
-nodeAPI :: JSONB a => Connection -> proxy a -> NodeId -> Server (NodeAPI a)
-nodeAPI conn p id
-                =  liftIO (getNode conn id p)
-              :<|> rename        conn id
-              :<|> postNode      conn id
-              :<|> putNode       conn id
-              :<|> deleteNode'   conn id
-              :<|> getChildren'  conn id p
-              
-              -- TODO gather it
-              :<|> getTable      conn id
-              :<|> tableNgramsPatch'  conn id
-              :<|> getTableNgrams  conn id
-              :<|> getPairing      conn id
-
-              :<|> getChart      conn id
-              :<|> favApi        conn id
-              :<|> delDocs       conn id
-              :<|> searchIn      conn id
-
-              -- Annuaire
-              -- :<|> upload
-              -- :<|> query
+nodeAPI :: JSONB a => proxy a -> NodeId -> GargServer (NodeAPI a)
+nodeAPI p id =  getNode     id p
+           :<|> rename      id
+           :<|> postNode    id
+           :<|> putNode     id
+           :<|> deleteNode  id
+           :<|> getChildren id p
+
+           -- TODO gather it
+           :<|> getTable         id
+           :<|> tableNgramsPatch id
+           :<|> getTableNgrams   id
+           :<|> getPairing       id
+
+           :<|> getChart id
+           :<|> favApi   id
+           :<|> delDocs  id
+           :<|> searchIn id
+           -- Annuaire
+           -- :<|> upload
+           -- :<|> query
 ------------------------------------------------------------------------
 data RenameNode = RenameNode { r_name :: Text }
   deriving (Generic)
@@ -191,9 +191,8 @@ instance FromJSON  Documents
 instance ToJSON    Documents
 instance ToSchema  Documents
 
-delDocs :: Connection -> CorpusId -> Documents -> Handler [Int]
-delDocs c cId ds = liftIO $ nodesToTrash c
-                $ map (\n -> (cId, n, True)) $ documents ds
+delDocs :: CorpusId -> Documents -> Cmd err [Int]
+delDocs cId ds = nodesToTrash $ map (\n -> (cId, n, True)) $ documents ds
 
 ------------------------------------------------------------------------
 type FavApi =  Summary " Favorites label"
@@ -210,17 +209,14 @@ instance FromJSON  Favorites
 instance ToJSON    Favorites
 instance ToSchema  Favorites
 
-putFav :: Connection -> CorpusId -> Favorites -> Handler [Int]
-putFav c cId fs = liftIO $ nodesToFavorite c
-                $ map (\n -> (cId, n, True)) $ favorites fs
+putFav :: CorpusId -> Favorites -> Cmd err [Int]
+putFav cId fs = nodesToFavorite $ map (\n -> (cId, n, True)) $ favorites fs
 
-delFav :: Connection -> CorpusId -> Favorites -> Handler [Int]
-delFav c cId fs = liftIO $ nodesToFavorite c
-                $ map (\n -> (cId, n, False)) $ favorites fs
+delFav :: CorpusId -> Favorites -> Cmd err [Int]
+delFav cId fs = nodesToFavorite $ map (\n -> (cId, n, False)) $ favorites fs
 
-favApi :: Connection -> CorpusId -> (Favorites -> Handler [Int])
-                               :<|> (Favorites -> Handler [Int])
-favApi c cId = putFav c cId :<|> delFav c cId
+favApi :: CorpusId -> GargServer FavApi
+favApi cId = putFav cId :<|> delFav cId
 
 ------------------------------------------------------------------------
 type TableApi = Summary " Table API"
@@ -254,13 +250,10 @@ type ChartApi = Summary " Chart API"
 
 ------------------------------------------------------------------------
 type GraphAPI   = Get '[JSON] Graph
-graphAPI :: Connection -> NodeId -> Server GraphAPI
-graphAPI c nId = liftIO $ graphAPI' c nId
+graphAPI :: NodeId -> GargServer GraphAPI
+graphAPI nId = do
 
-graphAPI' :: Connection -> NodeId -> IO Graph
-graphAPI' c nId = do
-  
-  nodeGraph <- getNode c nId HyperdataGraph
+  nodeGraph <- getNode nId HyperdataGraph
 
   let metadata = GraphMetadata "Title" [maybe 0 identity $ _node_parentId nodeGraph] 
                                        [ LegendField 1 "#FFFFFF" "Label 1"
@@ -276,64 +269,58 @@ graphAPI' c nId = do
   -- liftIO $ liftIO $ pure $  maybe t identity maybeGraph
   -- TODO what do we get about the node? to replace contextText
 
+instance HasNodeError ServantErr where
+  _NodeError = prism' make match
+    where
+      err = err404 { errBody = "NodeError: No list found" }
+      make NoListFound = err
+      match e = guard (e == err) $> NoListFound
+
 -- TODO(orphan): There should be a proper APIError data type with a case TreeError.
 instance HasTreeError ServantErr where
-  _TreeError = prism' mk (const Nothing) -- Note a prism
+  _TreeError = prism' mk (const $ panic "HasTreeError ServantErr: not a prism")
     where
       mk NoRoot       = err404 { errBody = "Root node not found"           }
       mk EmptyRoot    = err500 { errBody = "Root node should not be empty" }
       mk TooManyRoots = err500 { errBody = "Too many root nodes"           }
 
 type TreeAPI   = Get '[JSON] (Tree NodeTree)
-treeAPI :: Connection -> NodeId -> Server TreeAPI
+treeAPI :: NodeId -> GargServer TreeAPI
 treeAPI = treeDB
 
 ------------------------------------------------------------------------
 -- | Check if the name is less than 255 char
-rename :: Connection -> NodeId -> RenameNode -> Handler [Int]
-rename c nId (RenameNode name) = liftIO $ U.update (U.Rename nId name) c
+rename :: NodeId -> RenameNode -> Cmd err [Int]
+rename nId (RenameNode name) = U.update (U.Rename nId name)
 
-getTable :: Connection -> NodeId -> Maybe TabType
+getTable :: NodeId -> Maybe TabType
          -> Maybe Offset  -> Maybe Limit
-         -> Maybe OrderBy -> Handler [FacetDoc]
-getTable c cId ft o l order = liftIO $ case ft of
-                                (Just Docs)  -> runViewDocuments' c cId False o l order
-                                (Just Trash) -> runViewDocuments' c cId True  o l order
+         -> Maybe OrderBy -> Cmd err [FacetDoc]
+getTable cId ft o l order = case ft of
+                                (Just Docs)  -> runViewDocuments cId False o l order
+                                (Just Trash) -> runViewDocuments cId True  o l order
                                 _     -> panic "not implemented"
 
-getPairing :: Connection -> ContactId -> Maybe TabType
+getPairing :: ContactId -> Maybe TabType
          -> Maybe Offset  -> Maybe Limit
-         -> Maybe OrderBy -> Handler [FacetDoc]
-getPairing c cId ft o l order = liftIO $ case ft of
-                                (Just Docs)  -> runViewAuthorsDoc c cId False o l order
-                                (Just Trash) -> runViewAuthorsDoc c cId True  o l order
+         -> Maybe OrderBy -> Cmd err [FacetDoc]
+getPairing cId ft o l order = case ft of
+                                (Just Docs)  -> runViewAuthorsDoc cId False o l order
+                                (Just Trash) -> runViewAuthorsDoc cId True  o l order
                                 _     -> panic "not implemented"
 
 
-getChart :: Connection -> NodeId -> Maybe UTCTime -> Maybe UTCTime
-                        -> Handler [FacetChart]
-getChart _ _ _ _ = undefined -- TODO
+getChart :: NodeId -> Maybe UTCTime -> Maybe UTCTime
+                   -> Cmd err [FacetChart]
+getChart _ _ _ = undefined -- TODO
 
-postNode :: Connection -> NodeId -> PostNode -> Handler [Int]
-postNode c pId (PostNode name nt) = liftIO $ mk c nt (Just pId) name
+postNode :: NodeId -> PostNode -> Cmd err [Int]
+postNode pId (PostNode name nt) = mk nt (Just pId) name
 
-putNode :: Connection -> NodeId -> Handler Int
+putNode :: NodeId -> Cmd err Int
 putNode = undefined -- TODO
 
-deleteNodes' :: Connection -> [NodeId] -> Handler Int
-deleteNodes' conn ids = liftIO (runCmd conn $ deleteNodes ids)
-
-deleteNode' :: Connection -> NodeId -> Handler Int
-deleteNode' conn id = liftIO (runCmd conn $ deleteNode id)
-
-getChildren' :: JSONB a => Connection -> NodeId -> proxy a -> Maybe NodeType
-              -> Maybe Int -> Maybe Int -> Handler [Node a]
-getChildren' conn pId p nodeType offset limit  = liftIO (getChildren conn pId p nodeType offset limit)
-
-tableNgramsPatch' :: Connection -> CorpusId -> Maybe ListId -> NgramsIdPatchsFeed -> Handler NgramsIdPatchsBack
-tableNgramsPatch' c cId mL ns = liftIO $ tableNgramsPatch c cId mL ns
-
-query :: Text -> Handler Text
+query :: Monad m => Text -> m Text
 query s = pure s
 
 
diff --git a/src/Gargantext/API/Search.hs b/src/Gargantext/API/Search.hs
index f7ab709cd74712042749a1f046e0c9905a1d5a0e..0e2d76906aca69f54847d15b23ea9fced8660dae 100644
--- a/src/Gargantext/API/Search.hs
+++ b/src/Gargantext/API/Search.hs
@@ -19,17 +19,16 @@ Count API part of Gargantext.
 {-# LANGUAGE DeriveGeneric      #-}
 {-# LANGUAGE DeriveAnyClass     #-}
 {-# LANGUAGE OverloadedStrings  #-}
+{-# LANGUAGE RankNTypes         #-}
 
 module Gargantext.API.Search
       where
 
 import GHC.Generics (Generic)
 import Data.Time (UTCTime)
-import Control.Monad.IO.Class (liftIO)
 import Data.Aeson.TH (deriveJSON)
 import Data.Swagger
 import Data.Text (Text)
-import Database.PostgreSQL.Simple (Connection)
 import Servant
 import Test.QuickCheck.Arbitrary
 import Test.QuickCheck (elements)
@@ -40,6 +39,7 @@ import Gargantext.Core.Types.Main (Offset, Limit)
 import Gargantext.Database.Types.Node
 import Gargantext.Database.TextSearch
 import Gargantext.Database.Facet
+import Gargantext.Database.Utils (Cmd)
 
 -----------------------------------------------------------------------
 -- | SearchIn [NodesId] if empty then global search
@@ -88,12 +88,12 @@ instance ToSchema SearchResults where
 type SearchAPI = Post '[JSON] SearchResults
 -----------------------------------------------------------------------
 
-search :: Connection -> SearchQuery -> Maybe Offset -> Maybe Limit -> Maybe OrderBy -> Handler SearchResults
-search c (SearchQuery q pId) o l order =
-  liftIO $ SearchResults <$> searchInCorpusWithContacts c pId q o l order
+search :: SearchQuery -> Maybe Offset -> Maybe Limit -> Maybe OrderBy -> Cmd err SearchResults
+search (SearchQuery q pId) o l order =
+  SearchResults <$> searchInCorpusWithContacts pId q o l order
 
-searchIn :: Connection -> NodeId -> SearchInQuery -> Maybe Offset -> Maybe Limit -> Maybe OrderBy -> Handler SearchResults
-searchIn c nId (SearchInQuery q ) o l order =
-  liftIO $ SearchResults <$> searchInCorpusWithContacts c nId q o l order
+searchIn :: NodeId -> SearchInQuery -> Maybe Offset -> Maybe Limit -> Maybe OrderBy -> Cmd err SearchResults
+searchIn nId (SearchInQuery q ) o l order =
+  SearchResults <$> searchInCorpusWithContacts nId q o l order
 
 
diff --git a/src/Gargantext/API/Settings.hs b/src/Gargantext/API/Settings.hs
index e49c367cb8ae6e80de829634decc283f6dd943b7..8ee03b14f3fdb8eb5538cc7730983c4ec94ad392 100644
--- a/src/Gargantext/API/Settings.hs
+++ b/src/Gargantext/API/Settings.hs
@@ -48,7 +48,7 @@ import qualified Jose.Jwa as Jose
 import Control.Monad.Logger
 import Control.Lens
 import Gargantext.Prelude
-import Gargantext.Database.Utils (databaseParameters)
+import Gargantext.Database.Utils (databaseParameters, HasConnection(..))
 import Gargantext.API.Orchestrator.Types
 
 type PortNumber = Int
@@ -136,6 +136,9 @@ data Env = Env
 
 makeLenses ''Env
 
+instance HasConnection Env where
+  connection = env_conn
+
 data MockEnv = MockEnv
   { _menv_firewall :: !FireWall
   }
diff --git a/src/Gargantext/Database.hs b/src/Gargantext/Database.hs
index 02b7e6367d10d8a3180348a656ae063e83eff1f4..4a7cd1ddf7e6b7dbbb090f42d4f1bc7d035634dd 100644
--- a/src/Gargantext/Database.hs
+++ b/src/Gargantext/Database.hs
@@ -18,12 +18,8 @@ Gargantext's database.
 
 module Gargantext.Database ( module Gargantext.Database.Utils
                            , module Gargantext.Database.Bashql
-                           , Connection
                            )
     where
 
 import Gargantext.Database.Utils (connectGargandb)
 import Gargantext.Database.Bashql
-import Database.PostgreSQL.Simple (Connection)
-
-
diff --git a/src/Gargantext/Database/Bashql.hs b/src/Gargantext/Database/Bashql.hs
index a284d470efa1f0099e76bb381c6720fc5f410480..7df8693871a56ccd871eff3e890e0b0c9824d233 100644
--- a/src/Gargantext/Database/Bashql.hs
+++ b/src/Gargantext/Database/Bashql.hs
@@ -59,6 +59,7 @@ AMS, and by SIAM.
 
 {-# LANGUAGE NoImplicitPrelude #-}
 {-# LANGUAGE FlexibleContexts  #-}
+{-# LANGUAGE RankNTypes        #-}
 
 module Gargantext.Database.Bashql ( get
                                   , ls
@@ -70,7 +71,6 @@ module Gargantext.Database.Bashql ( get
                                   , rename
                                   , tree
                                   -- , mkCorpus, mkAnnuaire
-                                  , runCmd'
                                  )
     where
 
@@ -80,51 +80,49 @@ import Data.Text (Text)
 import Data.List (concat, last)
 
 import Gargantext.Core.Types
-import Gargantext.Database.Utils (connectGargandb, Cmd(..), runCmd, mkCmd)
+import Gargantext.Database.Utils (runOpaQuery, Cmd)
 import Gargantext.Database.Schema.Node
 import qualified Gargantext.Database.Node.Update as U (Update(..), update)
 import Gargantext.Prelude
 
-import Opaleye hiding (FromField)
-
 
 -- List of NodeId
 -- type PWD a = PWD UserId [a]
 type PWD = [NodeId]
 --data PWD' a = a | PWD' [a]
 
-rename :: NodeId -> Text -> Cmd [Int]
-rename n t = mkCmd $ \conn -> U.update (U.Rename n t) conn
+rename :: NodeId -> Text -> Cmd err [Int]
+rename n t = U.update $ U.Rename n t
 
-mv :: NodeId -> ParentId -> Cmd [Int]
-mv n p = mkCmd $ \conn -> U.update (U.Move n p) conn
+mv :: NodeId -> ParentId -> Cmd err [Int]
+mv n p = U.update $ U.Move n p
 
 -- | TODO get Children or Node
-get :: PWD -> Cmd [NodeAny]
+get :: PWD -> Cmd err [NodeAny]
 get []  = pure []
-get pwd = Cmd . ReaderT $ \conn -> runQuery conn $ selectNodesWithParentID (last pwd)
+get pwd = runOpaQuery $ selectNodesWithParentID (last pwd)
 
 -- | Home, need to filter with UserId
-home :: Cmd PWD
-home = map _node_id <$> Cmd (ReaderT (getNodesWithParentId 0 Nothing))
+home :: Cmd err PWD
+home = map _node_id <$> getNodesWithParentId 0 Nothing
 
 -- | ls == get Children
-ls :: PWD -> Cmd [NodeAny]
+ls :: PWD -> Cmd err [NodeAny]
 ls = get
 
-tree :: PWD -> Cmd [NodeAny]
+tree :: PWD -> Cmd err [NodeAny]
 tree p = do
   ns       <- get p
   children <- mapM (\n -> get [_node_id n]) ns
   pure $ ns <> concat children
 
 -- | TODO
-post :: PWD -> [NodeWrite'] -> Cmd Int64
+post :: PWD -> [NodeWrite'] -> Cmd err Int64
 post [] _   = pure 0
 post _ []   = pure 0
-post pth ns = Cmd . ReaderT $ insertNodesWithParent (Just $ last pth) ns
+post pth ns = insertNodesWithParent (Just $ last pth) ns
 
---postR :: PWD -> [NodeWrite'] -> Cmd [Int]
+--postR :: PWD -> [NodeWrite'] -> Cmd err [Int]
 --postR [] _ _ = pure [0]
 --postR _ [] _ = pure [0]
 --postR pth ns c = mkNodeR (last pth) ns c
@@ -132,15 +130,15 @@ post pth ns = Cmd . ReaderT $ insertNodesWithParent (Just $ last pth) ns
 -- | WIP
 -- rm : mv to trash
 -- del : empty trash
---rm :: Connection -> PWD -> [NodeId] -> IO Int
+--rm :: PWD -> [NodeId] -> IO Int
 --rm = del
-del :: [NodeId] -> Cmd Int
+del :: [NodeId] -> Cmd err Int
 del [] = pure 0
 del ns = deleteNodes ns
 
 -- | TODO
-put :: U.Update -> Cmd [Int]
-put u = mkCmd $ U.update u
+put :: U.Update -> Cmd err [Int]
+put = U.update
 
 -- | TODO
 -- cd (Home UserId) | (Node NodeId)
@@ -151,7 +149,7 @@ put u = mkCmd $ U.update u
 -- type Name = Text
 
 
---mkCorpus :: ToJSON a => Name -> (a -> Text) -> [a] -> Cmd NewNode
+--mkCorpus :: ToJSON a => Name -> (a -> Text) -> [a] -> Cmd err NewNode
 --mkCorpus name title ns = do
 --  pid <- home
 --  
@@ -167,7 +165,7 @@ put u = mkCmd $ U.update u
 ---- |
 ---- import IMTClient as C
 ---- postAnnuaire "Annuaire IMT" (\n -> (maybe "" identity (C.prenom n)) <> " " <> (maybe "" identity (C.nom n))) (take 30 annuaire)
---mkAnnuaire :: ToJSON a => Name -> (a -> Text) -> [a] -> Cmd NewNode
+--mkAnnuaire :: ToJSON a => Name -> (a -> Text) -> [a] -> Cmd err NewNode
 --mkAnnuaire name title ns = do
 --  pid <- lastMay <$> home
 --  let pid' = case lastMay pid of
@@ -185,6 +183,3 @@ put u = mkCmd $ U.update u
 -- let myCorpus' = Prelude.filter (\n -> T.length (maybe "" identity (hyperdataDocument_title n)) > 30) myCorpus
 
 -- corporaOf :: Username -> IO [Corpus]
-
-runCmd' :: Cmd a -> IO a
-runCmd' f = connectGargandb "gargantext.ini" >>= \c -> runCmd c f
diff --git a/src/Gargantext/Database/Cooc.hs b/src/Gargantext/Database/Cooc.hs
index 74d0fee52a5ed56613d065ce26628bca6d621eef..f8d77e7777211495ab5e0db1138ef407f13da770 100644
--- a/src/Gargantext/Database/Cooc.hs
+++ b/src/Gargantext/Database/Cooc.hs
@@ -13,26 +13,24 @@ Portability : POSIX
 --{-# LANGUAGE OverloadedStrings #-}
 
 {-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE RankNTypes  #-}
 
 module Gargantext.Database.Cooc where
 
-import Control.Monad ((>>=))
-import Database.PostgreSQL.Simple
 import Database.PostgreSQL.Simple.SqlQQ
 
 import Gargantext.Prelude
-import Gargantext (connectGargandb)
+import Gargantext.Database.Utils (Cmd, runCmdDevNoErr, runPGSQuery)
 
 type CorpusId    = Int
 type MainListId  = Int
 type GroupListId = Int
 
 coocTest :: IO [(Int, Int, Int)]
-coocTest = connectGargandb "gargantext.ini"
-  >>= \conn -> dBcooc conn 421968 446602 446599 
+coocTest = runCmdDevNoErr $ dBcooc 421968 446602 446599
 
-dBcooc :: Connection -> CorpusId -> MainListId -> GroupListId -> IO [(Int, Int, Int)]
-dBcooc conn corpus mainList groupList = query conn [sql|
+dBcooc :: CorpusId -> MainListId -> GroupListId -> Cmd err [(Int, Int, Int)]
+dBcooc corpus mainList groupList = runPGSQuery [sql|
   set work_mem='1GB';
 
   --EXPLAIN ANALYZE
diff --git a/src/Gargantext/Database/Facet.hs b/src/Gargantext/Database/Facet.hs
index 3882401c045d0ccd369d8ef7a16ac5613547bb52..1c2dbca798e5838dd79ff849b14fe27553cb91e7 100644
--- a/src/Gargantext/Database/Facet.hs
+++ b/src/Gargantext/Database/Facet.hs
@@ -21,6 +21,7 @@ Portability : POSIX
 {-# LANGUAGE NoImplicitPrelude         #-}
 {-# LANGUAGE NoMonomorphismRestriction #-}
 {-# LANGUAGE OverloadedStrings         #-}
+{-# LANGUAGE RankNTypes                #-}
 {-# LANGUAGE TemplateHaskell           #-}
 ------------------------------------------------------------------------
 module Gargantext.Database.Facet
@@ -37,7 +38,6 @@ import Data.Swagger
 import Data.Text (Text)
 import Data.Time (UTCTime)
 import Data.Time.Segment (jour)
-import Database.PostgreSQL.Simple (Connection)
 import GHC.Generics (Generic)
 import Gargantext.Core.Types
 import Gargantext.Core.Utils.Prefix (unPrefix)
@@ -204,8 +204,8 @@ instance Arbitrary OrderBy
     arbitrary = elements [minBound..maxBound]
 
 
-runViewAuthorsDoc :: Connection -> ContactId -> Trash -> Maybe Offset -> Maybe Limit -> Maybe OrderBy -> IO [FacetDoc]
-runViewAuthorsDoc c cId t o l order = runQuery c (filterWith o l order $ viewAuthorsDoc cId t ntId)
+runViewAuthorsDoc :: ContactId -> Trash -> Maybe Offset -> Maybe Limit -> Maybe OrderBy -> Cmd err [FacetDoc]
+runViewAuthorsDoc cId t o l order = runOpaQuery $ filterWith o l order $ viewAuthorsDoc cId t ntId
   where
     ntId = NodeDocument
 
@@ -244,13 +244,9 @@ queryAuthorsDoc = leftJoin5 queryNodeTable queryNodeNgramTable queryNgramsTable
 
 ------------------------------------------------------------------------
 
-runViewDocuments :: CorpusId -> Trash -> Maybe Offset -> Maybe Limit -> Maybe OrderBy -> Cmd [FacetDoc]
-runViewDocuments cId t o l order = mkCmd $ \c -> runViewDocuments' c cId t o l order
-
--- | TODO use only Cmd with Reader and delete function below
-runViewDocuments' :: Connection -> CorpusId -> Trash -> Maybe Offset -> Maybe Limit -> Maybe OrderBy -> IO [FacetDoc]
-runViewDocuments' c cId t o l order = runQuery c ( filterWith o l order
-                                                $ viewDocuments cId t ntId)
+runViewDocuments :: CorpusId -> Trash -> Maybe Offset -> Maybe Limit -> Maybe OrderBy -> Cmd err [FacetDoc]
+runViewDocuments cId t o l order =
+    runOpaQuery $ filterWith o l order $ viewDocuments cId t ntId
   where
     ntId = nodeTypeId NodeDocument
 
diff --git a/src/Gargantext/Database/Flow.hs b/src/Gargantext/Database/Flow.hs
index f553cfd813f6187cce35dcb04f6dbea82d1812fd..8f84d5e72d6d52ee63218b7644ce61c690ffc804 100644
--- a/src/Gargantext/Database/Flow.hs
+++ b/src/Gargantext/Database/Flow.hs
@@ -12,11 +12,13 @@ Portability : POSIX
 {-# LANGUAGE DeriveGeneric     #-}
 {-# LANGUAGE NoImplicitPrelude #-}
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RankNTypes        #-}
 
 module Gargantext.Database.Flow -- (flowDatabase, ngrams2list)
     where
 
 --import Control.Lens (view)
+import Control.Monad.IO.Class (liftIO)
 --import Gargantext.Core.Types
 --import Gargantext.Database.Node.Contact (HyperdataContact(..))
 import Data.Map (Map, lookup)
@@ -29,21 +31,20 @@ import Gargantext.Core.Types (NodePoly(..), ListType(..), listTypeId, Terms(..))
 import Gargantext.Core.Types.Individu (Username)
 import Gargantext.Core.Types.Main
 import Gargantext.Core (Lang(..))
-import Gargantext.Database.Bashql (runCmd') -- , del)
 import Gargantext.Database.Config (userMaster, userArbitrary, corpusMasterName)
 import Gargantext.Database.Flow.Utils (insertToNodeNgrams)
 import Gargantext.Text.Terms (extractTerms)
 import Gargantext.Database.Node.Document.Add    (add)
 import Gargantext.Database.Node.Document.Insert (insertDocuments, ReturnId(..), addUniqIdsDoc, addUniqIdsContact, ToDbData(..))
-import Gargantext.Database.Root (getRootCmd)
+import Gargantext.Database.Root (getRoot)
 import Gargantext.Database.Schema.Ngrams (insertNgrams, Ngrams(..), NgramsT(..), NgramsIndexed(..), indexNgramsT,  NgramsType(..), text2ngrams)
-import Gargantext.Database.Schema.Node (mkRoot, mkCorpus, getOrMkList, mkGraph, mkDashboard, mkAnnuaire, getCorporaWithParentId')
+import Gargantext.Database.Schema.Node (mkRoot, mkCorpus, getOrMkList, mkGraph, mkDashboard, mkAnnuaire, getCorporaWithParentId, HasNodeError)
 import Gargantext.Database.Schema.NodeNgram (NodeNgramPoly(..), insertNodeNgrams)
 import Gargantext.Database.Schema.NodeNgramsNgrams (NodeNgramsNgramsPoly(..), insertNodeNgramsNgramsNew)
 import Gargantext.Database.Schema.User (getUser, UserLight(..))
 import Gargantext.Database.Types.Node (HyperdataDocument(..))
 import Gargantext.Database.Types.Node (NodeType(..), NodeId)
-import Gargantext.Database.Utils (Cmd(..))
+import Gargantext.Database.Utils (Cmd)
 import Gargantext.Text.Terms (TermType(..))
 import Gargantext.Ext.IMT (toSchoolName)
 import Gargantext.Ext.IMTUser (deserialiseImtUsersFromFile)
@@ -52,56 +53,57 @@ import Gargantext.Text.Parsers (parseDocs, FileFormat)
 import System.FilePath (FilePath)
 import qualified Data.Map as DM
 
-flowCorpus :: FileFormat -> FilePath -> CorpusName -> IO CorpusId
+flowCorpus :: HasNodeError err => FileFormat -> FilePath -> CorpusName -> Cmd err CorpusId
 flowCorpus ff fp cName = do
-  hyperdataDocuments' <- map addUniqIdsDoc <$> parseDocs ff fp
+  hyperdataDocuments' <- map addUniqIdsDoc <$> liftIO (parseDocs ff fp)
   params <- flowInsert NodeCorpus hyperdataDocuments' cName
   flowCorpus' NodeCorpus hyperdataDocuments' params
 
 
 flowInsert :: NodeType -> [HyperdataDocument] -> CorpusName
-     -> IO ([ReturnId], MasterUserId, MasterCorpusId, UserId, CorpusId)
+     -> Cmd err ([ReturnId], MasterUserId, MasterCorpusId, UserId, CorpusId)
 flowInsert _nt hyperdataDocuments cName = do
   let hyperdataDocuments' = map (\h -> ToDbDocument h) hyperdataDocuments
 
   (masterUserId, _, masterCorpusId) <- subFlowCorpus userMaster corpusMasterName
-  ids  <- runCmd' $ insertDocuments masterUserId masterCorpusId NodeDocument hyperdataDocuments'
+  ids  <- insertDocuments masterUserId masterCorpusId NodeDocument hyperdataDocuments'
   
   (userId, _, userCorpusId) <- subFlowCorpus userArbitrary cName
-  _ <- runCmd' $ add userCorpusId (map reId ids)
+  _ <- add userCorpusId (map reId ids)
   
   pure (ids, masterUserId, masterCorpusId, userId, userCorpusId)
 
 
-flowAnnuaire :: FilePath -> IO ()
+flowAnnuaire :: FilePath -> Cmd err ()
 flowAnnuaire filePath = do
-  contacts <- deserialiseImtUsersFromFile filePath
+  contacts <- liftIO $ deserialiseImtUsersFromFile filePath
   ps <- flowInsertAnnuaire "Annuaire" $ map (\h-> ToDbContact h) $ map addUniqIdsContact contacts
   printDebug "length annuaire" ps
 
 
 flowInsertAnnuaire :: CorpusName -> [ToDbData]
-                    -> IO ([ReturnId], UserId, CorpusId, UserId, CorpusId)
+                    -> Cmd err ([ReturnId], UserId, CorpusId, UserId, CorpusId)
 flowInsertAnnuaire name children = do
 
   (masterUserId, _, masterCorpusId) <- subFlowCorpus userMaster corpusMasterName
-  ids  <- runCmd' $ insertDocuments masterUserId masterCorpusId NodeContact children
+  ids  <- insertDocuments masterUserId masterCorpusId NodeContact children
   
   (userId, _, userCorpusId) <- subFlowAnnuaire userArbitrary name
-  _ <- runCmd' $ add userCorpusId (map reId ids)
+  _ <- add userCorpusId (map reId ids)
   
   printDebug "AnnuaireID" userCorpusId
 
   pure (ids, masterUserId, masterCorpusId, userId, userCorpusId)
 
 
-flowCorpus' :: NodeType -> [HyperdataDocument]
+flowCorpus' :: HasNodeError err
+            => NodeType -> [HyperdataDocument]
             -> ([ReturnId], UserId, CorpusId, UserId, CorpusId)
-            -> IO CorpusId
+            -> Cmd err CorpusId
 flowCorpus' NodeCorpus hyperdataDocuments (ids,masterUserId,masterCorpusId, userId,userCorpusId) = do
 --------------------------------------------------
   -- List Ngrams Flow
-  userListId <- runCmd' $ flowListUser userId userCorpusId
+  userListId <- flowListUser userId userCorpusId
   printDebug "Working on User ListId : " userListId
   
   let documentsWithId = mergeData (toInserted ids) (toInsert hyperdataDocuments)
@@ -111,22 +113,22 @@ flowCorpus' NodeCorpus hyperdataDocuments (ids,masterUserId,masterCorpusId, user
   let maps            = mapNodeIdNgrams docsWithNgrams
   
   -- printDebug "maps" (maps)
-  indexedNgrams <- runCmd' $ indexNgrams maps
+  indexedNgrams <- indexNgrams maps
   -- printDebug "inserted ngrams" indexedNgrams
-  _             <- runCmd' $ insertToNodeNgrams indexedNgrams
+  _             <- insertToNodeNgrams indexedNgrams
   
-  listId2    <- runCmd' $ flowList masterUserId masterCorpusId indexedNgrams
+  listId2    <- flowList masterUserId masterCorpusId indexedNgrams
   printDebug "Working on ListId : " listId2
   --}
 --------------------------------------------------
-  _ <- runCmd' $ mkDashboard userCorpusId userId
-  _ <- runCmd' $ mkGraph     userCorpusId userId
+  _ <- mkDashboard userCorpusId userId
+  _ <- mkGraph     userCorpusId userId
   
   -- Annuaire Flow
-  -- _ <- runCmd' $ mkAnnuaire  rootUserId userId
+  -- _ <- mkAnnuaire  rootUserId userId
 
   pure userCorpusId
-  -- runCmd' $ del [corpusId2, corpusId]
+  -- del [corpusId2, corpusId]
 
 flowCorpus' NodeAnnuaire _hyperdataDocuments (_ids,_masterUserId,_masterCorpusId,_userId,_userCorpusId) = undefined
 flowCorpus' _ _ _ = undefined
@@ -134,19 +136,19 @@ flowCorpus' _ _ _ = undefined
 
 type CorpusName = Text
 
-subFlowCorpus :: Username -> CorpusName -> IO (UserId, RootId, CorpusId)
+subFlowCorpus :: Username -> CorpusName -> Cmd err (UserId, RootId, CorpusId)
 subFlowCorpus username cName = do
-  maybeUserId <- runCmd' (getUser username)
+  maybeUserId <- getUser username
 
   let userId = case maybeUserId of
         Nothing   -> panic "Error: User does not exist (yet)"
         -- mk NodeUser gargantua_id "Node Gargantua"
         Just user -> userLight_id user
 
-  rootId' <- map _node_id <$> runCmd' (getRootCmd username)
+  rootId' <- map _node_id <$> getRoot username
 
   rootId'' <- case rootId' of
-        []  -> runCmd' (mkRoot username userId)
+        []  -> mkRoot username userId
         n   -> case length n >= 2 of
             True  -> panic "Error: more than 1 userNode / user"
             False -> pure rootId'
@@ -154,14 +156,14 @@ subFlowCorpus username cName = do
 
   corpusId'' <- if username == userMaster
                   then do
-                    ns <- runCmd' $ getCorporaWithParentId' rootId
+                    ns <- getCorporaWithParentId rootId
                     pure $ map _node_id ns
                   else
                     pure []
 
   corpusId' <- if corpusId'' /= []
                   then pure corpusId''
-                  else runCmd' $ mkCorpus (Just cName) Nothing rootId userId
+                  else mkCorpus (Just cName) Nothing rootId userId
 
   let corpusId = maybe (panic "error corpusId") identity (head corpusId')
 
@@ -170,25 +172,25 @@ subFlowCorpus username cName = do
   pure (userId, rootId, corpusId)
 
 
-subFlowAnnuaire :: Username -> CorpusName -> IO (UserId, RootId, CorpusId)
+subFlowAnnuaire :: Username -> CorpusName -> Cmd err (UserId, RootId, CorpusId)
 subFlowAnnuaire username _cName = do
-  maybeUserId <- runCmd' (getUser username)
+  maybeUserId <- getUser username
 
   let userId = case maybeUserId of
         Nothing   -> panic "Error: User does not exist (yet)"
         -- mk NodeUser gargantua_id "Node Gargantua"
         Just user -> userLight_id user
 
-  rootId' <- map _node_id <$> runCmd' (getRootCmd username)
+  rootId' <- map _node_id <$> getRoot username
 
   rootId'' <- case rootId' of
-        []  -> runCmd' (mkRoot username userId)
+        []  -> mkRoot username userId
         n   -> case length n >= 2 of
             True  -> panic "Error: more than 1 userNode / user"
             False -> pure rootId'
   let rootId = maybe (panic "error rootId") identity (head rootId'')
 
-  corpusId' <- runCmd' $ mkAnnuaire rootId userId
+  corpusId' <- mkAnnuaire rootId userId
   
   let corpusId = maybe (panic "error corpusId") identity (head corpusId')
 
@@ -232,14 +234,14 @@ data DocumentIdWithNgrams =
 -- add f :: Text -> Text
 -- newtype Ngrams = Ngrams Text
 -- TODO group terms
-extractNgramsT :: HyperdataDocument -> IO (Map (NgramsT Ngrams) Int)
+extractNgramsT :: HyperdataDocument -> Cmd err (Map (NgramsT Ngrams) Int)
 extractNgramsT doc = do
   
   let source    = text2ngrams $ maybe "Nothing" identity $ _hyperdataDocument_source doc
   let institutes = map text2ngrams $ maybe ["Nothing"] (map toSchoolName . (splitOn ", "))  $ _hyperdataDocument_institutes doc
   let authors    = map text2ngrams $ maybe ["Nothing"] (splitOn ", ") $ _hyperdataDocument_authors doc
   let leText = catMaybes [_hyperdataDocument_title doc, _hyperdataDocument_abstract doc]
-  terms' <- map text2ngrams <$> map (intercalate " " . _terms_label) <$> concat <$> extractTerms (Multi EN) leText
+  terms' <- map text2ngrams <$> map (intercalate " " . _terms_label) <$> concat <$> liftIO (extractTerms (Multi EN) leText)
 
   pure $ DM.fromList $  [(NgramsT Sources source, 1)]
                      <> [(NgramsT Institutes  i' , 1)| i' <- institutes ]
@@ -249,8 +251,8 @@ extractNgramsT doc = do
 
 
 
-documentIdWithNgrams :: (HyperdataDocument -> IO (Map (NgramsT Ngrams) Int))
-                     -> [DocumentWithId]   -> IO [DocumentIdWithNgrams]
+documentIdWithNgrams :: (HyperdataDocument -> Cmd err (Map (NgramsT Ngrams) Int))
+                     -> [DocumentWithId]   -> Cmd err [DocumentIdWithNgrams]
 documentIdWithNgrams f = mapM toDocumentIdWithNgrams
   where
     toDocumentIdWithNgrams d = do
@@ -265,7 +267,7 @@ mapNodeIdNgrams ds = DM.map (DM.fromListWith (+)) $ DM.fromListWith (<>) xs
     n2i = map (\d -> ((documentId . documentWithId) d, document_ngrams d))
 
 indexNgrams :: Map (NgramsT Ngrams       ) (Map NodeId Int)
-       -> Cmd (Map (NgramsT NgramsIndexed) (Map NodeId Int))
+  -> Cmd err (Map (NgramsT NgramsIndexed) (Map NodeId Int))
 indexNgrams ng2nId = do
   terms2id <- insertNgrams (map _ngramsT $ DM.keys ng2nId)
   pure $ DM.mapKeys (indexNgramsT terms2id) ng2nId
@@ -273,7 +275,7 @@ indexNgrams ng2nId = do
 
 ------------------------------------------------------------------------
 ------------------------------------------------------------------------
-flowList :: UserId -> CorpusId -> Map (NgramsT NgramsIndexed) (Map NodeId Int) -> Cmd ListId
+flowList :: HasNodeError err => UserId -> CorpusId -> Map (NgramsT NgramsIndexed) (Map NodeId Int) -> Cmd err ListId
 flowList uId cId ngs = do
   -- printDebug "ngs:" ngs
   lId <- getOrMkList cId uId
@@ -291,7 +293,7 @@ flowList uId cId ngs = do
 
   pure lId
 
-flowListUser :: UserId -> CorpusId -> Cmd Int
+flowListUser :: HasNodeError err => UserId -> CorpusId -> Cmd err Int
 flowListUser uId cId = getOrMkList cId uId
 
 ------------------------------------------------------------------------
@@ -304,11 +306,11 @@ groupNgramsBy isEqual cId = DM.fromList $ catMaybes [ isEqual n1 n2 | n1 <- DM.k
 
 
 -- TODO check: do not insert duplicates
-insertGroups :: ListId -> Map NgramsIndexed NgramsIndexed -> Cmd Int
+insertGroups :: ListId -> Map NgramsIndexed NgramsIndexed -> Cmd err Int
 insertGroups lId ngrs =
   insertNodeNgramsNgramsNew [ NodeNgramsNgrams lId ng1 ng2 (Just 1)
-                              | (ng1, ng2) <- map (both _ngramsId) $ DM.toList ngrs
-                              , ng1 /= ng2
+                            | (ng1, ng2) <- map (both _ngramsId) $ DM.toList ngrs
+                            , ng1 /= ng2
                             ]
 
 ------------------------------------------------------------------------
@@ -317,7 +319,7 @@ ngrams2list :: Map (NgramsT NgramsIndexed) (Map NodeId Int) -> [(ListType,Ngrams
 ngrams2list = zip (repeat CandidateList) . map (\(NgramsT _lost_t ng) -> ng) . DM.keys
 
 -- | TODO: weight of the list could be a probability
-insertLists :: ListId -> [(ListType,NgramsIndexed)] -> Cmd Int
+insertLists :: ListId -> [(ListType,NgramsIndexed)] -> Cmd err Int
 insertLists lId lngs =
   insertNodeNgrams [ NodeNgram Nothing lId ngr (fromIntegral $ listTypeId l) (listTypeId l)
                      | (l,ngr) <- map (second _ngramsId) lngs
diff --git a/src/Gargantext/Database/Flow/Pairing.hs b/src/Gargantext/Database/Flow/Pairing.hs
index dd47ec23248a0c1b89db5f47fff1ff197df3ddc5..aa71683b15b29d71c266624378a0a4630a09662a 100644
--- a/src/Gargantext/Database/Flow/Pairing.hs
+++ b/src/Gargantext/Database/Flow/Pairing.hs
@@ -9,9 +9,10 @@ Portability : POSIX
 
 -}
 
-{-# LANGUAGE QuasiQuotes            #-}
+{-# LANGUAGE QuasiQuotes       #-}
 {-# LANGUAGE NoImplicitPrelude #-}
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RankNTypes        #-}
 -- {-# LANGUAGE Arrows #-}
 
 module Gargantext.Database.Flow.Pairing
@@ -19,7 +20,6 @@ module Gargantext.Database.Flow.Pairing
 
 --import Debug.Trace (trace)
 import Control.Lens (_Just,view)
-import Database.PostgreSQL.Simple (Connection, query)
 import Database.PostgreSQL.Simple.SqlQQ (sql)
 -- import Opaleye
 -- import Opaleye.Aggregate
@@ -36,27 +36,26 @@ import Gargantext.Database.Schema.Ngrams -- (NgramsType(..))
 --import Gargantext.Database.Types.Node -- (Hyperdata(..))
 import Gargantext.Database.Node.Contact
 import Gargantext.Database.Flow.Utils
-import Gargantext.Database.Utils (Cmd, mkCmd)
+import Gargantext.Database.Utils (Cmd, runPGSQuery)
 import Gargantext.Database.Node.Children
 import Gargantext.Core.Types.Main
 import Gargantext.Core.Types (NodeType(..))
-import Gargantext.Database.Bashql (runCmd')
 
 -- TODO mv this type in Types Main
 type Terms = Text
 
 -- | TODO : add paring policy as parameter
-pairing :: AnnuaireId -> CorpusId -> IO Int
+pairing :: AnnuaireId -> CorpusId -> Cmd err Int
 pairing aId cId = do
-  contacts' <- runCmd' $ getContacts aId (Just NodeContact)
+  contacts' <- getContacts aId (Just NodeContact)
   let contactsMap = pairingPolicyToMap toLower $ toMaps extractNgramsT contacts'
-  
-  ngramsMap' <- runCmd' $ getNgramsTindexed cId Authors
+
+  ngramsMap' <- getNgramsTindexed cId Authors
   let ngramsMap = pairingPolicyToMap lastName ngramsMap'
 
   let indexedNgrams = pairMaps contactsMap ngramsMap
-  
-  runCmd' $ insertToNodeNgrams indexedNgrams
+
+  insertToNodeNgrams indexedNgrams
   -- TODO add List
 
 lastName :: Terms -> Terms
@@ -92,13 +91,13 @@ pairMaps m1 m2 = DM.fromList $ catMaybes $ map (\(k,n) -> (,) <$> lookup' k m2 <
 
 
 -----------------------------------------------------------------------
-getNgramsTindexed:: CorpusId -> NgramsType -> Cmd (Map (NgramsT Ngrams) NgramsId)
-getNgramsTindexed corpusId ngramsType' = mkCmd $ \c -> fromList
+getNgramsTindexed:: CorpusId -> NgramsType -> Cmd err (Map (NgramsT Ngrams) NgramsId)
+getNgramsTindexed corpusId ngramsType' = fromList
     <$> map (\(ngramsId',t,n) -> (NgramsT ngramsType' (Ngrams t n),ngramsId'))
-    <$> selectNgramsTindexed c corpusId ngramsType'
+    <$> selectNgramsTindexed corpusId ngramsType'
 
-selectNgramsTindexed :: Connection -> CorpusId -> NgramsType -> IO [(NgramsId, Terms, Int)]
-selectNgramsTindexed c corpusId ngramsType'' = query c selectQuery (corpusId, ngramsTypeId ngramsType'')
+selectNgramsTindexed :: CorpusId -> NgramsType -> Cmd err [(NgramsId, Terms, Int)]
+selectNgramsTindexed corpusId ngramsType'' = runPGSQuery selectQuery (corpusId, ngramsTypeId ngramsType'')
   where
     selectQuery = [sql| SELECT n.id,n.terms,n.n from ngrams n
                   JOIN nodes_ngrams occ ON occ.ngram_id = n.id
diff --git a/src/Gargantext/Database/Flow/Utils.hs b/src/Gargantext/Database/Flow/Utils.hs
index e6442342c3bc0d518b495845b8f6f7998c245b22..1cd185db4abab1c879bb80ad40d157829478d37e 100644
--- a/src/Gargantext/Database/Flow/Utils.hs
+++ b/src/Gargantext/Database/Flow/Utils.hs
@@ -11,6 +11,7 @@ Portability : POSIX
 
 {-# LANGUAGE NoImplicitPrelude #-}
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RankNTypes        #-}
 
 module Gargantext.Database.Flow.Utils
     where
@@ -53,11 +54,11 @@ data DocumentIdWithNgrams a =
      } deriving (Show)
 
 
-insertToNodeNgrams :: Map (NgramsT NgramsIndexed) (Map NodeId Int) -> Cmd Int
+insertToNodeNgrams :: Map (NgramsT NgramsIndexed) (Map NodeId Int) -> Cmd err Int
 insertToNodeNgrams m = insertNodeNgrams [ NodeNgram Nothing nId  ((_ngramsId    . _ngramsT   ) ng)
                                                 (fromIntegral n) ((ngramsTypeId . _ngramsType) ng)
-                                          | (ng, nId2int) <- DM.toList m
-                                          , (nId, n)      <- DM.toList nId2int
+                                        | (ng, nId2int) <- DM.toList m
+                                        , (nId, n)      <- DM.toList nId2int
                                         ]
 
 
diff --git a/src/Gargantext/Database/Node/Children.hs b/src/Gargantext/Database/Node/Children.hs
index 71c70f6cb3004935353e735bd1c89d0f3d60ac60..476656d8b35fa852dbefd08d41085f47138cad9a 100644
--- a/src/Gargantext/Database/Node/Children.hs
+++ b/src/Gargantext/Database/Node/Children.hs
@@ -14,10 +14,10 @@ Portability : POSIX
 {-# LANGUAGE Arrows                 #-}
 {-# LANGUAGE FlexibleInstances      #-}
 {-# LANGUAGE FlexibleContexts       #-}
+{-# LANGUAGE RankNTypes             #-}
 
 module Gargantext.Database.Node.Children where
 
-import Database.PostgreSQL.Simple (Connection)
 import Opaleye
 import Gargantext.Core.Types
 import Gargantext.Database.Schema.Node
@@ -29,12 +29,12 @@ import Gargantext.Database.Node.Contact (HyperdataContact)
 import Control.Arrow (returnA)
 
 -- | TODO: use getChildren with Proxy ?
-getContacts :: ParentId -> Maybe NodeType -> Cmd [Node HyperdataContact]
-getContacts pId maybeNodeType = mkCmd $ \c -> runQuery c $ selectChildren pId maybeNodeType
+getContacts :: ParentId -> Maybe NodeType -> Cmd err [Node HyperdataContact]
+getContacts pId maybeNodeType = runOpaQuery $ selectChildren pId maybeNodeType
 
 
-getChildren :: JSONB a => Connection -> ParentId -> proxy a -> Maybe NodeType -> Maybe Offset -> Maybe Limit -> IO [Node a]
-getChildren c pId _ maybeNodeType maybeOffset maybeLimit = runQuery c 
+getChildren :: JSONB a => ParentId -> proxy a -> Maybe NodeType -> Maybe Offset -> Maybe Limit -> Cmd err [Node a]
+getChildren pId _ maybeNodeType maybeOffset maybeLimit = runOpaQuery
                   $ limit' maybeLimit $ offset' maybeOffset
                   $ orderBy (asc _node_id)
                   $ selectChildren pId maybeNodeType
diff --git a/src/Gargantext/Database/Node/Document/Add.hs b/src/Gargantext/Database/Node/Document/Add.hs
index 819f2cbc04f9858bccf83f850ae6d9c29b5d87f7..c067b47759302de56d465e04eead38a8ef184510 100644
--- a/src/Gargantext/Database/Node/Document/Add.hs
+++ b/src/Gargantext/Database/Node/Document/Add.hs
@@ -17,6 +17,7 @@ Add Documents/Contact to a Corpus/Annuaire.
 {-# LANGUAGE NoImplicitPrelude    #-}
 {-# LANGUAGE OverloadedStrings    #-}
 {-# LANGUAGE QuasiQuotes          #-}
+{-# LANGUAGE RankNTypes           #-}
 {-# LANGUAGE TypeSynonymInstances #-}
 ------------------------------------------------------------------------
 module Gargantext.Database.Node.Document.Add where
@@ -24,7 +25,7 @@ module Gargantext.Database.Node.Document.Add where
 
 import Data.ByteString.Internal (ByteString)
 import Data.Typeable (Typeable)
-import Database.PostgreSQL.Simple (Query, formatQuery, query, Only(..))
+import Database.PostgreSQL.Simple (Query, Only(..))
 import Database.PostgreSQL.Simple.SqlQQ
 import Database.PostgreSQL.Simple.ToField (toField)
 import Database.PostgreSQL.Simple.ToRow (ToRow(..))
@@ -32,7 +33,7 @@ import Database.PostgreSQL.Simple.Types (Values(..), QualifiedIdentifier(..))
 
 import Data.Text (Text)
 
-import Gargantext.Database.Utils (mkCmd, Cmd(..))
+import Gargantext.Database.Utils (Cmd, runPGSQuery, formatPGSQuery)
 import Gargantext.Database.Types.Node
 import Gargantext.Prelude
 
@@ -41,14 +42,14 @@ import GHC.Generics (Generic)
 
 type ParentId = Int
 
-add :: ParentId -> [NodeId] -> Cmd [Only Int]
-add pId ns = mkCmd $ \c -> query c queryAdd (Only $ Values fields inputData)
+add :: ParentId -> [NodeId] -> Cmd err [Only Int]
+add pId ns = runPGSQuery queryAdd (Only $ Values fields inputData)
   where
     fields    = map (\t-> QualifiedIdentifier Nothing t) inputSqlTypes
     inputData = prepare pId ns
 
-add_debug :: ParentId -> [NodeId] -> Cmd ByteString
-add_debug pId ns = mkCmd $ \c -> formatQuery c queryAdd (Only $ Values fields inputData)
+add_debug :: ParentId -> [NodeId] -> Cmd err ByteString
+add_debug pId ns = formatPGSQuery queryAdd (Only $ Values fields inputData)
   where
     fields    = map (\t-> QualifiedIdentifier Nothing t) inputSqlTypes
     inputData = prepare pId ns
diff --git a/src/Gargantext/Database/Node/Document/Insert.hs b/src/Gargantext/Database/Node/Document/Insert.hs
index 4e84abe3002180acbef48ce3b7b4a06005892a09..d4498290effcc7ca90b68578c3a8b07220ac8e62 100644
--- a/src/Gargantext/Database/Node/Document/Insert.hs
+++ b/src/Gargantext/Database/Node/Document/Insert.hs
@@ -44,8 +44,7 @@ the concatenation of the parameters defined by @hashParameters@.
 > -- * Example
 > insertTest :: FromRow r => CorpusId -> [Node HyperdataDocument] -> IO [r]
 > insertTest :: IO [ReturnId]
-> insertTest = connectGargandb "gargantext.ini"
->          >>= \conn -> insertDocuments conn 1 452162 hyperdataDocuments
+> insertTest = runCmdDev $ insertDocuments 1 452162 hyperdataDocuments
 
 -}
 ------------------------------------------------------------------------
@@ -55,6 +54,7 @@ the concatenation of the parameters defined by @hashParameters@.
 {-# LANGUAGE NoImplicitPrelude    #-}
 {-# LANGUAGE OverloadedStrings    #-}
 {-# LANGUAGE QuasiQuotes          #-}
+{-# LANGUAGE RankNTypes           #-}
 {-# LANGUAGE TypeSynonymInstances #-}
 ------------------------------------------------------------------------
 module Gargantext.Database.Node.Document.Insert where
@@ -66,7 +66,7 @@ import Data.Aeson (toJSON, Value)
 import Data.Maybe (maybe)
 import Data.Text (Text)
 import Data.Typeable (Typeable)
-import Database.PostgreSQL.Simple (FromRow, Query, query, Only(..))
+import Database.PostgreSQL.Simple (FromRow, Query, Only(..))
 import Database.PostgreSQL.Simple.FromRow (fromRow, field)
 import Database.PostgreSQL.Simple.SqlQQ
 import Database.PostgreSQL.Simple.ToField (toField)
@@ -74,7 +74,7 @@ import Database.PostgreSQL.Simple.ToRow (ToRow(..))
 import Database.PostgreSQL.Simple.Types (Values(..), QualifiedIdentifier(..))
 import GHC.Generics (Generic)
 import Gargantext.Database.Config (nodeTypeId)
-import Gargantext.Database.Utils (mkCmd, Cmd(..))
+import Gargantext.Database.Utils (Cmd, runPGSQuery)
 import Gargantext.Database.Node.Contact -- (HyperdataContact(..), ContactWho(..))
 import Gargantext.Database.Types.Node
 import Gargantext.Prelude
@@ -113,8 +113,9 @@ import Database.PostgreSQL.Simple (formatQuery)
 
 data ToDbData = ToDbDocument HyperdataDocument | ToDbContact HyperdataContact
 
-insertDocuments :: UserId -> ParentId -> NodeType -> [ToDbData] -> Cmd [ReturnId]
-insertDocuments uId pId nodeType hs = mkCmd $ \c -> query c queryInsert (Only $ Values fields $ prepare uId pId nodeType hs)
+insertDocuments :: UserId -> ParentId -> NodeType -> [ToDbData] -> Cmd err [ReturnId]
+insertDocuments uId pId nodeType =
+    runPGSQuery queryInsert . Only . Values fields . prepare uId pId nodeType
   where
     fields    = map (\t-> QualifiedIdentifier Nothing t) inputSqlTypes
 
@@ -123,7 +124,7 @@ insertDocuments uId pId nodeType hs = mkCmd $ \c -> query c queryInsert (Only $
 -- to print rendered query (Debug purpose) use @formatQuery@ function.
 {-
 insertDocuments_Debug :: (Hyperdata a, ToJSON a, ToRow a) => UserId -> ParentId -> [a] -> Cmd ByteString
-insertDocuments_Debug uId pId hs = mkCmd $ \conn -> formatQuery conn queryInsert (Only $ Values fields inputData)
+insertDocuments_Debug uId pId hs = formatPGSQuery queryInsert (Only $ Values fields inputData)
   where
     fields    = map (\t-> QualifiedIdentifier Nothing t) inputSqlTypes
     inputData = prepare uId pId hs
diff --git a/src/Gargantext/Database/Node/Update.hs b/src/Gargantext/Database/Node/Update.hs
index ecf2df2f7a8360c4c066ac85d3a97e98d1f5a59c..d30b5f2d21778e41c891e804787e41c7a2731025 100644
--- a/src/Gargantext/Database/Node/Update.hs
+++ b/src/Gargantext/Database/Node/Update.hs
@@ -10,8 +10,9 @@ Portability : POSIX
 -}
 
 {-# LANGUAGE NoImplicitPrelude #-}
-{-# LANGUAGE QuasiQuotes #-}
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE QuasiQuotes       #-}
+{-# LANGUAGE RankNTypes        #-}
 
 
 module Gargantext.Database.Node.Update (Update(..), update) where
@@ -21,10 +22,11 @@ import qualified Data.Text as DT
 import Database.PostgreSQL.Simple
 
 import Gargantext.Prelude
+import Gargantext.Database.Utils
 
 -- import Data.ByteString
---rename :: Connection -> NodeId -> Text -> IO ByteString
---rename conn nodeId name = formatQuery conn "UPDATE nodes SET name=? where id=?" (name,nodeId)
+--rename :: NodeId -> Text -> IO ByteString
+--rename nodeId name = formatPGSQuery "UPDATE nodes SET name=? where id=?" (name,nodeId)
 ------------------------------------------------------------------------
 type NodeId = Int
 type Name   = Text
@@ -41,10 +43,10 @@ data Update = Rename NodeId Name
 unOnly :: Only a -> a
 unOnly (Only a) = a
 
-update :: Update -> Connection -> IO [Int]
-update (Rename nId name) conn = map unOnly <$> query conn "UPDATE nodes SET name=? where id=? returning id"
+update :: Update -> Cmd err [Int]
+update (Rename nId name) = map unOnly <$> runPGSQuery "UPDATE nodes SET name=? where id=? returning id"
                                            (DT.take 255 name,nId)
-update (Move nId pId) conn    = map unOnly <$> query conn "UPDATE nodes SET parent_id= ? where id=? returning id"
+update (Move nId pId)    = map unOnly <$> runPGSQuery "UPDATE nodes SET parent_id= ? where id=? returning id"
                                            (pId, nId)
 
 
diff --git a/src/Gargantext/Database/Root.hs b/src/Gargantext/Database/Root.hs
index 22715ed7a72afcb5963a63ae189ec0d937b5cd5b..e997bdf1f79fcc779726c74ffeef302c77fdb65d 100644
--- a/src/Gargantext/Database/Root.hs
+++ b/src/Gargantext/Database/Root.hs
@@ -21,12 +21,12 @@ Portability : POSIX
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE MultiParamTypeClasses  #-}
 {-# LANGUAGE NoImplicitPrelude      #-}
+{-# LANGUAGE RankNTypes             #-}
 {-# LANGUAGE TemplateHaskell        #-}
 
 module Gargantext.Database.Root where
 
-import Database.PostgreSQL.Simple (Connection)
-import Opaleye (restrict, (.==), Query, runQuery)
+import Opaleye (restrict, (.==), Query)
 import Opaleye.PGTypes (pgStrictText, pgInt4)
 import Control.Arrow (returnA)
 import Gargantext.Prelude
@@ -36,13 +36,10 @@ import Gargantext.Database.Schema.Node (queryNodeTable)
 import Gargantext.Database.Schema.User (queryUserTable, UserPoly(..))
 import Gargantext.Database.Config (nodeTypeId)
 import Gargantext.Core.Types.Individu (Username)
-import Gargantext.Database.Utils (Cmd(..), mkCmd)
+import Gargantext.Database.Utils (Cmd, runOpaQuery)
 
-getRootCmd :: Username -> Cmd [Node HyperdataUser]
-getRootCmd u = mkCmd $ \c -> getRoot u c
-
-getRoot :: Username -> Connection -> IO [Node HyperdataUser]
-getRoot uname conn = runQuery conn (selectRoot uname)
+getRoot :: Username -> Cmd err [Node HyperdataUser]
+getRoot = runOpaQuery . selectRoot
 
 selectRoot :: Username -> Query NodeRead
 selectRoot username = proc () -> do
diff --git a/src/Gargantext/Database/Schema/Ngrams.hs b/src/Gargantext/Database/Schema/Ngrams.hs
index a1071b345350f8b0e9f5e2eca05678b42213165f..d678cae6698afd6be9d1d5ae1c983a185d806a9e 100644
--- a/src/Gargantext/Database/Schema/Ngrams.hs
+++ b/src/Gargantext/Database/Schema/Ngrams.hs
@@ -19,6 +19,7 @@ Ngrams connection to the Database.
 {-# LANGUAGE NoImplicitPrelude      #-}
 {-# LANGUAGE OverloadedStrings      #-}
 {-# LANGUAGE QuasiQuotes            #-}
+{-# LANGUAGE RankNTypes             #-}
 {-# LANGUAGE TemplateHaskell        #-}
 
 module Gargantext.Database.Schema.Ngrams where
@@ -43,12 +44,12 @@ import Gargantext.Database.Config (nodeTypeId,userMaster)
 import Gargantext.Database.Root (getRoot)
 import Gargantext.Database.Types.Node (NodeType)
 import Gargantext.Database.Schema.Node (getListsWithParentId, getCorporaWithParentId)
-import Gargantext.Database.Utils (mkCmd, Cmd(..))
+import Gargantext.Database.Utils (Cmd, runPGSQuery, runOpaQuery, formatPGSQuery)
 import Gargantext.Prelude
 import Opaleye
 import Prelude (Enum, Bounded, minBound, maxBound)
 import qualified Data.Set as DS
-import qualified Database.PostgreSQL.Simple as DPS
+import qualified Database.PostgreSQL.Simple as PGS
 
 --{-
 data NgramsPoly id terms n = NgramsDb { ngrams_id    :: id
@@ -85,8 +86,8 @@ ngramsTable = Table "ngrams" (pNgramsDb NgramsDb { ngrams_id    = optional "id"
 queryNgramsTable :: Query NgramsRead
 queryNgramsTable = queryTable ngramsTable
 
-dbGetNgramsDb :: DPS.Connection -> IO [NgramsDb]
-dbGetNgramsDb conn = runQuery conn queryNgramsTable
+dbGetNgramsDb :: Cmd err [NgramsDb]
+dbGetNgramsDb = runOpaQuery queryNgramsTable
 --}
 
 -- | Main Ngrams Types
@@ -118,7 +119,7 @@ data Ngrams = Ngrams { _ngramsTerms :: Text
            } deriving (Generic, Show, Eq, Ord)
 
 makeLenses ''Ngrams
-instance DPS.ToRow Ngrams where
+instance PGS.ToRow Ngrams where
   toRow (Ngrams t s) = [toField t, toField s]
 
 text2ngrams :: Text -> Ngrams
@@ -148,7 +149,7 @@ data NgramIds =
   , ngramTerms :: Text
   } deriving (Show, Generic, Eq, Ord)
 
-instance DPS.FromRow NgramIds where
+instance PGS.FromRow NgramIds where
   fromRow = NgramIds <$> field <*> field
 
 ----------------------
@@ -160,21 +161,21 @@ indexNgramsT m ngrId = indexNgramsTWith f ngrId
 indexNgramsTWith :: (NgramsTerms -> NgramsId) -> NgramsT Ngrams-> NgramsT NgramsIndexed
 indexNgramsTWith f (NgramsT t n) = NgramsT t (NgramsIndexed n ((f . _ngramsTerms) n))
 
-insertNgrams :: [Ngrams] -> Cmd (Map NgramsTerms NgramsId)
+insertNgrams :: [Ngrams] -> Cmd err (Map NgramsTerms NgramsId)
 insertNgrams ns = fromList <$> map (\(NgramIds i t) -> (t, i)) <$> (insertNgrams' ns)
 
-insertNgrams' :: [Ngrams] -> Cmd [NgramIds]
-insertNgrams' ns = mkCmd $ \conn -> DPS.query conn queryInsertNgrams (DPS.Only $ Values fields ns)
+insertNgrams' :: [Ngrams] -> Cmd err [NgramIds]
+insertNgrams' ns = runPGSQuery queryInsertNgrams (PGS.Only $ Values fields ns)
   where
     fields = map (\t -> QualifiedIdentifier Nothing t) ["text", "int4"]
 
-insertNgrams_Debug :: [(NgramsTerms, Size)] -> Cmd ByteString
-insertNgrams_Debug ns = mkCmd $ \conn -> DPS.formatQuery conn queryInsertNgrams (DPS.Only $ Values fields ns)
+insertNgrams_Debug :: [(NgramsTerms, Size)] -> Cmd err ByteString
+insertNgrams_Debug ns = formatPGSQuery queryInsertNgrams (PGS.Only $ Values fields ns)
   where
     fields = map (\t -> QualifiedIdentifier Nothing t) ["text", "int4"]
 
 ----------------------
-queryInsertNgrams :: DPS.Query
+queryInsertNgrams :: PGS.Query
 queryInsertNgrams = [sql|
     WITH input_rows(terms,n) AS (?)
     , ins AS (
@@ -197,26 +198,25 @@ queryInsertNgrams = [sql|
 -- TODO: the way we are getting main Master Corpus and List ID is not clean
 -- TODO: if ids are not present -> create
 -- TODO: Global Env State Monad to keep in memory the ids without retrieving it each time
-getNgramsTableDb :: DPS.Connection
-               -> NodeType             -> NgramsType
-               -> NgramsTableParamUser
-               -> Limit -> Offset
-               -> IO ([NgramsTableData], MapToParent, MapToChildren)
-getNgramsTableDb c nt ngrt ntp@(NgramsTableParam listIdUser _) limit_ offset_ = do
+getNgramsTableDb :: NodeType -> NgramsType
+                 -> NgramsTableParamUser
+                 -> Limit -> Offset
+                 -> Cmd err ([NgramsTableData], MapToParent, MapToChildren)
+getNgramsTableDb nt ngrt ntp@(NgramsTableParam listIdUser _) limit_ offset_ = do
   
   
-  maybeRoot <- head <$> getRoot userMaster c
+  maybeRoot <- head <$> getRoot userMaster
   let path = "Garg.Db.Ngrams.getTableNgrams: "
   let masterRootId = maybe (panic $ path <> "no userMaster Tree") (view node_id) maybeRoot
   -- let errMess = panic "Error"
 
-  corpusMasterId <- maybe (panic "error master corpus") (view node_id) <$> head <$> getCorporaWithParentId c masterRootId
+  corpusMasterId <- maybe (panic "error master corpus") (view node_id) <$> head <$> getCorporaWithParentId masterRootId
   
-  listMasterId   <- maybe (panic "error master list") (view node_id) <$> head <$> getListsWithParentId   c corpusMasterId
+  listMasterId   <- maybe (panic "error master list") (view node_id) <$> head <$> getListsWithParentId corpusMasterId
   
-  ngramsTableData <- getNgramsTableData c nt ngrt ntp (NgramsTableParam listMasterId corpusMasterId) limit_ offset_
+  ngramsTableData <- getNgramsTableData nt ngrt ntp (NgramsTableParam listMasterId corpusMasterId) limit_ offset_
   
-  (mapToParent,mapToChildren) <- getNgramsGroup c listIdUser listMasterId
+  (mapToParent,mapToChildren) <- getNgramsGroup listIdUser listMasterId
   pure (ngramsTableData, mapToParent,mapToChildren)
 
 
@@ -234,15 +234,14 @@ data NgramsTableData = NgramsTableData { _ntd_ngrams   :: Text
                                        , _ntd_weight   :: Double
     } deriving (Show)
 
-getNgramsTableData :: DPS.Connection
-                   -> NodeType -> NgramsType
+getNgramsTableData :: NodeType -> NgramsType
                    -> NgramsTableParamUser -> NgramsTableParamMaster 
                    -> Limit -> Offset
-                   -> IO [NgramsTableData]
-getNgramsTableData conn nodeT ngrmT (NgramsTableParam ul uc) (NgramsTableParam ml mc) limit_ offset_ =
+                   -> Cmd err [NgramsTableData]
+getNgramsTableData nodeT ngrmT (NgramsTableParam ul uc) (NgramsTableParam ml mc) limit_ offset_ =
   trace ("Ngrams table params" <> show params) <$>
   map (\(t,n,nt,w) -> NgramsTableData t n (fromListTypeId nt) w) <$>
-    DPS.query conn querySelectTableNgrams params
+    runPGSQuery querySelectTableNgrams params
       where
         nodeTId = nodeTypeId   nodeT
         ngrmTId = ngramsTypeId ngrmT
@@ -251,7 +250,7 @@ getNgramsTableData conn nodeT ngrmT (NgramsTableParam ul uc) (NgramsTableParam m
 
 
 
-querySelectTableNgrams :: DPS.Query
+querySelectTableNgrams :: PGS.Query
 querySelectTableNgrams = [sql|
 
     WITH tableUser AS (
@@ -296,20 +295,14 @@ type ListIdMaster = Int
 type MapToChildren = Map Text (Set Text)
 type MapToParent   = Map Text Text
 
-getNgramsGroup :: DPS.Connection -> ListIdUser -> ListIdMaster -> IO (MapToParent, MapToChildren)
-getNgramsGroup conn lu lm = do
-  groups <- getNgramsGroup' conn lu lm
+getNgramsGroup :: ListIdUser -> ListIdMaster -> Cmd err (MapToParent, MapToChildren)
+getNgramsGroup lu lm = do
+  groups <- runPGSQuery querySelectNgramsGroup (lu,lm)
   let mapChildren = fromListWith (<>) $ map (\(a,b) -> (a, DS.singleton b)) groups
   let mapParent   = fromListWith (<>) $ map (\(a,b) -> (b, a)) groups
   pure (mapParent, mapChildren)
 
-getNgramsGroup' :: DPS.Connection -> ListIdUser -> ListIdMaster -> IO [(Text,Text)]
-getNgramsGroup' conn lu lm = DPS.query conn querySelectNgramsGroup (lu,lm)
-
-getNgramsGroup'' :: ListIdUser -> ListIdMaster -> Cmd [(Text, Text)]
-getNgramsGroup'' lu lm = mkCmd $ \conn -> DPS.query conn querySelectNgramsGroup (lu,lm)
-
-querySelectNgramsGroup :: DPS.Query
+querySelectNgramsGroup :: PGS.Query
 querySelectNgramsGroup = [sql|
     WITH groupUser AS (
       SELECT n1.terms AS t1, n2.terms AS t2 FROM nodes_ngrams_ngrams nnn
diff --git a/src/Gargantext/Database/Schema/Node.hs b/src/Gargantext/Database/Schema/Node.hs
index ce66e54d6b60e7a27da218fdcabfc7f004fb6af6..f3315223ae41709288765328712ef0136d6c5185 100644
--- a/src/Gargantext/Database/Schema/Node.hs
+++ b/src/Gargantext/Database/Schema/Node.hs
@@ -20,20 +20,21 @@ Portability : POSIX
 {-# LANGUAGE OverloadedStrings      #-}
 {-# LANGUAGE MultiParamTypeClasses  #-}
 {-# LANGUAGE NoImplicitPrelude      #-}
+{-# LANGUAGE RankNTypes             #-}
 {-# LANGUAGE TemplateHaskell        #-}
 
 module Gargantext.Database.Schema.Node where
 
 import Control.Arrow (returnA)
-import Control.Lens (set)
+import Control.Lens (Prism', set, view, (#), (^?))
 import Control.Lens.TH (makeLensesWith, abbreviatedFields)
+import Control.Monad.Error.Class (MonadError(..))
 import Data.Aeson
 import Data.ByteString (ByteString)
 import Data.Maybe (Maybe(..), fromMaybe)
 import Data.Profunctor.Product.TH (makeAdaptorAndInstance)
 import Data.Text (Text, pack)
 import Data.Time (UTCTime)
-import Database.PostgreSQL.Simple (Connection)
 import Database.PostgreSQL.Simple.FromField (FromField, fromField)
 import GHC.Int (Int64)
 import Gargantext.Core (Lang(..))
@@ -53,8 +54,26 @@ import qualified Data.ByteString.Lazy as DBL
 import qualified Data.Profunctor.Product as PP
 
 ------------------------------------------------------------------------
-instance FromField HyperdataAny
-  where
+
+data NodeError = NoListFound
+  deriving (Show)
+
+class HasNodeError e where
+  _NodeError :: Prism' e NodeError
+
+nodeError :: (MonadError e m, HasNodeError e) => NodeError -> m a
+nodeError ne = throwError $ _NodeError # ne
+
+catchNodeError :: (MonadError e m, HasNodeError e) => m a -> (NodeError -> m a) -> m a
+catchNodeError f g = catchError f (\e -> maybe (throwError e) g (e ^? _NodeError))
+
+------------------------------------------------------------------------
+type AnnuaireId = Int
+
+type DocId  = Int
+type TypeId = Int
+------------------------------------------------------------------------
+instance FromField HyperdataAny where
     fromField = fromField'
 
 instance FromField HyperdataCorpus
@@ -267,8 +286,8 @@ selectNode id = proc () -> do
     returnA -< row
 
 
-runGetNodes :: Query NodeRead -> Cmd [NodeAny]
-runGetNodes q = mkCmd $ \conn -> runQuery conn q
+runGetNodes :: Query NodeRead -> Cmd err [NodeAny]
+runGetNodes = runOpaQuery
 
 ------------------------------------------------------------------------
 ------------------------------------------------------------------------
@@ -298,53 +317,43 @@ selectNodesWith' parentId maybeNodeType = proc () -> do
     returnA -< node
 
 
-
--- deleteNode :: (MonadReader Connection m, MonadIO m) => Int -> m Int
--- deleteNode :: Int -> Cmd' Int
-
-deleteNode :: Int -> Cmd Int
+deleteNode :: Int -> Cmd err Int
 deleteNode n = mkCmd $ \conn ->
   fromIntegral <$> runDelete conn nodeTable
                  (\(Node n_id _ _ _ _ _ _) -> n_id .== pgInt4 n)
 
-deleteNodes :: [Int] -> Cmd Int
+deleteNodes :: [Int] -> Cmd err Int
 deleteNodes ns = mkCmd $ \conn ->
   fromIntegral <$> runDelete conn nodeTable
                    (\(Node n_id _ _ _ _ _ _) -> in_ ((map pgInt4 ns)) n_id)
 
 
-getNodesWith :: JSONB a => Connection -> Int -> proxy a -> Maybe NodeType
-             -> Maybe Offset -> Maybe Limit -> IO [Node a]
-getNodesWith conn parentId _ nodeType maybeOffset maybeLimit =
-    runQuery conn $ selectNodesWith parentId nodeType maybeOffset maybeLimit
-
+-- TODO: NodeType should match with `a'
+getNodesWith :: JSONB a => Int -> proxy a -> Maybe NodeType
+             -> Maybe Offset -> Maybe Limit -> Cmd err [Node a]
+getNodesWith parentId _ nodeType maybeOffset maybeLimit =
+    runOpaQuery $ selectNodesWith parentId nodeType maybeOffset maybeLimit
 
--- NP check type
-getNodesWithParentId :: Int
-                     -> Maybe Text -> Connection -> IO [NodeAny]
-getNodesWithParentId n _ conn = runQuery conn $ selectNodesWithParentID n
 
-getNodesWithParentId' :: Int
-                     -> Maybe Text -> Connection -> IO [NodeAny]
-getNodesWithParentId' n _ conn = runQuery conn $ selectNodesWithParentID n
+-- TODO: Why is the second parameter ignored?
+-- TODO: Why not use getNodesWith?
+getNodesWithParentId :: Int -> Maybe Text -> Cmd err [NodeAny]
+getNodesWithParentId n _ = runOpaQuery $ selectNodesWithParentID n
 
 
 ------------------------------------------------------------------------
-getDocumentsV3WithParentId :: Connection -> Int -> IO [Node HyperdataDocumentV3]
-getDocumentsV3WithParentId conn n = runQuery conn $ selectNodesWith' n (Just NodeDocument)
-
-getDocumentsWithParentId :: Connection -> Int -> IO [Node HyperdataDocument]
-getDocumentsWithParentId conn n = runQuery conn $ selectNodesWith' n (Just NodeDocument)
+getDocumentsV3WithParentId :: Int -> Cmd err [Node HyperdataDocumentV3]
+getDocumentsV3WithParentId n = runOpaQuery $ selectNodesWith' n (Just NodeDocument)
 
-getListsWithParentId :: Connection -> Int -> IO [Node HyperdataList]
-getListsWithParentId conn n = runQuery conn $ selectNodesWith' n (Just NodeList)
+-- TODO: merge with getDocumentsWithParentId by having a class IsHyperdataDocument
+getDocumentsWithParentId :: Int -> Cmd err [Node HyperdataDocument]
+getDocumentsWithParentId n = runOpaQuery $ selectNodesWith' n (Just NodeDocument)
 
-getCorporaWithParentId :: Connection -> Int -> IO [Node HyperdataCorpus]
-getCorporaWithParentId conn n = runQuery conn $ selectNodesWith' n (Just NodeCorpus)
-
-getCorporaWithParentId' :: Int -> Cmd [Node HyperdataCorpus]
-getCorporaWithParentId' n = mkCmd $ \conn -> runQuery conn $ selectNodesWith' n (Just NodeCorpus)
+getListsWithParentId :: Int -> Cmd err [Node HyperdataList]
+getListsWithParentId n = runOpaQuery $ selectNodesWith' n (Just NodeList)
 
+getCorporaWithParentId :: Int -> Cmd err [Node HyperdataCorpus]
+getCorporaWithParentId n = runOpaQuery $ selectNodesWith' n (Just NodeCorpus)
 
 ------------------------------------------------------------------------
 selectNodesWithParentID :: Int -> Query NodeRead
@@ -363,13 +372,12 @@ selectNodesWithType type_id = proc () -> do
 
 type JSONB = QueryRunnerColumnDefault PGJsonb
 
-getNode :: JSONB a => Connection -> Int -> proxy a -> IO (Node a)
-getNode conn id _ = do
-    fromMaybe (error $ "Node does node exist: " <> show id) . headMay <$> runQuery conn (limit 1 $ selectNode (pgInt4 id))
+getNode :: JSONB a => Int -> proxy a -> Cmd err (Node a)
+getNode id _ = do
+    fromMaybe (error $ "Node does node exist: " <> show id) . headMay <$> runOpaQuery (limit 1 $ selectNode (pgInt4 id))
 
-getNodesWithType :: Connection -> Column PGInt4 -> IO [Node HyperdataDocument]
-getNodesWithType conn type_id = do
-    runQuery conn $ selectNodesWithType type_id
+getNodesWithType :: Column PGInt4 -> Cmd err [Node HyperdataDocument]
+getNodesWithType = runOpaQuery . selectNodesWithType
 
 ------------------------------------------------------------------------
 
@@ -476,20 +484,19 @@ node2row (Node id tn ud pid nm dt hp) = ((pgInt4       <$> id)
                                            ,(pgStrictJSONB    hp)
                                            )
 ------------------------------------------------------------------------
-insertNodesR' :: [NodeWrite'] -> Cmd [Int]
-insertNodesR' ns = mkCmd $ \c -> insertNodesR ns c
+insertNodes :: [NodeWrite'] -> Cmd err Int64
+insertNodes ns = mkCmd $ \conn ->
+  runInsertMany conn nodeTable' (map node2row ns)
 
-insertNodes :: [NodeWrite'] -> Connection -> IO Int64
-insertNodes ns conn = runInsertMany conn nodeTable' (map node2row ns)
+insertNodesR :: [NodeWrite'] -> Cmd err [Int]
+insertNodesR ns = mkCmd $ \conn ->
+  runInsert_ conn (Insert nodeTable' (node2row <$> ns) (rReturning (\(i,_,_,_,_,_,_) -> i)) Nothing)
 
-insertNodesR :: [NodeWrite'] -> Connection -> IO [Int]
-insertNodesR ns conn = runInsertManyReturning conn nodeTable' (map node2row ns) (\(i,_,_,_,_,_,_) -> i)
-                       -------------------------
-insertNodesWithParent :: Maybe ParentId -> [NodeWrite'] -> Connection -> IO Int64
-insertNodesWithParent pid ns conn = insertNodes (map (set node_parentId pid) ns) conn
+insertNodesWithParent :: Maybe ParentId -> [NodeWrite'] -> Cmd err Int64
+insertNodesWithParent pid ns = insertNodes (set node_parentId pid <$> ns)
 
-insertNodesWithParentR :: Maybe ParentId -> [NodeWrite'] -> Connection -> IO [Int]
-insertNodesWithParentR pid ns conn = insertNodesR (map (set node_parentId pid) ns) conn
+insertNodesWithParentR :: Maybe ParentId -> [NodeWrite'] -> Cmd err [Int]
+insertNodesWithParentR pid ns = insertNodesR (set node_parentId pid <$> ns)
 ------------------------------------------------------------------------
 -- TODO Hierachy of Nodes
 -- post and get same types Node' and update if changes
@@ -529,14 +536,14 @@ type NodeWriteT =  ( Maybe (Column PGInt4)
                    ,        Column PGJsonb
                    )
 
-mkNode' :: [NodeWrite] -> Cmd Int64
+mkNode' :: [NodeWrite] -> Cmd err Int64
 mkNode' ns = mkCmd $ \conn -> runInsertMany conn nodeTable ns
 
 -- TODO: replace mkNodeR'
-mkNodeR'' :: [NodeWrite] -> Cmd [Int]
+mkNodeR'' :: [NodeWrite] -> Cmd err [Int]
 mkNodeR'' ns = mkCmd $ \conn -> runInsertManyReturning conn nodeTable ns (_node_id)
 
-mkNodeR' :: [NodeWriteT] -> Cmd [Int]
+mkNodeR' :: [NodeWriteT] -> Cmd err [Int]
 mkNodeR' ns = mkCmd $ \conn -> runInsertManyReturning conn nodeTable' ns (\(i,_,_,_,_,_,_) -> i)
 
 ------------------------------------------------------------------------
@@ -545,7 +552,7 @@ data NewNode = NewNode { _newNodeId :: Int
                        , _newNodeChildren :: [Int] }
 
 -- | postNode
-postNode :: UserId -> Maybe ParentId -> Node' -> Cmd NewNode
+postNode :: UserId -> Maybe ParentId -> Node' -> Cmd err NewNode
 postNode uid pid (Node' nt txt v []) = do
   pids <- mkNodeR' [node2table uid pid (Node' nt txt v [])]
   case pids of
@@ -571,72 +578,53 @@ childWith _   _   (Node' _        _   _ _) = panic "This NodeType can not be a c
 
 
 
-mk :: Connection -> NodeType -> Maybe ParentId -> Text -> IO [Int]
-mk c nt pId name  = mk' c nt userId pId name
+mk :: NodeType -> Maybe ParentId -> Text -> Cmd err [Int]
+mk nt pId name  = mk' nt userId pId name
   where
     userId = 1
 
-mk' :: Connection -> NodeType -> UserId -> Maybe ParentId -> Text -> IO [Int]
-mk' c nt uId pId name  = map fromIntegral <$> insertNodesWithParentR pId [node nt name hd pId uId] c
+mk' :: NodeType -> UserId -> Maybe ParentId -> Text -> Cmd err [Int]
+mk' nt uId pId name  = map fromIntegral <$> insertNodesWithParentR pId [node nt name hd pId uId]
   where
     hd = HyperdataUser . Just . pack $ show EN
 
 type Name = Text
 
-mk'' :: NodeType -> Maybe ParentId -> UserId -> Name -> Cmd [Int]
-mk'' NodeUser Nothing uId name  = mkCmd $ \c -> mk' c NodeUser uId Nothing name
+mk'' :: NodeType -> Maybe ParentId -> UserId -> Name -> Cmd err [Int]
+mk'' NodeUser Nothing uId name  = mk' NodeUser uId Nothing name
 mk'' NodeUser _       _   _     = panic "NodeUser do not have any parent"
 mk'' _        Nothing _   _     = panic "NodeType does   have a   parent"
-mk'' nt       pId     uId name  = mkCmd $ \c -> mk' c nt uId pId name
-
+mk'' nt       pId     uId name  = mk' nt uId pId name
 
-mkRoot :: Username -> UserId -> Cmd [Int]
+mkRoot :: Username -> UserId -> Cmd err [Int]
 mkRoot uname uId = case uId > 0 of
                False -> panic "UserId <= 0"
                True  -> mk'' NodeUser Nothing uId uname
 
-mkCorpus :: Maybe Name -> Maybe HyperdataCorpus -> ParentId -> UserId -> Cmd [Int]
-mkCorpus n h p u = insertNodesR' [nodeCorpusW n h p u]
+mkCorpus :: Maybe Name -> Maybe HyperdataCorpus -> ParentId -> UserId -> Cmd err [Int]
+mkCorpus n h p u = insertNodesR [nodeCorpusW n h p u]
 
---{-
-getOrMkList :: ParentId -> UserId -> Cmd Int
-getOrMkList pId uId = do
-  maybeList <- defaultListSafe' pId
-  case maybeList of
-    Nothing -> maybe (panic "no list") identity <$> headMay <$> mkList pId uId
-    Just x  -> pure x
-
-defaultListSafe' :: CorpusId -> Cmd (Maybe ListId)
-defaultListSafe' cId = mkCmd $ \c -> do
-  maybeNode <- headMay <$> getListsWithParentId c cId
-  case maybeNode of
-    Nothing -> pure Nothing
-    (Just node) -> pure $ Just $ _node_id node
---}
+getOrMkList :: HasNodeError err => ParentId -> UserId -> Cmd err Int
+getOrMkList pId uId =
+  defaultList pId
+    `catchNodeError`
+  (\NoListFound -> maybe (nodeError NoListFound) pure . headMay =<< mkList pId uId)
 
-defaultListSafe :: Connection -> CorpusId -> IO (Maybe ListId)
-defaultListSafe c cId = do
-  maybeNode <- headMay <$> getListsWithParentId c cId
-  case maybeNode of
-    Nothing -> pure Nothing
-    (Just node) -> pure $ Just $ _node_id node
-
-defaultList :: Connection -> CorpusId -> IO ListId
-defaultList c cId = maybe (panic errMessage) identity <$> defaultListSafe c cId
-  where
-    errMessage = "Gargantext.API.Ngrams.defaultList: no list found"
+defaultList :: HasNodeError err => CorpusId -> Cmd err ListId
+defaultList cId =
+  maybe (nodeError NoListFound) (pure . view node_id) . headMay =<< getListsWithParentId cId
 
-mkList :: ParentId -> UserId -> Cmd [Int]
-mkList p u = insertNodesR' [nodeListW Nothing Nothing p u]
+mkList :: HasNodeError err => ParentId -> UserId -> Cmd err [Int]
+mkList p u = insertNodesR [nodeListW Nothing Nothing p u]
 
-mkGraph :: ParentId -> UserId -> Cmd [Int]
-mkGraph p u = insertNodesR' [nodeGraphW Nothing Nothing p u]
+mkGraph :: ParentId -> UserId -> Cmd err [Int]
+mkGraph p u = insertNodesR [nodeGraphW Nothing Nothing p u]
 
-mkDashboard :: ParentId -> UserId -> Cmd [Int]
-mkDashboard p u = insertNodesR' [nodeDashboardW Nothing Nothing p u]
+mkDashboard :: ParentId -> UserId -> Cmd err [Int]
+mkDashboard p u = insertNodesR [nodeDashboardW Nothing Nothing p u]
 
-mkAnnuaire :: ParentId -> UserId -> Cmd [Int]
-mkAnnuaire p u = insertNodesR' [nodeAnnuaireW Nothing Nothing p u]
+mkAnnuaire :: ParentId -> UserId -> Cmd err [Int]
+mkAnnuaire p u = insertNodesR [nodeAnnuaireW Nothing Nothing p u]
 
 -- | Default CorpusId Master and ListId Master
 
diff --git a/src/Gargantext/Database/Schema/NodeNgram.hs b/src/Gargantext/Database/Schema/NodeNgram.hs
index 0beaf5e2c9f0d0d4d8880d1f111c1c38d4e3b0eb..84a337803ac0ad38ad867f88b4153039bdf728f5 100644
--- a/src/Gargantext/Database/Schema/NodeNgram.hs
+++ b/src/Gargantext/Database/Schema/NodeNgram.hs
@@ -23,6 +23,7 @@ if Node is a List     then it is listing (either Stop, Candidate or Map)
 {-# LANGUAGE NoImplicitPrelude      #-}
 {-# LANGUAGE OverloadedStrings      #-}
 {-# LANGUAGE QuasiQuotes            #-}
+{-# LANGUAGE RankNTypes             #-}
 {-# LANGUAGE TemplateHaskell        #-}
 
 
@@ -35,10 +36,10 @@ import Data.Profunctor.Product.TH (makeAdaptorAndInstance)
 import Database.PostgreSQL.Simple.Types (Values(..), QualifiedIdentifier(..))
 import Database.PostgreSQL.Simple.SqlQQ (sql)
 import Gargantext.Core.Types.Main (ListId, ListTypeId)
-import Gargantext.Database.Utils (mkCmd, Cmd(..))
+import Gargantext.Database.Utils (mkCmd, Cmd, runPGSQuery)
 import Gargantext.Prelude
 import Opaleye
-import qualified Database.PostgreSQL.Simple as PGS (Connection, query, Only(..))
+import qualified Database.PostgreSQL.Simple as PGS (Only(..))
 
 -- | TODO : remove id
 data NodeNgramPoly id node_id ngram_id weight ngrams_type
@@ -94,14 +95,14 @@ nodeNgramTable  = Table "nodes_ngrams"
 queryNodeNgramTable :: Query NodeNgramRead
 queryNodeNgramTable = queryTable nodeNgramTable
 
-insertNodeNgrams :: [NodeNgram] -> Cmd Int
+insertNodeNgrams :: [NodeNgram] -> Cmd err Int
 insertNodeNgrams = insertNodeNgramW
                  . map (\(NodeNgram _ n g w t) ->
                           NodeNgram Nothing (pgInt4 n)   (pgInt4 g)
                                             (pgDouble w) (pgInt4 t)
                         )
 
-insertNodeNgramW :: [NodeNgramWrite] -> Cmd Int
+insertNodeNgramW :: [NodeNgramWrite] -> Cmd err Int
 insertNodeNgramW nns =
   mkCmd $ \c -> fromIntegral <$> runInsert_ c insertNothing
     where
@@ -113,8 +114,8 @@ insertNodeNgramW nns =
 
 type NgramsText = Text
 
-updateNodeNgrams :: PGS.Connection -> [(ListId, NgramsText, ListTypeId)] -> IO [PGS.Only Int]
-updateNodeNgrams c input = PGS.query c updateQuery (PGS.Only $ Values fields $ input)
+updateNodeNgrams :: [(ListId, NgramsText, ListTypeId)] -> Cmd err [PGS.Only Int]
+updateNodeNgrams input = runPGSQuery updateQuery (PGS.Only $ Values fields $ input)
   where
     fields = map (\t-> QualifiedIdentifier Nothing t) ["int4","text","int4"]
     updateQuery = [sql| UPDATE nodes_ngrams as old SET
diff --git a/src/Gargantext/Database/Schema/NodeNgramsNgrams.hs b/src/Gargantext/Database/Schema/NodeNgramsNgrams.hs
index 305841d53a3543686c63ad17e60d32945e439432..285d007bef532c10cf14f23e193cb3cb45dd4aca 100644
--- a/src/Gargantext/Database/Schema/NodeNgramsNgrams.hs
+++ b/src/Gargantext/Database/Schema/NodeNgramsNgrams.hs
@@ -25,22 +25,25 @@ Next Step benchmark:
 {-# LANGUAGE NoImplicitPrelude      #-}
 {-# LANGUAGE OverloadedStrings      #-}
 {-# LANGUAGE QuasiQuotes            #-}
+{-# LANGUAGE RankNTypes             #-}
 {-# LANGUAGE TemplateHaskell        #-}
 {-# OPTIONS_GHC -fno-warn-orphans   #-}
 
 module Gargantext.Database.Schema.NodeNgramsNgrams
   where
 
+import Control.Lens (view)
 import Control.Lens.TH (makeLensesWith, abbreviatedFields)
+import Control.Monad.IO.Class (liftIO)
 import Data.Text (Text)
 import Data.Maybe (Maybe)
 import Data.Profunctor.Product.TH (makeAdaptorAndInstance)
 import Database.PostgreSQL.Simple.SqlQQ (sql)
 import Database.PostgreSQL.Simple.Types (Values(..), QualifiedIdentifier(..))
-import Gargantext.Database.Utils (mkCmd, Cmd(..))
+import Gargantext.Database.Utils (Cmd, runOpaQuery, runPGSQuery, connection)
 import Gargantext.Prelude
 import Opaleye
-import qualified Database.PostgreSQL.Simple as DPS
+import qualified Database.PostgreSQL.Simple as PGS
 
 data NodeNgramsNgramsPoly node_id ngram1_id ngram2_id weight =
   NodeNgramsNgrams { _nng_NodeId   :: node_id
@@ -90,8 +93,8 @@ queryNodeNgramsNgramsTable = queryTable nodeNgramsNgramsTable
 
 -- | Select NodeNgramsNgrams
 -- TODO not optimized (get all ngrams without filters)
-nodeNgramsNgrams :: DPS.Connection -> IO [NodeNgramsNgrams]
-nodeNgramsNgrams conn = runQuery conn queryNodeNgramsNgramsTable
+nodeNgramsNgrams :: Cmd err [NodeNgramsNgrams]
+nodeNgramsNgrams = runOpaQuery queryNodeNgramsNgramsTable
 
 instance QueryRunnerColumnDefault PGInt4 (Maybe Int) where
     queryRunnerColumnDefault = fieldQueryRunnerColumn
@@ -101,7 +104,7 @@ instance QueryRunnerColumnDefault PGFloat8 (Maybe Double) where
 
 
 -- TODO: Add option on conflict
-insertNodeNgramsNgramsNew :: [NodeNgramsNgrams] -> Cmd Int
+insertNodeNgramsNgramsNew :: [NodeNgramsNgrams] -> Cmd err Int
 insertNodeNgramsNgramsNew = insertNodeNgramsNgramsW
                  . map (\(NodeNgramsNgrams n ng1 ng2 maybeWeight) ->
                           NodeNgramsNgrams (pgInt4 n  )
@@ -110,10 +113,10 @@ insertNodeNgramsNgramsNew = insertNodeNgramsNgramsW
                                            (pgDouble <$> maybeWeight)
                         )
 
-insertNodeNgramsNgramsW :: [NodeNgramsNgramsWrite] -> Cmd Int
-insertNodeNgramsNgramsW ns =
-  mkCmd $ \c -> fromIntegral
-       <$> runInsertMany c nodeNgramsNgramsTable ns
+insertNodeNgramsNgramsW :: [NodeNgramsNgramsWrite] -> Cmd err Int
+insertNodeNgramsNgramsW ns = do
+  c <- view connection
+  liftIO $ fromIntegral <$> runInsertMany c nodeNgramsNgramsTable ns
 
 ------------------------------------------------------------------------
 data Action   = Del | Add
@@ -121,20 +124,17 @@ data Action   = Del | Add
 type NgramsParent = Text
 type NgramsChild  = Text
 
-ngramsGroup :: Action -> [(Int, NgramsParent, NgramsChild, Maybe Double)] -> Cmd [Int]
-ngramsGroup a ngs = mkCmd $ \c -> ngramsGroup' c a ngs
-
--- TODO: remove this function (use Reader Monad only)
-ngramsGroup' :: DPS.Connection -> Action -> [(Int, NgramsParent, NgramsChild, Maybe Double)] -> IO [Int]
-ngramsGroup' c action ngs = runNodeNgramsNgrams c q ngs
+ngramsGroup' :: Action -> [(Int, NgramsParent, NgramsChild, Maybe Double)]
+             -> Cmd err [Int]
+ngramsGroup' action ngs = runNodeNgramsNgrams q ngs
   where
     q = case action of
           Del -> queryDelNodeNgramsNgrams
           Add -> queryInsertNodeNgramsNgrams
 
 
-runNodeNgramsNgrams :: DPS.Connection -> DPS.Query -> [(Int, NgramsParent, NgramsChild, Maybe Double)] -> IO [Int]
-runNodeNgramsNgrams c q ngs = map (\(DPS.Only a) -> a) <$> DPS.query c q (DPS.Only $ Values fields ngs' )
+runNodeNgramsNgrams :: PGS.Query -> [(Int, NgramsParent, NgramsChild, Maybe Double)] -> Cmd err [Int]
+runNodeNgramsNgrams q ngs = map (\(PGS.Only a) -> a) <$> runPGSQuery q (PGS.Only $ Values fields ngs' )
   where
     ngs'   = map (\(n,ng1,ng2,w) -> (n,ng1,ng2,maybe 0 identity w)) ngs
     fields = map (\t -> QualifiedIdentifier Nothing t)
@@ -142,7 +142,7 @@ runNodeNgramsNgrams c q ngs = map (\(DPS.Only a) -> a) <$> DPS.query c q (DPS.On
 
 --------------------------------------------------------------------
 -- TODO: on conflict update weight
-queryInsertNodeNgramsNgrams :: DPS.Query
+queryInsertNodeNgramsNgrams :: PGS.Query
 queryInsertNodeNgramsNgrams = [sql|
     WITH input_rows(nId,ng1,ng2,w) AS (?)
     INSERT INTO nodes_ngrams_ngrams (node_id,ngram1_id,ngram2_id,weight)
@@ -152,7 +152,7 @@ queryInsertNodeNgramsNgrams = [sql|
     ON CONFLICT (node_id,ngram1_id,ngram2_id) DO NOTHING -- unique index created here
            |]
 
-queryDelNodeNgramsNgrams :: DPS.Query
+queryDelNodeNgramsNgrams :: PGS.Query
 queryDelNodeNgramsNgrams = [sql|
     WITH input(nId,ng1,ng2,w) AS (?)
     DELETE FROM nodes_ngrams_ngrams nnn
diff --git a/src/Gargantext/Database/Schema/NodeNode.hs b/src/Gargantext/Database/Schema/NodeNode.hs
index c905c3b341213ad6b85b574ebe32d891c30a641f..72fdc43443d2014bacdfe9892f0824cf24831596 100644
--- a/src/Gargantext/Database/Schema/NodeNode.hs
+++ b/src/Gargantext/Database/Schema/NodeNode.hs
@@ -20,11 +20,12 @@ commentary with @some markup@.
 {-# LANGUAGE MultiParamTypeClasses  #-}
 {-# LANGUAGE NoImplicitPrelude      #-}
 {-# LANGUAGE OverloadedStrings      #-}
+{-# LANGUAGE RankNTypes             #-}
 {-# LANGUAGE TemplateHaskell        #-}
 
 module Gargantext.Database.Schema.NodeNode where
 
-import qualified Database.PostgreSQL.Simple as PGS (Connection, Query, query, Only(..))
+import qualified Database.PostgreSQL.Simple as PGS (Query, Only(..))
 import Database.PostgreSQL.Simple.Types (Values(..), QualifiedIdentifier(..))
 import Database.PostgreSQL.Simple.SqlQQ (sql)
 import Control.Lens.TH (makeLensesWith, abbreviatedFields)
@@ -82,8 +83,8 @@ queryNodeNodeTable = queryTable nodeNodeTable
 
 
 -- | not optimized (get all ngrams without filters)
-nodesNodes :: Cmd [NodeNode]
-nodesNodes = mkCmd $ \c -> runQuery c queryNodeNodeTable
+nodesNodes :: Cmd err [NodeNode]
+nodesNodes = runOpaQuery queryNodeNodeTable
 
 instance QueryRunnerColumnDefault (Nullable PGInt4) Int where
     queryRunnerColumnDefault = fieldQueryRunnerColumn
@@ -97,8 +98,8 @@ instance QueryRunnerColumnDefault PGBool (Maybe Bool) where
 
 ------------------------------------------------------------------------
 -- | Favorite management
-nodeToFavorite :: PGS.Connection -> CorpusId -> DocId -> Bool -> IO [Int]
-nodeToFavorite c cId dId b = map (\(PGS.Only a) -> a) <$> PGS.query c favQuery (b,cId,dId)
+nodeToFavorite :: CorpusId -> DocId -> Bool -> Cmd err [Int]
+nodeToFavorite cId dId b = map (\(PGS.Only a) -> a) <$> runPGSQuery favQuery (b,cId,dId)
   where
     favQuery :: PGS.Query
     favQuery = [sql|UPDATE nodes_nodes SET favorite = ?
@@ -106,9 +107,9 @@ nodeToFavorite c cId dId b = map (\(PGS.Only a) -> a) <$> PGS.query c favQuery (
                RETURNING node2_id;
                |]
 
-nodesToFavorite :: PGS.Connection -> [(CorpusId,DocId,Bool)] -> IO [Int]
-nodesToFavorite c inputData = map (\(PGS.Only a) -> a) 
-                            <$> PGS.query c trashQuery (PGS.Only $ Values fields inputData)
+nodesToFavorite :: [(CorpusId,DocId,Bool)] -> Cmd err [Int]
+nodesToFavorite inputData = map (\(PGS.Only a) -> a)
+                            <$> runPGSQuery trashQuery (PGS.Only $ Values fields inputData)
   where
     fields = map (\t-> QualifiedIdentifier Nothing t) ["int4","int4","bool"]
     trashQuery :: PGS.Query
@@ -123,8 +124,8 @@ nodesToFavorite c inputData = map (\(PGS.Only a) -> a)
 ------------------------------------------------------------------------
 ------------------------------------------------------------------------
 -- | Trash management
-nodeToTrash :: PGS.Connection -> CorpusId -> DocId -> Bool -> IO [PGS.Only Int]
-nodeToTrash c cId dId b = PGS.query c trashQuery (b,cId,dId)
+nodeToTrash :: CorpusId -> DocId -> Bool -> Cmd err [PGS.Only Int]
+nodeToTrash cId dId b = runPGSQuery trashQuery (b,cId,dId)
   where
     trashQuery :: PGS.Query
     trashQuery = [sql|UPDATE nodes_nodes SET delete = ?
@@ -133,9 +134,9 @@ nodeToTrash c cId dId b = PGS.query c trashQuery (b,cId,dId)
                   |]
 
 -- | Trash Massive
-nodesToTrash :: PGS.Connection -> [(CorpusId,DocId,Bool)] -> IO [Int]
-nodesToTrash c input = map (\(PGS.Only a) -> a)
-                        <$> PGS.query c trashQuery (PGS.Only $ Values fields input)
+nodesToTrash :: [(CorpusId,DocId,Bool)] -> Cmd err [Int]
+nodesToTrash input = map (\(PGS.Only a) -> a)
+                        <$> runPGSQuery trashQuery (PGS.Only $ Values fields input)
   where
     fields = map (\t-> QualifiedIdentifier Nothing t) ["int4","int4","bool"]
     trashQuery :: PGS.Query
@@ -148,8 +149,8 @@ nodesToTrash c input = map (\(PGS.Only a) -> a)
                   |]
 
 -- | /!\ Really remove nodes in the Corpus or Annuaire
-emptyTrash :: PGS.Connection -> CorpusId -> IO [PGS.Only Int]
-emptyTrash c cId = PGS.query c delQuery (PGS.Only cId)
+emptyTrash :: CorpusId -> Cmd err [PGS.Only Int]
+emptyTrash cId = runPGSQuery delQuery (PGS.Only cId)
   where
     delQuery :: PGS.Query
     delQuery = [sql|DELETE from nodes_nodes n
diff --git a/src/Gargantext/Database/Schema/NodeNodeNgram.hs b/src/Gargantext/Database/Schema/NodeNodeNgram.hs
index 9f58043a6c4f6423250bb4c4710a2b4571b7ab3d..23ff6af502392bf800d8240441ce8029772365f7 100644
--- a/src/Gargantext/Database/Schema/NodeNodeNgram.hs
+++ b/src/Gargantext/Database/Schema/NodeNodeNgram.hs
@@ -24,7 +24,7 @@ import Prelude
 import Data.Maybe (Maybe)
 import Data.Profunctor.Product.TH (makeAdaptorAndInstance)
 import Control.Lens.TH (makeLensesWith, abbreviatedFields)
-import qualified Database.PostgreSQL.Simple as PGS
+import Gargantext.Database.Utils (Cmd, runOpaQuery)
 
 import Opaleye
 
@@ -76,8 +76,8 @@ queryNodeNodeNgramTable :: Query NodeNodeNgramRead
 queryNodeNodeNgramTable = queryTable nodeNodeNgramTable
 
 -- | not optimized (get all ngrams without filters)
-nodeNodeNgrams :: PGS.Connection -> IO [NodeNodeNgram]
-nodeNodeNgrams conn = runQuery conn queryNodeNodeNgramTable
+nodeNodeNgrams :: Cmd err [NodeNodeNgram]
+nodeNodeNgrams = runOpaQuery queryNodeNodeNgramTable
 
 instance QueryRunnerColumnDefault PGFloat8 (Maybe Double) where
     queryRunnerColumnDefault = fieldQueryRunnerColumn
diff --git a/src/Gargantext/Database/Schema/User.hs b/src/Gargantext/Database/Schema/User.hs
index 0cff318b46ebe8a475c3ab15efa4985572764809..a642ca9008a8eee53bfcd7b917cc76819cab5d06 100644
--- a/src/Gargantext/Database/Schema/User.hs
+++ b/src/Gargantext/Database/Schema/User.hs
@@ -20,6 +20,7 @@ Functions to deal with users, database side.
 {-# LANGUAGE Arrows                      #-}
 {-# LANGUAGE NoImplicitPrelude           #-}
 {-# LANGUAGE OverloadedStrings           #-}
+{-# LANGUAGE RankNTypes                  #-}
 
 module Gargantext.Database.Schema.User where
 
@@ -156,15 +157,14 @@ instance QueryRunnerColumnDefault PGTimestamptz (Maybe UTCTime) where
   queryRunnerColumnDefault = fieldQueryRunnerColumn
 
 
-users :: Cmd [User]
-users = mkCmd $ \conn -> runQuery conn queryUserTable
+users :: Cmd err [User]
+users = runOpaQuery queryUserTable
 
-usersLight :: Cmd [UserLight]
-usersLight = mkCmd $ \conn -> map toUserLight <$> runQuery conn queryUserTable
+usersLight :: Cmd err [UserLight]
+usersLight = map toUserLight <$> users
 
-
-getUser :: Username -> Cmd (Maybe UserLight)
-getUser u = mkCmd $ \c -> userLightWithUsername u <$> runCmd c usersLight
+getUser :: Username -> Cmd err (Maybe UserLight)
+getUser u = userLightWithUsername u <$> usersLight
 
 
 
diff --git a/src/Gargantext/Database/TextSearch.hs b/src/Gargantext/Database/TextSearch.hs
index 7bff15ea758dcefb771e12334fd8738524feec6f..c3b9d06f242d485c10db277d5865e0c40dd987b1 100644
--- a/src/Gargantext/Database/TextSearch.hs
+++ b/src/Gargantext/Database/TextSearch.hs
@@ -11,6 +11,7 @@ Portability : POSIX
 {-# LANGUAGE Arrows            #-}
 {-# LANGUAGE NoImplicitPrelude #-}
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RankNTypes        #-}
 
 module Gargantext.Database.TextSearch where
 
@@ -21,7 +22,7 @@ import Data.List (intersperse, take, drop)
 import Data.String (IsString(..))
 import Data.Text (Text, words, unpack, intercalate)
 import Data.Time (UTCTime)
-import Database.PostgreSQL.Simple -- (Query, Connection)
+import Database.PostgreSQL.Simple (Query)
 import Database.PostgreSQL.Simple.ToField
 import Gargantext.Database.Config (nodeTypeId)
 import Gargantext.Database.Types.Node (NodeType(..))
@@ -33,6 +34,7 @@ import Gargantext.Database.Schema.Ngrams
 import Gargantext.Database.Schema.NodeNode
 import Gargantext.Database.Schema.NodeNgram
 import Gargantext.Database.Queries.Join (leftJoin6)
+import Gargantext.Database.Utils (Cmd, runPGSQuery, runOpaQuery)
 import Gargantext.Text.Terms.Mono.Stem.En (stemIt)
 import Gargantext.Core.Types
 import Control.Arrow (returnA)
@@ -41,8 +43,8 @@ import Opaleye hiding (Query, Order)
 
 
 ------------------------------------------------------------------------
-searchInDatabase :: Connection -> ParentId -> Text -> IO [(NodeId, HyperdataDocument)]
-searchInDatabase c p t = runQuery c (queryInDatabase p t)
+searchInDatabase :: ParentId -> Text -> Cmd err [(NodeId, HyperdataDocument)]
+searchInDatabase p t = runOpaQuery (queryInDatabase p t)
 
 -- | Global search query where ParentId is Master Node Corpus Id 
 queryInDatabase :: ParentId -> Text -> O.Query (Column PGInt4, Column PGJsonb)
@@ -54,8 +56,8 @@ queryInDatabase _ q = proc () -> do
 
 ------------------------------------------------------------------------
 -- | todo add limit and offset and order
-searchInCorpus :: Connection -> CorpusId -> [Text] -> Maybe Offset -> Maybe Limit -> Maybe OrderBy -> IO [FacetDoc]
-searchInCorpus c cId q o l order = runQuery c (filterWith o l order $ queryInCorpus cId q')
+searchInCorpus :: CorpusId -> [Text] -> Maybe Offset -> Maybe Limit -> Maybe OrderBy -> Cmd err [FacetDoc]
+searchInCorpus cId q o l order = runOpaQuery (filterWith o l order $ queryInCorpus cId q')
   where
     q' = intercalate " | " $ map stemIt q
 
@@ -77,20 +79,20 @@ joinInCorpus = leftJoin queryNodeSearchTable queryNodeNodeTable cond
 type AuthorName = Text
 
 -- | TODO Optim: Offset and Limit in the Query
-searchInCorpusWithContacts :: Connection -> CorpusId -> [Text] -> Maybe Offset -> Maybe Limit -> Maybe OrderBy -> IO [FacetPaired Int UTCTime HyperdataDocument Int [Pair Int Text]]
-searchInCorpusWithContacts c cId q o l order = take (maybe 5 identity l) <$> drop (maybe 0 identity o)
+searchInCorpusWithContacts :: CorpusId -> [Text] -> Maybe Offset -> Maybe Limit -> Maybe OrderBy -> Cmd err [FacetPaired Int UTCTime HyperdataDocument Int [Pair Int Text]]
+searchInCorpusWithContacts cId q o l order = take (maybe 5 identity l) <$> drop (maybe 0 identity o)
   <$> map (\((i,u,h,s), ps) -> FacetPaired i u h s (catMaybes ps))
   <$> toList <$> fromListWith (<>)
   <$> map (\(FacetPaired i u h s p) -> ((i,u,h,s), [maybePair p]))
-  <$> searchInCorpusWithContacts' c cId q o l order
+  <$> searchInCorpusWithContacts' cId q o l order
   where
     maybePair (Pair Nothing Nothing) = Nothing
     maybePair (Pair _ Nothing) = Nothing
     maybePair (Pair Nothing _) = Nothing
     maybePair (Pair (Just p_id) (Just p_label)) = Just $ Pair p_id p_label
 
-searchInCorpusWithContacts' :: Connection -> CorpusId -> [Text] -> Maybe Offset -> Maybe Limit -> Maybe OrderBy -> IO [(FacetPaired Int UTCTime HyperdataDocument Int (Pair (Maybe Int) (Maybe Text)))]
-searchInCorpusWithContacts' c cId q o l order = runQuery c $ queryInCorpusWithContacts cId q' o l order
+searchInCorpusWithContacts' :: CorpusId -> [Text] -> Maybe Offset -> Maybe Limit -> Maybe OrderBy -> Cmd err [(FacetPaired Int UTCTime HyperdataDocument Int (Pair (Maybe Int) (Maybe Text)))]
+searchInCorpusWithContacts' cId q o l order = runOpaQuery $ queryInCorpusWithContacts cId q' o l order
   where
     q' = intercalate " | " $ map stemIt q
 
@@ -196,13 +198,12 @@ textSearchQuery = "SELECT n.id, n.hyperdata->'publication_year'     \
 -- | Text Search Function for Master Corpus
 -- TODO : text search for user corpus
 -- Example:
--- textSearchTest :: ParentId -> TSQuery -> Cmd [(Int, Value, Value, Value, Value, Maybe Int)]
--- textSearchTest pId q = mkCmd $ \c -> textSearch c q pId 5 0 Asc
-textSearch :: Connection 
-           -> TSQuery -> ParentId
+-- textSearchTest :: ParentId -> TSQuery -> Cmd err [(Int, Value, Value, Value, Value, Maybe Int)]
+-- textSearchTest pId q = textSearch q pId 5 0 Asc
+textSearch :: TSQuery -> ParentId
            -> Limit -> Offset -> Order
-           -> IO [(Int,Value,Value,Value, Value, Maybe Int)]
-textSearch conn q p l o ord = query conn textSearchQuery (q,p,p,typeId,ord,o,l)
+           -> Cmd err [(Int,Value,Value,Value, Value, Maybe Int)]
+textSearch q p l o ord = runPGSQuery textSearchQuery (q,p,p,typeId,ord,o,l)
   where
     typeId = nodeTypeId NodeDocument
 
diff --git a/src/Gargantext/Database/Tree.hs b/src/Gargantext/Database/Tree.hs
index 0e605043ff8d2bf4c2a740522ee407dae7b6381f..fe97dc8b20d03cd24499b7b3c26597a3fd175fa7 100644
--- a/src/Gargantext/Database/Tree.hs
+++ b/src/Gargantext/Database/Tree.hs
@@ -13,13 +13,13 @@ Let a Root Node, return the Tree of the Node as a directed acyclic graph
 -}
 
 {-# LANGUAGE NoImplicitPrelude #-}
-{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE QuasiQuotes       #-}
+{-# LANGUAGE RankNTypes        #-}
 
 module Gargantext.Database.Tree (treeDB, TreeError(..), HasTreeError(..), dbTree, toNodeTree, DbTreeNode) where
 
 import Control.Lens (Prism', (#), (^..), at, each, _Just, to)
 import Control.Monad.Error.Class (MonadError(throwError))
-import Control.Monad.IO.Class (MonadIO(liftIO))
 import Data.Map (Map, fromListWith, lookup)
 import Data.Text (Text)
 import Database.PostgreSQL.Simple
@@ -28,11 +28,11 @@ import Database.PostgreSQL.Simple.SqlQQ
 import Gargantext.Prelude
 import Gargantext.Core.Types.Main (NodeTree(..), Tree(..))
 import Gargantext.Database.Config (fromNodeTypeId)
+import Gargantext.Database.Utils (Cmd, runPGSQuery)
 ------------------------------------------------------------------------
--- import Gargantext (connectGargandb)
--- import Control.Monad ((>>=))
+-- import Gargantext.Database.Utils (runCmdDev)
 -- treeTest :: IO (Tree NodeTree)
--- treeTest = connectGargandb "gargantext.ini" >>= \c -> treeDB c 347474
+-- treeTest = runCmdDev $ treeDB 347474
 ------------------------------------------------------------------------
 
 data TreeError = NoRoot | EmptyRoot | TooManyRoots
@@ -45,9 +45,8 @@ treeError :: (MonadError e m, HasTreeError e) => TreeError -> m a
 treeError te = throwError $ _TreeError # te
 
 -- | Returns the Tree of Nodes in Database
-treeDB :: (MonadIO m, MonadError e m, HasTreeError e)
-       => Connection -> RootId -> m (Tree NodeTree)
-treeDB c r = toTree =<< (toTreeParent <$> liftIO (dbTree c r))
+treeDB :: HasTreeError err => RootId -> Cmd err (Tree NodeTree)
+treeDB r = toTree =<< (toTreeParent <$> dbTree r)
 
 type RootId = Int
 type ParentId = Int
@@ -83,8 +82,8 @@ data DbTreeNode = DbTreeNode { dt_nodeId :: Int
 
 -- | Main DB Tree function
 -- TODO add typenames as parameters
-dbTree :: Connection -> RootId -> IO [DbTreeNode]
-dbTree conn rootId = map (\(nId, tId, pId, n) -> DbTreeNode nId tId pId n) <$> query conn [sql|
+dbTree :: RootId -> Cmd err [DbTreeNode]
+dbTree rootId = map (\(nId, tId, pId, n) -> DbTreeNode nId tId pId n) <$> runPGSQuery [sql|
   WITH RECURSIVE
       -- starting node(s)
       starting (id, typename, parent_id, name) AS
diff --git a/src/Gargantext/Database/Utils.hs b/src/Gargantext/Database/Utils.hs
index 71b554e5c941d81d0968d341105131a99382f1f0..af2e047f1da51fbf28aadc4fe6b20a98fbe75d89 100644
--- a/src/Gargantext/Database/Utils.hs
+++ b/src/Gargantext/Database/Utils.hs
@@ -11,15 +11,18 @@ Here is a longer description of this module, containing some
 commentary with @some markup@.
 -}
 
+{-# LANGUAGE ConstraintKinds   #-}
 {-# LANGUAGE FlexibleContexts  #-}
 {-# LANGUAGE NoImplicitPrelude #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE RankNTypes                 #-}
 
 module Gargantext.Database.Utils where
 
-import Control.Applicative (Applicative)
+import Control.Lens (Getter, view)
 import Control.Monad.Reader
+import Control.Monad.Except
 import Data.Aeson (Result(Error,Success), fromJSON, FromJSON)
 import Data.Either.Extra (Either(Left, Right))
 import Data.Ini (readIniFile, lookupValue)
@@ -33,36 +36,54 @@ import Database.PostgreSQL.Simple (Connection, connect)
 import Database.PostgreSQL.Simple.FromField ( Conversion, ResultError(ConversionFailed), fromField, returnError)
 import Database.PostgreSQL.Simple.Internal  (Field)
 import Gargantext.Prelude
-import Opaleye (Query, Unpackspec, showSqlForPostgres)
+import Opaleye (Query, Unpackspec, showSqlForPostgres, FromFields, Select, runQuery)
 import System.IO (FilePath)
 import Text.Read (read)
 import qualified Data.ByteString      as DB
 import qualified Database.PostgreSQL.Simple as PGS
 
-------------------------------------------------------------------------
-{- | Reader Monad reinvented here:
+class HasConnection env where
+  connection :: Getter env Connection
 
-newtype Cmd a = Cmd { unCmd :: Connection -> IO a }
+instance HasConnection Connection where
+  connection = identity
 
-instance Monad Cmd where
-  return a = Cmd $ \_ -> return a
+type CmdM env err m =
+  ( MonadReader env m
+  , HasConnection env
+  , MonadError err m
+  , MonadIO m
+  )
 
-  m >>= f = Cmd $ \c -> do
-    a <- unCmd m c
-    unCmd (f a) c
--}
+type Cmd err a = forall m env. CmdM env err m => m a
+
+-- TODO: ideally there should be very few calls to this functions.
+mkCmd :: (Connection -> IO a) -> Cmd err a
+mkCmd k = do
+  conn <- view connection
+  liftIO $ k conn
+
+runCmd :: Connection -> Cmd err a -> IO (Either err a)
+runCmd conn m = runExceptT $ runReaderT m conn
 
---type Cmd' a = forall m. (MonadReader env m, HasConnection env, MonadIO m) => m a
+-- Use only for dev
+runCmdDev :: Show err => Cmd err a -> IO a
+runCmdDev f = do
+  conn <- connectGargandb "gargantext.ini"
+  either (fail . show) pure =<< runCmd conn f
 
-newtype Cmd a = Cmd (ReaderT Connection IO a)
-  deriving (Functor, Applicative, Monad, MonadReader Connection, MonadIO)
+-- Use only for dev
+runCmdDevNoErr :: Cmd () a -> IO a
+runCmdDevNoErr = runCmdDev
 
-runCmd :: Connection -> Cmd a -> IO a
-runCmd c (Cmd f) = runReaderT f c
+runOpaQuery :: Default FromFields fields haskells => Select fields -> Cmd err [haskells]
+runOpaQuery q = mkCmd $ \c -> runQuery c q
 
-mkCmd :: (Connection -> IO a) -> Cmd a
-mkCmd = Cmd . ReaderT
+formatPGSQuery :: PGS.ToRow a => PGS.Query -> a -> Cmd err DB.ByteString
+formatPGSQuery q a = mkCmd $ \conn -> PGS.formatQuery conn q a
 
+runPGSQuery :: (PGS.ToRow a, PGS.FromRow b) => PGS.Query -> a -> Cmd err [b]
+runPGSQuery q a = mkCmd $ \conn -> PGS.query conn q a
 ------------------------------------------------------------------------
 
 databaseParameters :: FilePath -> IO PGS.ConnectInfo
diff --git a/src/Gargantext/Text/Flow.hs b/src/Gargantext/Text/Flow.hs
index 2f23f25ea4e9b3933ebfbcffd8e711f660b0ce23..5f714c39449eaaf86134de6538f38c4db551c75b 100644
--- a/src/Gargantext/Text/Flow.hs
+++ b/src/Gargantext/Text/Flow.hs
@@ -17,6 +17,7 @@ From text to viz, all the flow of texts in Gargantext.
 module Gargantext.Text.Flow
   where
 
+import Control.Monad.Reader
 import GHC.IO (FilePath)
 import qualified Data.Text as T
 import Data.Text.IO (readFile)
@@ -27,7 +28,7 @@ import qualified Data.Set as DS
 import qualified Data.Array.Accelerate as A
 import qualified Data.Map.Strict as M
 ----------------------------------------------
-import Gargantext.Database (Connection)
+import Database.PostgreSQL.Simple (Connection)
 
 import Gargantext.Database.Schema.Node
 import Gargantext.Database.Types.Node
@@ -86,7 +87,7 @@ textFlow termType workType = do
                 FullText path -> splitBy (Sentences 5) <$> readFile path
                 CSV      path -> readCsvOn [csv_title, csv_abstract] path
                 Contexts ctxt -> pure ctxt
-                DBV3 con corpusId -> catMaybes <$> map (\n -> hyperdataDocumentV3_title (_node_hyperdata n)  <> hyperdataDocumentV3_abstract (_node_hyperdata n))<$> getDocumentsV3WithParentId con corpusId
+                DBV3 con corpusId -> catMaybes <$> map (\n -> hyperdataDocumentV3_title (_node_hyperdata n)  <> hyperdataDocumentV3_abstract (_node_hyperdata n))<$> runReaderT (getDocumentsV3WithParentId corpusId) con
                 _             -> undefined -- TODO Query not supported
 
   textFlow' termType contexts
diff --git a/src/Gargantext/Viz/Graph.hs b/src/Gargantext/Viz/Graph.hs
index 40308bb34f778ef62b25ff9b472f9e9873aa39d4..70e6deb08ec5a2ec0b990e74b1c98b0254026548 100644
--- a/src/Gargantext/Viz/Graph.hs
+++ b/src/Gargantext/Viz/Graph.hs
@@ -18,6 +18,7 @@ module Gargantext.Viz.Graph
 
 ------------------------------------------------------------------------
 import Control.Lens (makeLenses)
+import Control.Monad.IO.Class (MonadIO(liftIO))
 import GHC.IO (FilePath)
 import GHC.Generics (Generic)
 import Data.Aeson.TH (deriveJSON)
@@ -207,7 +208,7 @@ graphV3ToGraphWithFiles g1 g2 = do
 
   DBL.writeFile g2 (DA.encode $ graphV3ToGraph newGraph)
 
-readGraphFromJson :: FilePath -> IO (Maybe Graph)
+readGraphFromJson :: MonadIO m => FilePath -> m (Maybe Graph)
 readGraphFromJson fp = do
-  graph <- DBL.readFile fp
+  graph <- liftIO $ DBL.readFile fp
   pure $ DA.decode graph